Database / Supabase Intermediate to Advanced Interview Questions
How does wrapping auth.uid() in a subquery improve Row Level Security performance?
Calling auth.uid() directly inside a policy's USING clause looks harmless, but Postgres treats it as a volatile function call by default, meaning the planner can't assume it returns the same value for every row and may end up re-evaluating it once per row scanned rather than once per query.
Wrapping it as (select auth.uid()) instead gives the planner a subquery it can evaluate once, cache the result as a constant, and then filter every row against that single cached value — the same technique used to make any expensive, row-invariant expression cheaper inside a large scan.
-- Slower: potentially re-evaluated per row create policy "owner_select" on documents for select using (auth.uid() = user_id); -- Faster: evaluated once, then reused as a constant create policy "owner_select" on documents for select using ((select auth.uid()) = user_id);
On a small table the difference is negligible, but on a table with hundreds of thousands or millions of rows subject to a policy, this rewrite can turn a noticeably slow filtered query into one that performs close to an equivalent query with no RLS at all. It's one of the first things worth checking when a table's read performance mysteriously regresses after enabling RLS.
More Related questions...