fix NOT VALID NOT NULL with ALTER COLUMN SET IDENTITY
hi.
The attached patch makes the last two statements below fail.
CREATE TABLE notnull_tbl1 (a int, b int);
INSERT INTO notnull_tbl1 VALUES (NULL, 1), (300, 3);
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID; -- ok
ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS
IDENTITY; --error
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID,
ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; --error
but in another case,
CREATE TABLE notnull_tbl1 (a int, b int);
INSERT INTO notnull_tbl1 VALUES (NULL, 1), (300, 3);
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID;
I am not so sure the below two statements should error out or not?
ALTER TABLE notnull_tbl1 ADD COLUMN c int GENERATED BY DEFAULT AS
IDENTITY, ADD CONSTRAINT nn NOT NULL c NOT VALID;
ALTER TABLE notnull_tbl1 ADD COLUMN d serial, ADD CONSTRAINT nn NOT
NULL c NOT VALID;
Attachments:
v1-0001-fix-NOT-VALID-NOT-NULL-CONSTRAINT-with-identity-column.patchtext/x-patch; charset=US-ASCII; name=v1-0001-fix-NOT-VALID-NOT-NULL-CONSTRAINT-with-identity-column.patchDownload
From 32dc89c57e47a89d8a0719a690f70a089d738925 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 3 Sep 2025 16:27:24 +0800
Subject: [PATCH v1 1/1] fix NOT VALID NOT NULL CONSTRAINT with identity column
fix the below problem:
CREATE TABLE notnull_tbl1 (a int, b int);
INSERT INTO notnull_tbl1 VALUES (NULL, 1), (300, 3);
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID; -- ok
ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; --expect error
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID,
ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; --expect error
---
src/backend/commands/tablecmds.c | 20 ++++++++++++++++++++
src/test/regress/expected/constraints.out | 11 +++++++++++
src/test/regress/sql/constraints.sql | 6 ++++++
3 files changed, 37 insertions(+)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..b9f1ae6788f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8277,6 +8277,26 @@ ATExecAddIdentity(Relation rel, const char *colName,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
colName, RelationGetRelationName(rel))));
+ else
+ {
+ 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,
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 3590d3274f0..af448eb9b3b 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -1404,6 +1404,17 @@ 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 set identity with 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 TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID, 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.
+-- cannot set column as serial with a no inherit not-null
+ALTER TABLE notnull_tbl1 ADD COLUMN d serial, ADD CONSTRAINT nn NOT NULL d NO INHERIT;
+ERROR: cannot change NO INHERIT status of NOT NULL constraint "notnull_tbl1_d_not_null" on relation "notnull_tbl1"
+HINT: You might need to make the existing constraint inheritable using ALTER TABLE ... ALTER CONSTRAINT ... INHERIT.
-- 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;
diff --git a/src/test/regress/sql/constraints.sql b/src/test/regress/sql/constraints.sql
index 1f6dc8fd69f..d31dae0b68e 100644
--- a/src/test/regress/sql/constraints.sql
+++ b/src/test/regress/sql/constraints.sql
@@ -831,6 +831,12 @@ 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 set identity with an invalid not-null
+ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID, ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
+
+-- cannot set column as serial with a no inherit not-null
+ALTER TABLE notnull_tbl1 ADD COLUMN d serial, ADD CONSTRAINT nn NOT NULL d NO INHERIT;
-- ALTER column SET NOT NULL validates an invalid constraint (but this fails
-- because of rows with null values)
--
2.34.1
On 2025-Sep-03, jian he wrote:
hi.
The attached patch makes the last two statements below fail.
CREATE TABLE notnull_tbl1 (a int, b int);
INSERT INTO notnull_tbl1 VALUES (NULL, 1), (300, 3);
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID; -- ok
ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS
IDENTITY; --error
Yeah, I think an error here is correct.
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID,
ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; --error
I don't understand the difference between this test case and the
previous one ... I mean, by this point, the constraint already has a
not-null constraint, so asking to add another one does nothing.
but in another case,
CREATE TABLE notnull_tbl1 (a int, b int);
INSERT INTO notnull_tbl1 VALUES (NULL, 1), (300, 3);
ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a NOT VALID;I am not so sure the below two statements should error out or not?
ALTER TABLE notnull_tbl1 ADD COLUMN c int GENERATED BY DEFAULT AS
IDENTITY, ADD CONSTRAINT nn NOT NULL c NOT VALID;
ALTER TABLE notnull_tbl1 ADD COLUMN d serial, ADD CONSTRAINT nn NOT NULL c NOT VALID;
Hmm. Here we add the column as identity or serial, which marks it as
not-null, and try to add a not-valid constraint to it on top. This
results in a valid constraint, so I think it's okay. (A new column
cannot have existing violating data.)
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
Pushed now to 18 and master. Thanks.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
"Once again, thank you and all of the developers for your hard work on
PostgreSQL. This is by far the most pleasant management experience of
any database I've worked on." (Dan Harris)
http://archives.postgresql.org/pgsql-performance/2006-04/msg00247.php