Add missing CommandCounterIncrement
authorÁlvaro Herrera <alvherre@alvh.no-ip.org>
Sun, 26 Jan 2025 16:34:28 +0000 (17:34 +0100)
committerÁlvaro Herrera <alvherre@alvh.no-ip.org>
Sun, 26 Jan 2025 16:34:28 +0000 (17:34 +0100)
For commit b663b9436e75 I thought this was useless, but turns out not to
be for the case where a partitioned table has two identical foreign key
constraints which can both be matched by the same constraint in a
partition during attach.  This CCI makes the match search for the second
constraint in the parent ignore the constraint in the child that has
already been matched by the first constraint in the parent.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/c599253c-1ccd-4161-80fc-c9065e037a09@gmail.com

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

index 57662fd76627e31e976261a144423d5d6341cef9..96a48ba82c1261d063d8bf1daa9a00989ffe80c5 100644 (file)
@@ -11564,22 +11564,22 @@ tryAttachPartitionForeignKey(List **wqueue,
        table_close(pg_constraint, RowShareLock);
    }
 
+   /*
+    * We updated this pg_constraint row above to set its parent; validating
+    * it will cause its convalidated flag to change, so we need CCI here.  In
+    * addition, we need it unconditionally for the rare case where the parent
+    * table has *two* identical constraints; when reaching this function for
+    * the second one, we must have made our changes visible, otherwise we
+    * would try to attach both to this one.
+    */
+   CommandCounterIncrement();
+
    /* If validation is needed, put it in the queue now. */
    if (queueValidation)
    {
        Relation    conrel;
 
-       /*
-        * We updated this pg_constraint row above to set its parent;
-        * validating it will cause its convalidated flag to change, so we
-        * need CCI here.  XXX it might work better to effect the convalidated
-        * changes for all constraints together during phase 3, but that
-        * requires more invasive code surgery.
-        */
-       CommandCounterIncrement();
-
        conrel = table_open(ConstraintRelationId, RowExclusiveLock);
-
        partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
        if (!HeapTupleIsValid(partcontup))
            elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
index e5842a208c01e9ca66022678f1f419ecabe60df5..374dcb266e74aec4f8b37d161cb738504ea0f892 100644 (file)
@@ -2062,6 +2062,16 @@ CREATE TABLE fk_partitioned_fk_6 (a int REFERENCES fk_partitioned_pk_6) PARTITIO
 ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_pk_6 FOR VALUES IN (1);
 ERROR:  cannot attach table "fk_partitioned_pk_6" as a partition because it is referenced by foreign key "fk_partitioned_fk_6_a_fkey"
 DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6;
+-- Verify that attaching to a parent with two identical constraints work
+CREATE TABLE fk_partitioned_pk_6 (a int PRIMARY KEY);
+CREATE TABLE fk_partitioned_fk_6 (a int,
+   FOREIGN KEY (a) REFERENCES fk_partitioned_pk_6,
+   FOREIGN KEY (a) REFERENCES fk_partitioned_pk_6
+) PARTITION BY LIST (a);
+CREATE TABLE fk_partitioned_fk_6_1 PARTITION OF fk_partitioned_fk_6 FOR VALUES IN (1);
+ALTER TABLE fk_partitioned_fk_6 DETACH PARTITION fk_partitioned_fk_6_1;
+ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_fk_6_1 FOR VALUES IN (1);
+DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6;
 -- This case is similar to above, but the referenced relation is one level
 -- lower in the hierarchy.  This one fails in a different way as the above,
 -- because we don't bother to protect against this case explicitly.  If the
index 45ad41fe59f463d34cc9ca0db6f16de18165ffa0..bc0adb8cfe9f306a852755efe1a673350eaffbee 100644 (file)
@@ -1491,6 +1491,17 @@ CREATE TABLE fk_partitioned_fk_6 (a int REFERENCES fk_partitioned_pk_6) PARTITIO
 ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_pk_6 FOR VALUES IN (1);
 DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6;
 
+-- Verify that attaching to a parent with two identical constraints work
+CREATE TABLE fk_partitioned_pk_6 (a int PRIMARY KEY);
+CREATE TABLE fk_partitioned_fk_6 (a int,
+   FOREIGN KEY (a) REFERENCES fk_partitioned_pk_6,
+   FOREIGN KEY (a) REFERENCES fk_partitioned_pk_6
+) PARTITION BY LIST (a);
+CREATE TABLE fk_partitioned_fk_6_1 PARTITION OF fk_partitioned_fk_6 FOR VALUES IN (1);
+ALTER TABLE fk_partitioned_fk_6 DETACH PARTITION fk_partitioned_fk_6_1;
+ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_fk_6_1 FOR VALUES IN (1);
+DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6;
+
 -- This case is similar to above, but the referenced relation is one level
 -- lower in the hierarchy.  This one fails in a different way as the above,
 -- because we don't bother to protect against this case explicitly.  If the