What does DROP ... CASCADE actually remove?
DROP with CASCADE hitting unexpected dependents
Warning: this works, but blocks traffic or rewrites data at scale.
What happens
CASCADE resolves the dependency graph at execution time and drops everything in it, without listing what it took. What it removes in production can differ from dev, a view or foreign key added since, gone silently.
Why it is dangerous on a populated table
It completes, but the time it holds its lock or rewrites rows scales with the table, and on a busy one that is long enough for traffic to queue.
Fires on
DROP TABLE legacy_events CASCADE;The safe pattern
Run the DROP without CASCADE first: it fails and lists every dependent object. Then drop each dependent explicitly in the migration, so the blast radius is written down and reviewed instead of resolved silently at execution time.
-- DROP ... RESTRICT (the default) fails and names every dependent;
-- drop them explicitly so the migration says what it removes:
SET lock_timeout = '5s';
DROP VIEW legacy_events_summary;
-- then, once nothing references it (see BV005/BV030):
-- DROP TABLE legacy_events;Fixtures
The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.
Fires (2)
ALTER TABLE users DROP COLUMN profile_id CASCADE;DROP TABLE legacy_events CASCADE;Stays silent (1)
DROP TABLE legacy_events;How to check locally
This rule runs locally in the free CLI, or with the full corpus through the hosted service. No install, nothing leaves your machine:
npx bolvrk check migration.sql