Prevent setting a column as identity if its not-null constraint is invalid
authorÁlvaro Herrera <alvherre@kurilemu.de>
Mon, 3 Nov 2025 14:58:19 +0000 (15:58 +0100)
committerÁlvaro Herrera <alvherre@kurilemu.de>
Mon, 3 Nov 2025 14:58:19 +0000 (15:58 +0100)
We don't allow null values to appear in identity-generated columns in
other ways, so we shouldn't let unvalidated not-null constraints do it
either.  Oversight in commit a379061a22a8.

Author: jian he <jian.universality@gmail.com>
Backpatch-through: 18
Discussion: https://postgr.es/m/CACJufxGQM_+vZoYJMaRoZfNyV=L2jxosjv_0TLAScbuLJXWRfQ@mail.gmail.com

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

index 5fd8b51312c8b3e02d505ae42989973b1b428317..34f9e342362cea67cf3fbfb16b0050199cef5825 100644 (file)
@@ -8281,6 +8281,31 @@ ATExecAddIdentity(Relation rel, const char *colName,
                 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
                        colName, RelationGetRelationName(rel))));
 
+   /*
+    * On the other hand, if a not-null constraint exists, then verify that
+    * it's compatible.
+    */
+   if (attTup->attnotnull)
+   {
+       HeapTuple   contup;
+       Form_pg_constraint conForm;
+
+       contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
+                                            attnum);
+       if (!HeapTupleIsValid(contup))
+           elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
+                colName, RelationGetRelationName(rel));
+
+       conForm = (Form_pg_constraint) GETSTRUCT(contup);
+       if (!conForm->convalidated)
+           ereport(ERROR,
+                   errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                   errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
+                          NameStr(conForm->conname), RelationGetRelationName(rel)),
+                   errhint("You might need to validate it using %s.",
+                           "ALTER TABLE ... VALIDATE CONSTRAINT"));
+   }
+
    if (attTup->attidentity)
        ereport(ERROR,
                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
index dda67798cb331814637943b2062771826b481ef8..1bbf59cca023de1d8c6e030944b44a29287521b0 100644 (file)
@@ -1404,6 +1404,10 @@ ALTER TABLE notnull_tbl1 ADD PRIMARY KEY (a);
 ERROR:  cannot create primary key on column "a"
 DETAIL:  The constraint "nn" on column "a" of table "notnull_tbl1", marked NOT VALID, is incompatible with a primary key.
 HINT:  You might need to validate it using ALTER TABLE ... VALIDATE CONSTRAINT.
+-- cannot set column as generated-as-identity if it has an invalid not-null
+ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
+ERROR:  incompatible NOT VALID constraint "nn" on relation "notnull_tbl1"
+HINT:  You might need to validate it using ALTER TABLE ... VALIDATE CONSTRAINT.
 -- ALTER column SET NOT NULL validates an invalid constraint (but this fails
 -- because of rows with null values)
 ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;
index 0a6290bc57167b64877cf50e90bc054e7a777e09..733a1dbccfe30be9da6e6452af5d5db1fb408605 100644 (file)
@@ -832,6 +832,9 @@ ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a;
 -- cannot add primary key on a column with an invalid not-null
 ALTER TABLE notnull_tbl1 ADD PRIMARY KEY (a);
 
+-- cannot set column as generated-as-identity if it has an invalid not-null
+ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
+
 -- ALTER column SET NOT NULL validates an invalid constraint (but this fails
 -- because of rows with null values)
 ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;