]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Reject ADD CONSTRAINT NOT NULL if name mismatches existing constraint
authorÁlvaro Herrera <alvherre@kurilemu.de>
Tue, 3 Feb 2026 11:33:29 +0000 (12:33 +0100)
committerÁlvaro Herrera <alvherre@kurilemu.de>
Tue, 3 Feb 2026 11:33:29 +0000 (12:33 +0100)
When using ALTER TABLE ... ADD CONSTRAINT to add a not-null constraint
with an explicit name, we have to ensure that if the column is already
marked NOT NULL, the provided name matches the existing constraint name.
Failing to do so could lead to confusion regarding which constraint
object actually enforces the rule.

This patch adds a check to throw an error if the user tries to add a
named not-null constraint to a column that already has one with a
different name.

Reported-by: yanliang lei <msdnchina@163.com>
Co-authored-by: Álvaro Herrera <alvherre@kurilemu.de>
Co-authored-bu: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Backpatch-through: 18
Discussion: https://postgr.es/m/19351-8f1c523ead498545%40postgresql.org

src/backend/catalog/heap.c
src/backend/catalog/pg_constraint.c
src/include/catalog/pg_constraint.h
src/test/regress/expected/constraints.out
src/test/regress/sql/constraints.sql

index fd6537567ea27a3ac8e179822406e3a9b16fce20..5b558f30c7a76b68cf2c6a4eadb1034348979c1d 100644 (file)
@@ -2635,6 +2635,7 @@ AddRelationNewConstraints(Relation rel,
                         * requested validity.
                         */
                        if (AdjustNotNullInheritance(RelationGetRelid(rel), colnum,
+                                                                                cdef->conname,
                                                                                 is_local, cdef->is_no_inherit,
                                                                                 cdef->skip_validation))
                                continue;
index 2d5ac1ea8138b92e6eb05cb7dcab97f5b845ac6a..afdfac8eca636e14c17d97fceaf0bc0a279f05de 100644 (file)
@@ -731,14 +731,15 @@ extractNotNullColumn(HeapTuple constrTup)
  * If a constraint exists but the connoinherit flag is not what the caller
  * wants, throw an error about the incompatibility.  If the desired
  * constraint is valid but the existing constraint is not valid, also
- * throw an error about that (the opposite case is acceptable).
+ * throw an error about that (the opposite case is acceptable).  If
+ * the proposed constraint has a different name, also throw an error.
  *
  * If everything checks out, we adjust conislocal/coninhcount and return
  * true.  If is_local is true we flip conislocal true, or do nothing if
  * it's already true; otherwise we increment coninhcount by 1.
  */
 bool
-AdjustNotNullInheritance(Oid relid, AttrNumber attnum,
+AdjustNotNullInheritance(Oid relid, AttrNumber attnum, const char *new_conname,
                                                 bool is_local, bool is_no_inherit, bool is_notvalid)
 {
        HeapTuple       tup;
@@ -777,6 +778,22 @@ AdjustNotNullInheritance(Oid relid, AttrNumber attnum,
                                        errhint("You might need to validate it using %s.",
                                                        "ALTER TABLE ... VALIDATE CONSTRAINT"));
 
+               /*
+                * If, for a new constraint that is being defined locally (i.e., not
+                * being passed down via inheritance), a name was specified, then
+                * verify that the existing constraint has the same name.  Otherwise
+                * throw an error.  Names of inherited constraints are ignored because
+                * they are not directly user-specified, so matching is not important.
+                */
+               if (is_local && new_conname &&
+                       strcmp(new_conname, NameStr(conform->conname)) != 0)
+                       ereport(ERROR,
+                                       errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                       errmsg("cannot create not-null constraint \"%s\" on column \"%s\" of table \"%s\"",
+                                                  new_conname, get_attname(relid, attnum, false), get_rel_name(relid)),
+                                       errdetail("A not-null constraint named \"%s\" already exists for this column.",
+                                                         NameStr(conform->conname)));
+
                if (!is_local)
                {
                        if (pg_add_s16_overflow(conform->coninhcount, 1,
index 4afceb5c692d2a224ca830998566605b5dd39b07..3a03b8d801aba0d2bb1bb0d8bce4b1b5a3e697e6 100644 (file)
@@ -263,7 +263,7 @@ extern HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum);
 extern HeapTuple findNotNullConstraint(Oid relid, const char *colname);
 extern HeapTuple findDomainNotNullConstraint(Oid typid);
 extern AttrNumber extractNotNullColumn(HeapTuple constrTup);
-extern bool AdjustNotNullInheritance(Oid relid, AttrNumber attnum,
+extern bool AdjustNotNullInheritance(Oid relid, AttrNumber attnum, const char *new_conname,
                                                                         bool is_local, bool is_no_inherit, bool is_notvalid);
 extern List *RelationGetNotNullConstraints(Oid relid, bool cooked,
                                                                                   bool include_noinh);
index 1bbf59cca023de1d8c6e030944b44a29287521b0..ebc892a2a4280589ed51f3c1189ef77299eb3e66 100644 (file)
@@ -846,8 +846,12 @@ CREATE TABLE notnull_tbl1 (a INTEGER NOT NULL NOT NULL);
 Not-null constraints:
     "notnull_tbl1_a_not_null" NOT NULL "a"
 
--- no-op
+-- specifying an existing constraint is a no-op
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT notnull_tbl1_a_not_null NOT NULL a;
+-- but using a different constraint name is not allowed
 ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a;
+ERROR:  cannot create not-null constraint "nn" on column "a" of table "notnull_tbl1"
+DETAIL:  A not-null constraint named "notnull_tbl1_a_not_null" already exists for this column.
 \d+ notnull_tbl1
                                Table "public.notnull_tbl1"
  Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
index 733a1dbccfe30be9da6e6452af5d5db1fb408605..1e9989698b68669080b5eebcdacc8a12ab1bfeb4 100644 (file)
@@ -623,7 +623,9 @@ DROP TABLE deferred_excl;
 -- verify constraints created for NOT NULL clauses
 CREATE TABLE notnull_tbl1 (a INTEGER NOT NULL NOT NULL);
 \d+ notnull_tbl1
--- no-op
+-- specifying an existing constraint is a no-op
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT notnull_tbl1_a_not_null NOT NULL a;
+-- but using a different constraint name is not allowed
 ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a;
 \d+ notnull_tbl1
 -- duplicate name