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/

Reply via email to