What happens when row-level security is disabled on a table?
Row-level security disabled on an existing table
Critical: this fails outright or takes production down.
What happens
DISABLE ROW LEVEL SECURITY switches off every policy on the table at once: from the moment it commits, queries see all rows, not the policy-filtered subset. If RLS was carrying tenant isolation or access control, this single line is a data exposure, and it looks like routine DDL.
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
ALTER TABLE orders DISABLE ROW LEVEL SECURITY;The safe pattern
Change the policies, not the switch: adjust or replace the specific policy that is in the way, keeping enforcement on throughout. If RLS truly must go, that is a reviewed security decision with its own sign-off, not a line inside a schema migration.
-- Adjust the policy that's in the way; keep enforcement on:
SET lock_timeout = '5s';
DROP POLICY orders_tenant_read ON orders;
CREATE POLICY orders_tenant_read ON orders
USING (tenant_id = current_setting('app.tenant_id')::bigint);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)
SET lock_timeout = '5s';
ALTER TABLE orders DISABLE ROW LEVEL SECURITY;Stays silent (2)
-- A table created in this migration has no policies to lose.
CREATE TABLE audit_scratch (id bigint PRIMARY KEY);
ALTER TABLE audit_scratch DISABLE ROW LEVEL SECURITY;SET lock_timeout = '5s';
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;How to check locally
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