On Tue, Mar 18, 2025, at 9:56 PM, Dean wrote: > Unfortunately, neither column lists nor row filters can provide the level of > control I'm proposing. These revised examples might help illustrate the use > case for DRF:
I'm afraid I didn't understand your proposal. Are you trying to use logical replication with RLS enabled on subscriber? > Alice, Bob, and Eve subscribe to changes on a `friend_requests` table. > Row-level security ensures CRUD access based on user IDs. > 1. Per-subscriber column control: Bob makes a change to the table. Alice > should receive the entire record, while Eve should only receive the timestamp > - no other columns. Why DRF is needed: Column lists are static and apply > equally to all subscribers, meaning we can't distinguish Alice's subscription > from Eve's. > 2. Bob DELETEs a row from the table. Alice should see the DELETE event, while > Eve should not even be aware of an event. Why DRF is needed: The > deterministic nature of row filters makes them unsuitable for per-subscriber > filtering based on session data. > > The goal of DRF is to allow per-subscriber variations in change broadcasts, > enabling granular control over what data is sent to each subscriber based on > their session context. You misunderstood the logical replication architecture. The filtering is applied *after* the WAL is decoded. See change_cb -- pgoutput_change(). You mentioned RLS but AFAICS it cannot replicate or do an initial synchronization to a table if RLS is enabled. See TargetPrivilegesCheck() -- worker.c. /* * We lack the infrastructure to honor RLS policies. It might be possible * to add such infrastructure here, but tablesync workers lack it, too, so * we don't bother. RLS does not ordinarily apply to TRUNCATE commands, * but it seems dangerous to replicate a TRUNCATE and then refuse to * replicate subsequent INSERTs, so we forbid all commands the same. */ if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user \"%s\" cannot replicate into relation with row-level security enabled: \"%s\"", GetUserNameFromId(GetUserId(), true), RelationGetRelationName(rel)))); See LogicalRepSyncTableStart() -- tablesync.c. /* * COPY FROM does not honor RLS policies. That is not a problem for * subscriptions owned by roles with BYPASSRLS privilege (or superuser, * who has it implicitly), but other roles should not be able to * circumvent RLS. Disallow logical replication into RLS enabled * relations for such roles. */ if (check_enable_rls(RelationGetRelid(rel), InvalidOid, false) == RLS_ENABLED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user \"%s\" cannot replicate into relation with row-level security enabled: \"%s\"", GetUserNameFromId(GetUserId(), true), RelationGetRelationName(rel)))); The comments already point out directions. Feel free to write a proposal for it. -- Euler Taveira EDB https://www.enterprisedb.com/