On Mon, May 4, 2026 at 4:59 PM Amit Kapila <[email protected]> wrote:
>
> On Sat, May 2, 2026 at 2:40 PM Dilip Kumar <[email protected]> wrote:
> >
> > On Fri, May 1, 2026 at 7:16 PM Dilip Kumar <[email protected]> wrote:
> > >
> > > 4. pg_conflict is the catalog schema and as Nisha reported,
> > > non-superusers aren't allowed to access the objects within it. Because
> > > of this, SELECT, DELETE, and TRUNCATE are disallowed even for the
> > > subscription owner if that owner is a non-superuser. I am working on
> > > the fix.
> >
> > While analyzing this, I realized that the schema ACL check happens
> > very early in analyze phase [1]. I'm not sure if we can bypass the
> > subscription owner from this check at that stage without implementing
> > a hacky solution.  Another option is to remove restrictions from the
> > pg_conflict schema for all users and keep only table-level
> > restrictions within that schema. I am exploring how to implement this.
> >
>
> How about if we grant usage privilege on pg_conflict schema to
> pg_create_subscription role and then allow only select, delete,
> truncate to table_owners on tables in pg_conflict schema? Internally
> the apply_worker can still make inserts to clt table in pg_conflict
> schema similar to what we do for toast tables.

I am still testing, but I quickly prototyped this approach and basic
things seem to be working.

<Test case Start>
dilipkumarb@dilipkumarb:~/PG/install$ psql -p 5433
postgres[3614939]=# CREATE USER dilip LOGIN ;
GRANT pg_create_subscription TO dilip;
GRANT ALL ON DATABASE postgres TO dilip;
postgres[3614939]=# \q

-- Connect to nonsuper user--
dilipkumarb@dilipkumarb:~/PG/install$ psql -p 5433 -U dilip
postgres[3615002]=> CREATE SUBSCRIPTION regress_clt_perm_test CONNECTION
'dbname=regress_doesnotexist password=pass' PUBLICATION testpub WITH
(connect = false, conflict_log_destination = 'table');

postgres[3615002]=> select * from pg_conflict.pg_conflict_log_164
pg_conflict.pg_conflict_log_16406  pg_conflict.pg_conflict_log_16412
postgres[3615002]=> select * from pg_conflict.pg_conflict_log_16412;
 relid | schemaname | relname | conflict_type | remote_xid |
remote_commit_lsn | remote_commit_ts | remote_origin |
replica_identity | remote_tuple | local
_conflicts
-------+------------+---------+---------------+------------+-------------------+------------------+---------------+------------------+--------------+------
-----------
(0 rows)

postgres[3615002]=> delete from pg_conflict.pg_conflict_log_16412;
DELETE 0
postgres[3615002]=> TRUNCATE pg_conflict.pg_conflict_log_16412;
TRUNCATE TABLE
postgres[3615002]=> \q
dilipkumarb@dilipkumarb:~/PG/install$ psql -p 5433
psql (19devel)
Type "help" for help.

--Create another user to test non subscription owner which has
pg_create_subscription role granted do not have access on another
subscription's conflict log tables
postgres[3615293]=# CREATE USER dilip1 LOGIN;
GRANT pg_create_subscription TO dilip1;
GRANT ALL ON DATABASE postgres TO dilip1;
dilipkumarb@dilipkumarb:~/PG/install$ psql -p 5433 -U dilip1
psql (19devel)
Type "help" for help.

postgres[3615370]=> select * from pg_conflict.pg_conflict_log_16412;
ERROR:  42501: permission denied for table pg_conflict_log_16412
LOCATION:  aclcheck_error, aclchk.c:2813
postgres[3615370]=> delete from pg_conflict.pg_conflict_log_16412;
ERROR:  42501: permission denied for table pg_conflict_log_16412
LOCATION:  aclcheck_error, aclchk.c:2813

<Test Case Ends>

PFA, poc patch for the same.

-- 
Regards,
Dilip Kumar
Google
From 3c21d00d7355d97d3bea62f010db773f469ac54c Mon Sep 17 00:00:00 2001
From: Dilip Kumar <[email protected]>
Date: Mon, 4 May 2026 18:25:45 +0530
Subject: [PATCH] test ownership

---
 src/backend/catalog/aclchk.c | 2 +-
 src/bin/initdb/initdb.c      | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 67424fe3b0c..ee814a534a4 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3345,7 +3345,7 @@ pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode 
mask,
         * themselves.  ACL_USAGE is if we ever have system sequences.
         */
        if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | 
ACL_USAGE)) &&
-               IsSystemClass(table_oid, classForm) &&
+               IsSystemClass(table_oid, classForm) && 
!IsConflictClass(classForm) &&
                classForm->relkind != RELKIND_VIEW &&
                !superuser_arg(roleid))
                mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | 
ACL_USAGE);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 14cb79c26be..fa3316fcb97 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1839,6 +1839,7 @@ setup_privileges(FILE *cmdfd)
                                  "  AND relacl IS NULL;\n\n",
                                  escape_quotes(username));
        PG_CMD_PUTS("GRANT USAGE ON SCHEMA pg_catalog, public TO PUBLIC;\n\n");
+       PG_CMD_PUTS("GRANT USAGE ON SCHEMA pg_conflict TO 
pg_create_subscription;\n\n");
        PG_CMD_PUTS("REVOKE ALL ON pg_largeobject FROM PUBLIC;\n\n");
        PG_CMD_PUTS("INSERT INTO pg_init_privs "
                                "  (objoid, classoid, objsubid, initprivs, 
privtype)"
-- 
2.49.0

Reply via email to