All rules Rule BV028 · DS
PostgreSQL
critical
DS · Destructive changes
hosted, paid, --remote

What does DROP OWNED BY delete?

DROP OWNED BY erasing everything a role owns

Critical: this fails outright or takes production down.

What happens

DROP OWNED BY removes every object the role owns in the current database and revokes its grants, the blast radius is whatever that role ever created, which nobody can enumerate from the migration text.

Why it is dangerous on a populated table

On an empty table this finishes before anyone notices. On a populated one the lock is held for the whole operation, every query on the table queues behind it, and the connection pool fills from the back.

Fires on

DROP OWNED BY deploy_user;

The safe pattern

REASSIGN OWNED BY transfers every object to a surviving role first; after that, DROP OWNED BY only revokes grants and the role can be dropped safely. Never run DROP OWNED while the role still owns objects you have not enumerated.

-- Transfer ownership instead of dropping the objects:
REASSIGN OWNED BY deploy_user TO app_owner;
-- now only grants remain attached to the role:
--    DROP OWNED BY deploy_user;   -- revokes grants only
--    DROP ROLE deploy_user;

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 (1)

drop owned
DROP OWNED BY old_deploy_user;

Stays silent (1)

revoke
REVOKE ALL ON orders FROM old_deploy_user;

How to check locally

Catch this before it ships

This rule runs in the hosted service on Startup and above: add --remote with a team token, or use the GitHub Action. No install, nothing leaves your machine:

npx bolvrk check migration.sql --remote