cockroachdb / cockroachdb/cockroach
sql: DROP ... CASCADE requires DROP privilege on dependent objects, diverging from PostgreSQL
- Dominant language
- Go
- Stars
- 32.5k
- Forks
- 4.1k
- PR merge metrics
- PR metrics pending
Description
**Describe the problem**
CockroachDB's `DROP ... CASCADE` requires the executing user to hold `DROP`
privilege on every dependent object that will be cascaded into, not just the
named target object. PostgreSQL only requires ownership of the named object;
it drops dependents without an additional per-dependent authorization check.
This is a semantic incompatibility with PostgreSQL. It is documented on the
[`DROP TABLE` required-privileges page](https://docs.cockroachlabs.com/docs/v26.3/drop-table#required-privileges),
but it is **not** listed on the
[PostgreSQL Compatibility page](https://docs.cockroachlabs.com/docs/stable/postgresql-compatibility),
so users migrating from PostgreSQL have no single place that flags the
difference.
**To Reproduce**
```sql
CREATE TABLE t (a INT);
CREATE VIEW v AS SELECT a FROM t;
CREATE USER alice;
GRANT USAGE ON SCHEMA public TO alice;
ALTER TABLE t OWNER TO alice;
SET ROLE alice;
DROP TABLE t CASCADE;
```
**CockroachDB** — `alice` owns `t` but not the dependent view `v`, and the drop is rejected:
```
demo@127.0.0.1:26257/demoapp/defaultdb> CREATE TABLE t (a INT);
-> CREATE VIEW v AS SELECT a FROM t;
-> CREATE USER alice;
-> GRANT USAGE ON SCHEMA public TO alice;
-> ALTER TABLE t OWNER TO alice;
-> SET ROLE alice;
-> DROP TABLE t CASCADE;
CREATE TABLE
CREATE VIEW
CREATE ROLE
GRANT
ALTER TABLE
SET
ERROR: user alice does not have DROP privilege on relation v
SQLSTATE: 42501
```
**PostgreSQL** (18.4) — the same sequence succeeds, cascading into `v` without a privilege check:
```
psql (18.4 (Homebrew))
scratch=# CREATE TABLE t (a INT);
CREATE VIEW v AS SELECT a FROM t;
CREATE USER alice;
GRANT USAGE ON SCHEMA public TO alice;
ALTER TABLE t OWNER TO alice;
SET ROLE alice;
DROP TABLE t CASCADE;
CREATE TABLE
CREATE VIEW
CREATE ROLE
GRANT
ALTER TABLE
SET
NOTICE: drop cascades to view v
DROP TABLE
```
**`GRANT ALL` / explicit `DROP` grant instead of ownership**
CRDB's check is satisfied by *either* ownership of the dependent *or* an
explicit privilege grant, because `CheckPrivilege(dependent, DROP)` resolves
via `HasPrivilege`, and an object's owner implicitly holds all privileges
(including `DROP`). So granting `alice` the `DROP` privilege on the dependent
view makes the same statement succeed:
```sql
-- ... same setup, but instead of transferring ownership of v:
GRANT ALL ON t TO alice; -- ALL includes DROP (TablePrivileges)
GRANT ALL ON v TO alice; -- this is what unblocks the cascade in CRDB
SET ROLE alice;
DROP TABLE t CASCADE; -- succeeds
```
`ALL` is the relevant grant because `DROP` is a member of `TablePrivileges`;
`GRANT DROP ON v TO alice` alone would suffice for the cascade check. The
original repro fails only because `alice` has neither ownership nor a `DROP`
grant on `v`.
This exposes the underlying design question: CRDB treats **ownership of the
dependent** and an **explicit `DROP` grant on the dependent** as
interchangeable for cascade purposes, and requires one of them. PostgreSQL
requires *neither* on dependents — only ownership of the named target. So
beyond "does CRDB check dependents at all," there is a second question worth
settling: should ownership and the `DROP` privilege be treated the same for
cascade authorization? If the dependent check is kept, it is currently an
owner-OR-DROP check; if it is removed to match PG, the distinction becomes
moot. Either way the intended model should be stated explicitly rather than
left as an artifact of `CheckPrivilege`'s owner-or-privilege semantics.
**Expected behavior**
`DROP TABLE t CASCADE` should succeed when the user is authorized to drop the
named target `t`, cascading into dependent objects without requiring `DROP`
privilege on each dependent — matching PostgreSQL.
**Where the divergence was introduced**
The dependent-object privilege check appears to have been added incidentally
rather than as a deliberate departure from PostgreSQL:
- Dependency tracking for `RESTRICT`/`CASCADE` was first added in
cockroachdb/cockroach#9724 (Sep 2016). At that point `canRemoveDependentView`
only verified that `CASCADE` was specified — it did **not** check privileges
on the dependent view.
- The `DROP` privilege check on dependent objects was added shortly after in
cockroachdb/cockroach#9988 (Oct 2016), which refactored the helper into
`canRemoveDependentViewGeneric` and inserted
`checkPrivilege(viewDesc, privilege.DROP)`. Neither the commit message nor
the PR review discussion mentions PostgreSQL, PG compatibility, or ownership
semantics — the review focused on rename restrictions, error messages, and
test coverage. There is no record of an intentional decision to diverge from
PostgreSQL.
This check later propagated to the declarative schema changer's
`dropCascadeDescriptor` helper.
**Documentation gap**
The behavior is documented under
[`DROP TABLE` → Required privileges](https://docs.cockroachlabs.com/docs/v26.3/drop-table#required-privileges)
but is missing from
[PostgreSQL Compatibility](https://docs.cockroachlabs.com/docs/stable/postgresql-compatibility).
If the divergence is retained, it should be listed on the compatibility page.
Jira issue: CRDB-67920
Epic CRDB-65516
Contributor guide
Research direction
Reproduce the SQL sequence in the issue, then inspect canRemoveDependentViewGeneric and the declarative schema changer's dropCascadeDescriptor helper. Compare their dependent-object privilege checks with the stated PostgreSQL behavior and existing tests. Done means the intended cascade authorization model is implemented and, if the divergence remains, documented on the PostgreSQL Compatibility page.
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
- 45/100