When using pgschema 1.9.0 with a Supabase/PostgreSQL database, schema-qualified functions inside RLS policy expressions are dumped/planned as unqualified function calls.
For example, Supabase RLS policies commonly use:
But pgschema outputs them as:
This causes plan or apply to fail because uid() / role() are not resolvable under the target schema search path.
Version
pgschema 1.9.0@e5673daf linux/amd64
Docker image: pgplex/pgschema:1.9.0
PostgreSQL: 17.6
Environment: Supabase postgres image
Minimal Reproduction
Given a Supabase-style database where auth.uid() exists and role authenticated exists:
CREATE SCHEMA IF NOT EXISTS pgschema_poc;
CREATE TABLE IF NOT EXISTS pgschema_poc.pgschema_poc_items (
id BIGSERIAL PRIMARY KEY,
owner_id uuid NOT NULL,
name text NOT NULL
);
ALTER TABLE pgschema_poc.pgschema_poc_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY pgschema_poc_select_own
ON pgschema_poc.pgschema_poc_items
FOR SELECT
TO authenticated
USING (owner_id = (SELECT auth.uid()));
Then run pgschema against the pgschema_poc schema:
pgschema plan \
--host postgres \
--db postgres \
--user supabase_admin \
--password '<password>' \
--schema pgschema_poc \
--file poc.sql \
--plan-host postgres \
--plan-db postgres \
--plan-user supabase_admin \
--plan-password '<password>' \
--sslmode disable \
--plan-sslmode disable \
--no-color
Actual Behavior
The generated plan contains:
CREATE POLICY pgschema_poc_select_own
ON pgschema_poc_items
FOR SELECT TO authenticated
USING (owner_id = ( SELECT uid() AS uid));
The original schema-qualified function call:
is emitted as:
Then apply fails with:
ERROR: function uid() does not exist (SQLSTATE 42883)
The same issue occurs with auth.role(), which is emitted as unqualified role().
Expected Behavior
pgschema should preserve schema-qualified function references in RLS policy expressions when the referenced function is outside the managed target schema.
Expected output:
CREATE POLICY pgschema_poc_select_own
ON pgschema_poc_items
FOR SELECT TO authenticated
USING (owner_id = ( SELECT auth.uid() AS uid));
or semantically equivalent SQL that still resolves to auth.uid().
Why This Matters
Supabase commonly uses auth.uid() and auth.role() in RLS policies. This issue prevents pgschema from safely managing Supabase public schemas with RLS enabled.
In our test database:
task_config no-op plan succeeded.
project_access_cache no-op plan succeeded.
public no-op plan failed because policies using auth.uid() / auth.role() were emitted as uid() / role().
Additional Observations
This appears to happen specifically in RLS policy expressions.
In the same dump, schema-qualified function calls inside PL/pgSQL function bodies were preserved correctly, for example:
v_user_id := (SELECT auth.uid());
SELECT auth.uid() AS uid;
FROM project_access_cache.get_user_accessible_projects(auth.uid()) gap;
Other schema-qualified references were also preserved in function bodies and triggers, for example:
extensions.similarity(...)
task_config.notify_task_event()
project_access_cache.sync_user_root_nodes_on_member_change()
So the issue seems specific to policy expression deparsing/planning, not all schema-qualified references.
Possible Workaround
Adding auth to search_path might work around this specific case, but it is not a safe general fix. The original policy explicitly references auth.uid(), and pgschema should preserve that namespace to avoid changing semantics or resolving to a different function in another schema.
Another workaround is wrapping auth.uid() in a function inside the managed schema, but that requires changing many existing Supabase RLS policies and should not be necessary.
When using pgschema
1.9.0with a Supabase/PostgreSQL database, schema-qualified functions inside RLS policy expressions are dumped/planned as unqualified function calls.For example, Supabase RLS policies commonly use:
But pgschema outputs them as:
This causes
planorapplyto fail becauseuid()/role()are not resolvable under the target schema search path.Version
Minimal Reproduction
Given a Supabase-style database where
auth.uid()exists and roleauthenticatedexists:Then run pgschema against the
pgschema_pocschema:Actual Behavior
The generated plan contains:
The original schema-qualified function call:
is emitted as:
Then
applyfails with:The same issue occurs with
auth.role(), which is emitted as unqualifiedrole().Expected Behavior
pgschema should preserve schema-qualified function references in RLS policy expressions when the referenced function is outside the managed target schema.
Expected output:
or semantically equivalent SQL that still resolves to
auth.uid().Why This Matters
Supabase commonly uses
auth.uid()andauth.role()in RLS policies. This issue prevents pgschema from safely managing Supabasepublicschemas with RLS enabled.In our test database:
task_configno-op plan succeeded.project_access_cacheno-op plan succeeded.publicno-op plan failed because policies usingauth.uid()/auth.role()were emitted asuid()/role().Additional Observations
This appears to happen specifically in RLS policy expressions.
In the same dump, schema-qualified function calls inside PL/pgSQL function bodies were preserved correctly, for example:
Other schema-qualified references were also preserved in function bodies and triggers, for example:
So the issue seems specific to policy expression deparsing/planning, not all schema-qualified references.
Possible Workaround
Adding
authtosearch_pathmight work around this specific case, but it is not a safe general fix. The original policy explicitly referencesauth.uid(), and pgschema should preserve that namespace to avoid changing semantics or resolving to a different function in another schema.Another workaround is wrapping
auth.uid()in a function inside the managed schema, but that requires changing many existing Supabase RLS policies and should not be necessary.