cockroachdb / cockroachdb/cockroach
sql: DDL in a routine can drop a table the calling statement is using
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 32.5k
- Forks
- 4.1k
- PR merge metrics
- PR metrics pending
Description
Describe the problem
Procedure bodies may contain CREATE TABLE / DROP TABLE (with
sql.procedures.plpgsql.late_binding.enabled). A UDF can CALL such a
procedure, and the UDF can be invoked from inside another statement. Nothing
stops the routine from dropping the very table the outer statement is reading
or writing. The outer statement then completes as if nothing happened.
PostgreSQL rejects this at the DDL with 55006:
ERROR: cannot DROP TABLE "pc" because it is being used by active queries in this session
The same guard (CheckTableNotInUse) also covers ALTER TABLE, open cursors,
and suspended portals. CockroachDB has a blanket guard for cursors (#74608) and
none for portals (#99085). This issue is the third gap: the active statement
itself.
To Reproduce
SET CLUSTER SETTING sql.procedures.plpgsql.late_binding.enabled = true;
CREATE TABLE parent (p INT PRIMARY KEY);
CREATE TABLE child (c INT PRIMARY KEY, p INT REFERENCES parent(p));
INSERT INTO parent VALUES (1);
CREATE PROCEDURE drop_child() LANGUAGE PLpgSQL AS $$
BEGIN
DROP TABLE child;
END $$;
CREATE FUNCTION f() RETURNS INT LANGUAGE PLpgSQL AS $$
BEGIN
CALL drop_child();
RETURN 1;
END $$;
INSERT INTO child VALUES (1, f());
The INSERT succeeds. child is gone afterwards. Same result with the legacy
and declarative schema changers.
With defer_foreign_key_checks = true the deferred FK check for the INSERT
is planned at commit against a dropped table and fails with an internal error
instead.
Expected behavior
The DROP TABLE inside the routine fails with 55006, as in PostgreSQL, and
the outer statement fails with it.
Additional context
ALTER TABLE is not reachable this way today because procedure bodies and
EXECUTE reject it. DROP TABLE of an unrelated table, or CREATE TABLE,
from inside a statement's input is fine in PostgreSQL and should stay allowed.
A precise fix needs the routine DDL path to know which relations the active
statement depends on. A conservative one rejects any DROP TABLE (and future
ALTER TABLE) from a routine that is not the top-level CALL.
Related: #174683 (DDL in UDFs) lists mid-query safety as an open requirement.
Jira issue: CRDB-68542
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the routine DDL path described in the issue and reproduce the SQL example with late binding enabled. Trace how the active statement's relation dependencies are exposed to routine execution, including the legacy and declarative schema changers. Done means dropping a relation used by the outer statement fails with 55006, while unrelated DROP TABLE and CREATE TABLE remain allowed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100