Re: Proposal: Conflict log history table for Logical Replication

From: Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>
To: Peter Smith <smithpb2250(at)gmail(dot)com>
Cc: vignesh C <vignesh21(at)gmail(dot)com>, Dilip Kumar <dilipbalaut(at)gmail(dot)com>, Nisha Moond <nisha(dot)moond412(at)gmail(dot)com>, shveta malik <shveta(dot)malik(at)gmail(dot)com>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>, Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, shveta malik <shvetamalik(at)gmail(dot)com>
Subject: Re: Proposal: Conflict log history table for Logical Replication
Date: 2026-05-14 10:48:33
Message-ID: CAA4eK1Ki5mBgAubBkUPcBjN=O1jeT3AUh7vLQBm8w=gQiHO5Jw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Wed, May 13, 2026 at 11:43 AM Peter Smith <smithpb2250(at)gmail(dot)com> wrote:
>
> Some review comments for v33-0001.
>
> ======
> src/backend/catalog/aclchk.c
>
> pg_class_aclmask_ext:
>
> 1.
> if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE |
> ACL_USAGE)) &&
> - IsSystemClass(table_oid, classForm) &&
> - classForm->relkind != RELKIND_VIEW &&
> + IsConflictClass(classForm) &&
> !superuser_arg(roleid))
> - mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
> + mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_USAGE);
> + else if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE |
> ACL_TRUNCATE | ACL_USAGE)) &&
> + IsSystemClass(table_oid, classForm) &&
> + classForm->relkind != RELKIND_VIEW &&
> + !superuser_arg(roleid))
> + mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
>
> The new patched code seems a bit repetitive.
>
> How about refactoring like below and putting the comments where they belong.
>
> if (!superuser_arg(roleid))
> {
> if (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE))
> {
> if (IsSystemClass(table_oid, classForm) &&
> classForm->relkind != RELKIND_VIEW)
> {
> /*
> * Deny anyone permission to update a system catalog unless
> * pg_authid.rolsuper is set.
> *
> * As of 7.4 we have some updatable system views; those shouldn't be
> * protected in this way. Assume the view rules can take care of
> * themselves. ACL_USAGE is if we ever have system sequences.
> */
> mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE |
> ACL_USAGE);
> }
> else if (IsConflictClass(classForm))
> {
> /*
> * For conflict log tables, we allow non-superusers to perform DELETE
> * and TRUNCATE for maintenance, while still restricting INSERT,
> * UPDATE, and USAGE.
> */
> mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_USAGE);
> }
> }
> }
> else
> {
> /* Superusers bypass all permission-checking. */
>
> ReleaseSysCache(tuple);
> return mask;
> }
>

It is okay to reduce duplicity here but the check for IsConflictClass
should be first because IsSystemClass also contains the similar check
though for a different reason.

>
> 8.
> Despite some of these just being static, I am beginning to think that
> the "conflict" specific CLT code might be more appropriate to be put
> in conflict.c, along with the CLT schema etc.
>
> e.g. functions like:
> - create_conflict_log_table_tupdesc
> - create_conflict_log_table
> - GetLogDestination
>

+1.

>
> ======
> src/backend/replication/logical/conflict.c
>
> 13.
> +const char *const ConflictLogDestNames[] = {
> + [CONFLICT_LOG_DEST_LOG] = "log",
> + [CONFLICT_LOG_DEST_TABLE] = "table",
> + [CONFLICT_LOG_DEST_ALL] = "all"
> +};
> +
> +const ConflictLogColumnDef v[] = {
> + { .attname = "relid", .atttypid = OIDOID },
> + { .attname = "schemaname", .atttypid = TEXTOID },
> + { .attname = "relname", .atttypid = TEXTOID },
> + { .attname = "conflict_type", .atttypid = TEXTOID },
> + { .attname = "remote_xid", .atttypid = XIDOID },
> + { .attname = "remote_commit_lsn",.atttypid = LSNOID },
> + { .attname = "remote_commit_ts", .atttypid = TIMESTAMPTZOID },
> + { .attname = "remote_origin", .atttypid = TEXTOID },
> + { .attname = "replica_identity", .atttypid = JSONOID },
> + { .attname = "remote_tuple", .atttypid = JSONOID },
> + { .attname = "local_conflicts", .atttypid = JSONARRAYOID }
> +};
>
...
>
> 13c.
> TBH, I preferred code how it used to be -- where all the CLT constants
> and structs and enums and schemas were kept together. Now they are
> split across conflict.h and conflict.c making it harder to read as
> well as introducing need for static asserts that were not needed
> before.
>

That would lead to unnecessary inclusions at multiple places where it
is not required. See my 4th comment in email [1].

[1]: https://www.postgresql.org/message-id/CAA4eK1LhOHa_TEznw%2BgFoq%2Bw0vMvvsDG2g9Xq8Mwa8xZMY73og%40mail.gmail.com

--
With Regards,
Amit Kapila.

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message shveta malik 2026-05-14 11:06:18 Re: synchronized_standby_slots behavior inconsistent with quorum-based synchronous replication
Previous Message Nitin Motiani 2026-05-14 10:09:09 Re: Adding pg_dump flag for parallel export to pipes