]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid name collision with NOT NULL constraints
authorÁlvaro Herrera <alvherre@kurilemu.de>
Sat, 21 Feb 2026 11:22:08 +0000 (12:22 +0100)
committerÁlvaro Herrera <alvherre@kurilemu.de>
Sat, 21 Feb 2026 11:22:08 +0000 (12:22 +0100)
If a CREATE TABLE statement defined a constraint whose name is identical
to the name generated for a NOT NULL constraint, we'd throw an
(unnecessary) unique key violation error on
pg_constraint_conrelid_contypid_conname_index: this can easily be
avoided by choosing a different name for the NOT NULL constraint.

Fix by passing the constraint names already created by
AddRelationNewConstraints() to AddRelationNotNullConstraints(), so that
the latter can avoid name collisions with them.

Bug: #19393
Author: Laurenz Albe <laurenz.albe@cybertec.at>
Reported-by: Hüseyin Demir <huseyin.d3r@gmail.com>
Backpatch-through: 18
Discussion: https://postgr.es/m/19393-6a82427485a744cf@postgresql.org

src/backend/catalog/heap.c
src/backend/commands/tablecmds.c
src/include/catalog/heap.h
src/test/regress/expected/create_table.out
src/test/regress/sql/create_table.sql

index a6ed9849e7775d337810b84320ee72353da67868..5748aa9a1a9afd5c9f8d5aae2a150d0557a48bc4 100644 (file)
@@ -2886,14 +2886,16 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
  * for each column, giving priority to user-specified ones, and setting
  * inhcount according to how many parents cause each column to get a
  * not-null constraint.  If a user-specified name clashes with another
- * user-specified name, an error is raised.
+ * user-specified name, an error is raised.  'existing_constraints'
+ * is a list of already defined constraint names, which should be avoided
+ * when generating further ones.
  *
  * Returns a list of AttrNumber for columns that need to have the attnotnull
  * flag set.
  */
 List *
 AddRelationNotNullConstraints(Relation rel, List *constraints,
-                                                         List *old_notnulls)
+                                                         List *old_notnulls, List *existing_constraints)
 {
        List       *givennames;
        List       *nnnames;
@@ -2905,7 +2907,7 @@ AddRelationNotNullConstraints(Relation rel, List *constraints,
         * because we must raise error for user-generated name conflicts, but for
         * system-generated name conflicts we just generate another.
         */
-       nnnames = NIL;
+       nnnames = list_copy(existing_constraints);      /* don't scribble on input */
        givennames = NIL;
 
        /*
index 2f5b7007ff9ec981408c3ad23d75acccd9dff74a..df1ba112b35bc3bfdd514c655320230bd77ef073 100644 (file)
@@ -781,6 +781,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
        List       *rawDefaults;
        List       *cookedDefaults;
        List       *nncols;
+       List       *connames = NIL;
        Datum           reloptions;
        ListCell   *listptr;
        AttrNumber      attnum;
@@ -1335,11 +1336,20 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
        /*
         * Now add any newly specified CHECK constraints to the new relation. Same
         * as for defaults above, but these need to come after partitioning is set
-        * up.
+        * up.  We save the constraint names that were used, to avoid dupes below.
         */
        if (stmt->constraints)
-               AddRelationNewConstraints(rel, NIL, stmt->constraints,
-                                                                 true, true, false, queryString);
+       {
+               List       *conlist;
+
+               conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
+                                                                                       true, true, false, queryString);
+               foreach_ptr(CookedConstraint, cons, conlist)
+               {
+                       if (cons->name != NULL)
+                               connames = lappend(connames, cons->name);
+               }
+       }
 
        /*
         * Finally, merge the not-null constraints that are declared directly with
@@ -1348,7 +1358,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
         * columns that don't yet have it.
         */
        nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
-                                                                                  old_notnulls);
+                                                                                  old_notnulls, connames);
        foreach_int(attrnum, nncols)
                set_attnotnull(NULL, rel, attrnum, true, false);
 
index 624c415dadb5831c734d72c1a6cfbbc1243a7efc..6c9ac812aa02bf48c9a3225ee85dd360e37c6649 100644 (file)
@@ -117,7 +117,8 @@ extern List *AddRelationNewConstraints(Relation rel,
                                                                           const char *queryString);
 extern List *AddRelationNotNullConstraints(Relation rel,
                                                                                   List *constraints,
-                                                                                  List *old_notnulls);
+                                                                                  List *old_notnulls,
+                                                                                  List *existing_constraints);
 
 extern void RelationClearMissing(Relation rel);
 
index 029beb20aaee3fb0115f665f020e8bb96e89cd0e..90639ea0d8948498ae9756314b7ff2cc9589d2b5 100644 (file)
@@ -161,6 +161,12 @@ ALTER TABLE remember_node_subid ALTER c TYPE bigint;
 SAVEPOINT q; DROP TABLE remember_node_subid; ROLLBACK TO q;
 COMMIT;
 DROP TABLE remember_node_subid;
+-- generated NOT NULL constraint names must not collide with explicitly named constraints
+CREATE TABLE two_not_null_constraints (
+   col integer NOT NULL,
+   CONSTRAINT two_not_null_constraints_col_not_null CHECK (col IS NOT NULL)
+);
+DROP TABLE two_not_null_constraints;
 --
 -- Partitioned tables
 --
index 9b3e93b416426c2c4ce79a2df596937dc8ee8ea6..80e424e6bdad3576b8f599e4fe82d6e6c68949a2 100644 (file)
@@ -105,6 +105,13 @@ SAVEPOINT q; DROP TABLE remember_node_subid; ROLLBACK TO q;
 COMMIT;
 DROP TABLE remember_node_subid;
 
+-- generated NOT NULL constraint names must not collide with explicitly named constraints
+CREATE TABLE two_not_null_constraints (
+   col integer NOT NULL,
+   CONSTRAINT two_not_null_constraints_col_not_null CHECK (col IS NOT NULL)
+);
+DROP TABLE two_not_null_constraints;
+
 --
 -- Partitioned tables
 --