Build1 distinct publisher3 min readPublished
ENABLE ROW LEVEL SECURITY leaves the table owner exempt, so an app account that owns its tables still reads every tenant. One developer built the same table five ways and found only one of the five sound.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The direction of the failure is what makes a one-tenant fixture useless as evidence. Row-level security fails by returning rows the caller should not see, never by returning zero, so a fixture that holds nothing to refuse cannot tell a working boundary from an absent one [8]. The scoped assertion most people write, serving role returns only its own rows [12], comes back green in that fixture whether the policy applies, sits inert because the app account owns the table, or was never written at all [7].
Read the grid by columns. One of the five builds is sound, so four are broken [1]. Equality, the double run that Mads Hansen proposed in reply to the author's earlier piece [13], goes red in exactly one of those four [2], because two runs that both return everything are still equal [14]. Equality has one thing going for it as an assertion: it is green on almost everything. Scoped catches a different single case, the two-tenant table with no policy at all [3]. Disjointness catches three, and equality plus disjointness is the pair that covers all four broken builds [4].
The author's first published assertion was the wrong shape, and he says so. It was assertNotSame on the owner run, asserting the leak, which goes red the day somebody applies the correct fix [5]. Equality survives both worlds because after FORCE the owner sees exactly what the serving role sees [1]. That is the measurement: owner=[1,2] under ENABLE only [3], owner=[1] after FORCE, same fixture, same query [4].
The part worth stealing is how the fixture check itself is written. Proving that a second tenant still exists in the table wants a connection that can see everything, and under FORCE the author reports no such connection is left; borrowing a superuser to look moves the check outside the boundary you are trying to prove [10]. Setting the tenant context to the second tenant and asserting that its set is non-empty and does not intersect the first stays inside it, on the same role under the same policy [11].
For the grid to transfer, the role your application connects with has to own its tables, which is what happens when that same role runs your migrations [2]. A serving role that does not own the table is not covered by the owner exemption in the first place [1], and on that deployment ENABLE alone is doing real work. The other two conditions are yours to check: whether your isolation test is the scoped one, and whether any fixture it runs against contains a second tenant. Marco's line in the thread is the one the author says he would keep, that the fixture is part of the security claim, and if removing the violating state does not make the test fail then the test was never proving the invariant [9].
Ranked by verification strength, evidence, and original report placement.
ENABLE ROW LEVEL SECURITY leaves the table owner exempt from the policy; FORCE ROW LEVEL SECURITY does not, and after FORCE the owner sees exactly what the serving role sees.
The author wrote ten days earlier that the Postgres role which runs your migrations bypasses every row-level security policy you wrote.
Measured on PostgreSQL 18.3 with ENABLE only, same fixture and query: tenant 1 returned serving=[1], owner=[1,2]; tenant 2 returned serving=[2], owner=[1,2]. Both marked divergent, a leak.
After FORCE ROW LEVEL SECURITY, on the same fixture and query: tenant 1 returned serving=[1], owner=[1]; tenant 2 returned serving=[2], owner=[2]. Both identical.
The author's first instinct was to assert that the owner run leaks, using assertNotSame; he says that assertion is wrong and goes red on the day someone applies the correct fix.
The invariant the author says survives both worlds is that both runs return the same set; written as equality it stays green when the schema is fixed and red when someone drops FORCE in a migration.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 31, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Postgres row-level security does nothing for the role your Symfony app connects with1 distinct publisher
build
A signed-out caller collapses a correct-looking RLS policy into using (true)1 distinct publisher
build
The agent asks, the gateway decides: why read-only is not a security boundary1 distinct publisher
build
Your "Index Only Scan" Did 2,847 Heap Fetches: Covering Indexes Are a Vacuum Problem1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Printed ids, one hand
Two different things hold this up. The ENABLE/FORCE distinction is documented Postgres behaviour a sceptical reader can check in a psql session in minutes. The five-way grid is not documented anywhere — it exists because this author ran it on a named version and printed the visible ids rather than a pass count, which makes it reproducible and still leaves it entirely unconfirmed by anyone else.
Nobody's deployment but his own
Nothing in this reporting tells us who is doing it. The only run in existence is the author's own fixture on 18.3, and measuring how Postgres behaves says nothing about how many teams have FORCE in their migrations or a second tenant in their seed data. No repositories, no CI configs, no incident where this bit someone in production.
The headline is the measurement
The boldest sentence — with one tenant in the fixture, your isolation suite passes against a database with no row-level security — is exactly what the published grid shows, and the piece arrives as a retraction of advice the same author gave ten days earlier. If anything he undersells it: he calls his own assertNotSame instinct wrong before anyone else can, and the claim most deserving of scrutiny, that after FORCE nobody is above the policy, is the one he demonstrates in both directions.
Reputation, not revenue
There is no product in this story and no vendor behind it. What exists is a dev.to author extending his own series and crediting by name the two commenters — Mads Hansen, then Marco — whose replies dismantled first his framing and then his fix. Audience-building is a real motive for a follow-up post; publicly reversing your last recommendation is a poor way to pursue it.
Cheap to falsify
Confidence here comes from how little it would take to prove wrong: three assertions, one helper function, a named Postgres version, outputs printed as ids. What holds it back is scope — one person measured once, and the industry-wide claim about single-tenant fixtures is inferred from five tables built to make the point rather than from any look at real test suites.