Choose FK name correctly during partition attachment
authorAlvaro Herrera <[email protected]>
Thu, 8 Sep 2022 11:17:02 +0000 (13:17 +0200)
committerAlvaro Herrera <[email protected]>
Thu, 8 Sep 2022 11:17:02 +0000 (13:17 +0200)
During ALTER TABLE ATTACH PARTITION, if the name of a parent's foreign
key constraint is already used on the partition, the code tries to
choose another one before the FK attributes list has been populated,
so the resulting constraint name was "<relname>__fkey" instead of
"<relname>_<attrs>_fkey".  Repair, and add a test case.

Backpatch to 12.  In 11, the code to attach a partition was not smart
enough to cope with conflicting constraint names, so the problem doesn't
exist there.

Author: Jehan-Guillaume de Rorthais <[email protected]>
Discussion: https://postgr.es/m/20220901184156.738ebee5@karst

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

index dacc989d855ac2ccbd116beccb32db15b10ea6a4..53b0f3a9c1e03b005d6103ccef8111d1f76d7833 100644 (file)
@@ -10304,16 +10304,6 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 
                /* No dice.  Set up to create our own constraint */
                fkconstraint = makeNode(Constraint);
-               if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
-                                                                RelationGetRelid(partRel),
-                                                                NameStr(constrForm->conname)))
-                       fkconstraint->conname =
-                               ChooseConstraintName(RelationGetRelationName(partRel),
-                                                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
-                                                                        "fkey",
-                                                                        RelationGetNamespace(partRel), NIL);
-               else
-                       fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
                fkconstraint->fk_upd_action = constrForm->confupdtype;
                fkconstraint->fk_del_action = constrForm->confdeltype;
                fkconstraint->deferrable = constrForm->condeferrable;
@@ -10328,6 +10318,16 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
                        fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
                                                                                         makeString(NameStr(att->attname)));
                }
+               if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+                                                                RelationGetRelid(partRel),
+                                                                NameStr(constrForm->conname)))
+                       fkconstraint->conname =
+                               ChooseConstraintName(RelationGetRelationName(partRel),
+                                                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
+                                                                        "fkey",
+                                                                        RelationGetNamespace(partRel), NIL);
+               else
+                       fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
 
                indexOid = constrForm->conindid;
                constrOid =
index 05b7244e4a7fd1cedc18d73be05a758523cfa5dd..e6f6602d9532189987091e105163be6b6de542d2 100644 (file)
@@ -603,6 +603,29 @@ COMMIT;
 ERROR:  duplicate key value violates unique constraint "parted_uniq_tbl_1_i_key"
 DETAIL:  Key (i)=(1) already exists.
 DROP TABLE parted_uniq_tbl;
+-- test naming a constraint in a partition when a conflict exists
+CREATE TABLE parted_fk_naming (
+    id bigint NOT NULL default 1,
+    id_abc bigint,
+    CONSTRAINT dummy_constr FOREIGN KEY (id_abc)
+        REFERENCES parted_fk_naming (id),
+    PRIMARY KEY (id)
+)
+PARTITION BY LIST (id);
+CREATE TABLE parted_fk_naming_1 (
+    id bigint NOT NULL default 1,
+    id_abc bigint,
+    PRIMARY KEY (id),
+    CONSTRAINT dummy_constr CHECK (true)
+);
+ALTER TABLE parted_fk_naming ATTACH PARTITION parted_fk_naming_1 FOR VALUES IN ('1');
+SELECT conname FROM pg_constraint WHERE conrelid = 'parted_fk_naming_1'::regclass AND contype = 'f';
+            conname             
+--------------------------------
+ parted_fk_naming_1_id_abc_fkey
+(1 row)
+
+DROP TABLE parted_fk_naming;
 -- test a HOT update that invalidates the conflicting tuple.
 -- the trigger should still fire and catch the violation
 BEGIN;
index 833819a32e29b1fca1fcb9ff7472dea0af4b3fc9..5ffcd4ffc7be6cd56620cf46a7ebf3035bfa200b 100644 (file)
@@ -430,6 +430,25 @@ INSERT INTO parted_uniq_tbl VALUES (1);    -- OK now, fail at commit
 COMMIT;
 DROP TABLE parted_uniq_tbl;
 
+-- test naming a constraint in a partition when a conflict exists
+CREATE TABLE parted_fk_naming (
+    id bigint NOT NULL default 1,
+    id_abc bigint,
+    CONSTRAINT dummy_constr FOREIGN KEY (id_abc)
+        REFERENCES parted_fk_naming (id),
+    PRIMARY KEY (id)
+)
+PARTITION BY LIST (id);
+CREATE TABLE parted_fk_naming_1 (
+    id bigint NOT NULL default 1,
+    id_abc bigint,
+    PRIMARY KEY (id),
+    CONSTRAINT dummy_constr CHECK (true)
+);
+ALTER TABLE parted_fk_naming ATTACH PARTITION parted_fk_naming_1 FOR VALUES IN ('1');
+SELECT conname FROM pg_constraint WHERE conrelid = 'parted_fk_naming_1'::regclass AND contype = 'f';
+DROP TABLE parted_fk_naming;
+
 -- test a HOT update that invalidates the conflicting tuple.
 -- the trigger should still fire and catch the violation