From cf42dee8571e7c584cd1f7435ebe0d08f08be4b9 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" Date: Thu, 11 Dec 2025 17:03:37 +0800 Subject: [PATCH v2 1/2] Inherit replica identity for new and attached partitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a replica identity is set on a partitioned table, newly created partitions and tables attached as partitions should inherit the parent’s replica identity setting. Previously, such relations kept the default replica identity, which could lead to inconsistent behavior in logical replication. Update StorePartitionBound() to copy relreplident from the parent relation when marking a table as a partition. This ensures consistent replica identity semantics across partition hierarchies, including multi-level partitioning. Document the behavior in ALTER TABLE ... REPLICA IDENTITY, and add regression tests covering: * partitions created with CREATE TABLE ... PARTITION OF * tables attached via ALTER TABLE ... ATTACH PARTITION * inheritance through partitioned children and grandchildren Author: Chao Li Discussion: https://postgr.es/m/CAEoWx2nJ71hy8R614HQr7vQhkBReO9AANPODPg0aSQs74eOdLQ@mail.gmail.com --- doc/src/sgml/ref/alter_table.sgml | 5 ++ src/backend/catalog/heap.c | 8 ++- .../regress/expected/replica_identity.out | 69 +++++++++++++++++++ src/test/regress/sql/replica_identity.sql | 41 +++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 9abd8037f28..fb9140c8794 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -965,6 +965,11 @@ WITH ( MODULUS numeric_literal, REM from the new value; however, if the old value is stored externally, it is always logged regardless of whether it changed. This option has no effect except when logical replication is in use. + When a replica identity is set on a partitioned table, new partitions + created with CREATE TABLE ... PARTITION OF and tables + attached with ALTER TABLE ... ATTACH PARTITION inherit + that replica identity, though it can still be changed for a partition + afterwards. DEFAULT diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 265cc3e5fbf..4ffc9de064e 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -4036,8 +4036,9 @@ RemovePartitionKeyByRelId(Oid relid) /* * StorePartitionBound - * Update pg_class tuple of rel to store the partition bound and set - * relispartition to true + * Update pg_class tuple of rel to store the partition bound, set + * relreplident to the same of the parent and set relispartition + * to true * * If this is the default partition, also update the default partition OID in * pg_partitioned_table. @@ -4090,6 +4091,9 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) /* Also set the flag */ ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = true; + /* Copy replication identity from parent if needed */ + ((Form_pg_class) GETSTRUCT(newtuple))->relreplident = parent->rd_rel->relreplident; + /* * We already checked for no inheritance children, but reset * relhassubclass in case it was left over. diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index b9b8dde018f..9766bd5af89 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -290,6 +290,73 @@ ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey; ERROR: constraint "test_replica_identity5_pkey" of relation "test_replica_identity5" does not exist ALTER TABLE test_replica_identity5 ALTER b DROP NOT NULL; ERROR: column "b" is in index used as replica identity +-- New partitions inherit parent's replica identity setting +CREATE TABLE test_replica_identity6 (a int, b int) PARTITION BY LIST (a); +ALTER TABLE test_replica_identity6 REPLICA IDENTITY FULL; +CREATE TABLE test_replica_identity6_p1 PARTITION OF test_replica_identity6 + FOR VALUES IN (1); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p1'::regclass; + relreplident +-------------- + f +(1 row) + +CREATE TABLE test_replica_identity6_p2 (a int, b int); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p2'::regclass; + relreplident +-------------- + d +(1 row) + +ALTER TABLE test_replica_identity6 ATTACH PARTITION test_replica_identity6_p2 + FOR VALUES IN (2); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p2'::regclass; + relreplident +-------------- + f +(1 row) + +-- Partitioned child tables and their grandchildren inherit replica identity +CREATE TABLE test_replica_identity7 (a int, b int) PARTITION BY LIST (a); +ALTER TABLE test_replica_identity7 REPLICA IDENTITY FULL; +CREATE TABLE test_replica_identity7_p PARTITION OF test_replica_identity7 + FOR VALUES IN (1) PARTITION BY LIST (b); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_p'::regclass; + relreplident +-------------- + f +(1 row) + +CREATE TABLE test_replica_identity7_p1 PARTITION OF test_replica_identity7_p + FOR VALUES IN (1); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_p1'::regclass; + relreplident +-------------- + f +(1 row) + +CREATE TABLE test_replica_identity7_leaf (a int, b int); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_leaf'::regclass; + relreplident +-------------- + d +(1 row) + +ALTER TABLE test_replica_identity7_p ATTACH PARTITION test_replica_identity7_leaf + FOR VALUES IN (2); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_leaf'::regclass; + relreplident +-------------- + f +(1 row) + DROP TABLE test_replica_identity; DROP TABLE test_replica_identity2; DROP TABLE test_replica_identity3; @@ -297,3 +364,5 @@ DROP TABLE test_replica_identity4; DROP TABLE test_replica_identity5; DROP TABLE test_replica_identity_othertable; DROP TABLE test_replica_identity_t3; +DROP TABLE test_replica_identity6; +DROP TABLE test_replica_identity7; diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql index 30daec05b71..7c0c7365c26 100644 --- a/src/test/regress/sql/replica_identity.sql +++ b/src/test/regress/sql/replica_identity.sql @@ -134,6 +134,45 @@ ALTER TABLE test_replica_identity5 ALTER b SET NOT NULL; ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey; ALTER TABLE test_replica_identity5 ALTER b DROP NOT NULL; +-- New partitions inherit parent's replica identity setting +CREATE TABLE test_replica_identity6 (a int, b int) PARTITION BY LIST (a); +ALTER TABLE test_replica_identity6 REPLICA IDENTITY FULL; + +CREATE TABLE test_replica_identity6_p1 PARTITION OF test_replica_identity6 + FOR VALUES IN (1); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p1'::regclass; + +CREATE TABLE test_replica_identity6_p2 (a int, b int); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p2'::regclass; +ALTER TABLE test_replica_identity6 ATTACH PARTITION test_replica_identity6_p2 + FOR VALUES IN (2); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity6_p2'::regclass; + +-- Partitioned child tables and their grandchildren inherit replica identity +CREATE TABLE test_replica_identity7 (a int, b int) PARTITION BY LIST (a); +ALTER TABLE test_replica_identity7 REPLICA IDENTITY FULL; + +CREATE TABLE test_replica_identity7_p PARTITION OF test_replica_identity7 + FOR VALUES IN (1) PARTITION BY LIST (b); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_p'::regclass; + +CREATE TABLE test_replica_identity7_p1 PARTITION OF test_replica_identity7_p + FOR VALUES IN (1); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_p1'::regclass; + +CREATE TABLE test_replica_identity7_leaf (a int, b int); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_leaf'::regclass; +ALTER TABLE test_replica_identity7_p ATTACH PARTITION test_replica_identity7_leaf + FOR VALUES IN (2); +SELECT relreplident FROM pg_class + WHERE oid = 'test_replica_identity7_leaf'::regclass; + DROP TABLE test_replica_identity; DROP TABLE test_replica_identity2; DROP TABLE test_replica_identity3; @@ -141,3 +180,5 @@ DROP TABLE test_replica_identity4; DROP TABLE test_replica_identity5; DROP TABLE test_replica_identity_othertable; DROP TABLE test_replica_identity_t3; +DROP TABLE test_replica_identity6; +DROP TABLE test_replica_identity7; -- 2.39.5 (Apple Git-154)