Invalidate partitions of table being attached/detached
authorAlvaro Herrera
Mon, 18 Oct 2021 22:08:25 +0000 (19:08 -0300)
committerAlvaro Herrera
Mon, 18 Oct 2021 22:08:25 +0000 (19:08 -0300)
Failing to do that, any direct inserts/updates of those partitions
would fail to enforce the correct constraint, that is, one that
considers the new partition constraint of their parent table.

Backpatch to 10.

Reported by: Hou Zhijie 
Author: Amit Langote 
Author: Álvaro Herrera 
Reviewed-by: Nitin Jadhav
Reviewed-by: Pavel Borisov
Discussion: https://postgr.es/m/OS3PR01MB5718DA1C4609A25186D1FBF194089%40OS3PR01MB5718.jpnprd01.prod.outlook.com

src/backend/commands/tablecmds.c
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 762f431ab883dc9f5f3a0732e076ba3063b85320..9322829b234045723454a5d594aef632a1730248 100644 (file)
@@ -17307,6 +17307,22 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 
    ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
 
+   /*
+    * If the partition we just attached is partitioned itself, invalidate
+    * relcache for all descendent partitions too to ensure that their
+    * rd_partcheck expression trees are rebuilt; partitions already locked
+    * at the beginning of this function.
+    */
+   if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+   {
+       ListCell *l;
+
+       foreach(l, attachrel_children)
+       {
+           CacheInvalidateRelcacheByRelid(lfirst_oid(l));
+       }
+   }
+
    /* keep our lock until commit */
    table_close(attachrel, NoLock);
 
@@ -17997,6 +18013,25 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
     * included in its partition descriptor.
     */
    CacheInvalidateRelcache(rel);
+
+   /*
+    * If the partition we just detached is partitioned itself, invalidate
+    * relcache for all descendent partitions too to ensure that their
+    * rd_partcheck expression trees are rebuilt; must lock partitions
+    * before doing so, using the same lockmode as what partRel has been
+    * locked with by the caller.
+    */
+   if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+   {
+       List   *children;
+
+       children = find_all_inheritors(RelationGetRelid(partRel),
+                                      AccessExclusiveLock, NULL);
+       foreach(cell, children)
+       {
+           CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
+       }
+   }
 }
 
 /*
index ad4ea303d0103cd3089d04dfda13608bbe6cfe02..0f87619de020e3f098e54072bab8fb86be4aa54a 100644 (file)
@@ -4496,3 +4496,23 @@ select indexrelid::regclass, indisclustered from pg_index
 (2 rows)
 
 drop table alttype_cluster;
+--
+-- Check that attaching or detaching a partitioned partition correctly leads
+-- to its partitions' constraint being updated to reflect the parent's
+-- newly added/removed constraint
+create table target_parted (a int, b int) partition by list (a);
+create table attach_parted (a int, b int) partition by list (b);
+create table attach_parted_part1 partition of attach_parted for values in (1);
+-- insert a row directly into the leaf partition so that its partition
+-- constraint is built and stored in the relcache
+insert into attach_parted_part1 values (1, 1);
+-- the following better invalidate the partition constraint of the leaf
+-- partition too...
+alter table target_parted attach partition attach_parted for values in (1);
+-- ...such that the following insert fails
+insert into attach_parted_part1 values (2, 1);
+ERROR:  new row for relation "attach_parted_part1" violates partition constraint
+DETAIL:  Failing row contains (2, 1).
+-- ...and doesn't when the partition is detached along with its own partition
+alter table target_parted detach partition attach_parted;
+insert into attach_parted_part1 values (2, 1);
index b269444acbada69fc5edc8a43a13173f3c797568..633bcb147fc70483bd029f505281d3c98244b0f1 100644 (file)
@@ -2949,3 +2949,22 @@ select indexrelid::regclass, indisclustered from pg_index
   where indrelid = 'alttype_cluster'::regclass
   order by indexrelid::regclass::text;
 drop table alttype_cluster;
+
+--
+-- Check that attaching or detaching a partitioned partition correctly leads
+-- to its partitions' constraint being updated to reflect the parent's
+-- newly added/removed constraint
+create table target_parted (a int, b int) partition by list (a);
+create table attach_parted (a int, b int) partition by list (b);
+create table attach_parted_part1 partition of attach_parted for values in (1);
+-- insert a row directly into the leaf partition so that its partition
+-- constraint is built and stored in the relcache
+insert into attach_parted_part1 values (1, 1);
+-- the following better invalidate the partition constraint of the leaf
+-- partition too...
+alter table target_parted attach partition attach_parted for values in (1);
+-- ...such that the following insert fails
+insert into attach_parted_part1 values (2, 1);
+-- ...and doesn't when the partition is detached along with its own partition
+alter table target_parted detach partition attach_parted;
+insert into attach_parted_part1 values (2, 1);