You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Afterwards the schema is still fully populated — in our case 19 tables and 63 functions survived a "successful" reset.
There is no warning and no error at the point the schema is skipped.
Expected behavior
db reset --linked should drop every user-defined schema. A schema that no extension created should be dropped regardless of its OID.
The cause
apps/cli/src/legacy/commands/db/shared/legacy-drop-schemas.ts (and identically apps/cli-go/pkg/migration/queries/drop.sql) lists user schemas with:
select pn.*from pg_namespace pn
left join pg_depend pd onpd.objid=pn.oidwherepd.deptype is nulland not pn.nspnamelike any(array[...])
andpn.nspowner::regrole::text!='supabase_admin'
pg_depend.objid is only meaningful together with pg_depend.classid, which says which system catalog the OID belongs to. OIDs are unique only within a catalog, so without the classid constraint a dependency row belonging to a pg_class / pg_proc / pg_type entry satisfies the join for an unrelated schema that happens to share the number. pd.deptype is null is then false and the schema is silently dropped from the result set — and therefore never dropped from the database.
Steps to reproduce
The trigger is an OID collision, so it cannot be forced on demand. But the defect is visible in the query itself, and the resulting state is directly observable. To check whether a given project is affected:
-- Any row returned is a schema `db reset` will silently skip.selectpn.nspname,
pn.oid,
(selectcount(*) from pg_depend pd
wherepd.objid=pn.oid) as rows_the_cli_counts,
(selectcount(*) from pg_depend pd
wherepd.objid=pn.oidandpd.classid='pg_namespace'::regclass) as real_dependency_rows
from pg_namespace pn
wherepn.nspname not like any(array['information\_schema','pg\_%','\_analytics',
'\_realtime','\_supavisor','pgbouncer','pgmq','pgsodium','pgtle',
'supabase\_migrations','vault','extensions','public'])
andpn.nspowner::regrole::text!='supabase_admin'and exists (select1from pg_depend pd wherepd.objid=pn.oid);
real_dependency_rows = 0 is the point: with classid constrained, the schema has no dependency rows at all. It is unambiguously user-defined. The two rows the CLI counts describe a different catalog object that happens to share the number 16424 — in our case the extensions.pg_stat_statements view.
Suggested fix
Constrain the join to the namespace catalog:
left join pg_depend pd
onpd.objid=pn.oidandpd.classid='pg_namespace'::regclass
Optionally also and pd.deptype = 'e', since the intent stated in the query's own comment is specifically to exclude extension-created schemas.
Additional context
Introduced in b5e0e17 (fix: simplify query for user defined schemas #2206, "fix: simplify query for user defined schemas", 2024-04-25), which replaced a name-based information_schema.schemata lookup with this join. The previous implementation could not exhibit the problem.
The failure state is self-sustaining, which is why it looks rare and then becomes permanent: a skipped schema is never dropped, so it keeps its OID, so it collides again on every subsequent reset. In our project the four sibling schemas carried freshly assigned OIDs in the 375100-375103 range because they are dropped and recreated each reset, while the affected schema still sat at 16424 from the original install. Recovery required dropping it manually once; after that it was reassigned a fresh OID and stopped colliding.
Because the skip is silent, the visible symptom is a migration failure that looks like a fault in the user's own migrations.
Environment
ubuntu-latest, GitHub Actions). Not platform specific — the defect is a SQL predicate executed against the remote database.supabase/setup-cli@v1supabase db reset --linked --yesActual output
The reset reports success, but one user schema is never dropped. The next migration that creates an object in that schema then fails:
Afterwards the schema is still fully populated — in our case 19 tables and 63 functions survived a "successful" reset.
There is no warning and no error at the point the schema is skipped.
Expected behavior
db reset --linkedshould drop every user-defined schema. A schema that no extension created should be dropped regardless of its OID.The cause
apps/cli/src/legacy/commands/db/shared/legacy-drop-schemas.ts(and identicallyapps/cli-go/pkg/migration/queries/drop.sql) lists user schemas with:pg_depend.objidis only meaningful together withpg_depend.classid, which says which system catalog the OID belongs to. OIDs are unique only within a catalog, so without theclassidconstraint a dependency row belonging to apg_class/pg_proc/pg_typeentry satisfies the join for an unrelated schema that happens to share the number.pd.deptype is nullis then false and the schema is silently dropped from the result set — and therefore never dropped from the database.Steps to reproduce
The trigger is an OID collision, so it cannot be forced on demand. But the defect is visible in the query itself, and the resulting state is directly observable. To check whether a given project is affected:
On our project this returned exactly one row:
real_dependency_rows = 0is the point: withclassidconstrained, the schema has no dependency rows at all. It is unambiguously user-defined. The two rows the CLI counts describe a different catalog object that happens to share the number 16424 — in our case theextensions.pg_stat_statementsview.Suggested fix
Constrain the join to the namespace catalog:
Optionally also
and pd.deptype = 'e', since the intent stated in the query's own comment is specifically to exclude extension-created schemas.Additional context
information_schema.schematalookup with this join. The previous implementation could not exhibit the problem.