From a53bb905eaba292123e1714da167c56b714e199d Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Fri, 9 Oct 2026 00:07:50 -0400 Subject: [PATCH v1 2/3] Keep a child's ENFORCED CHECK when the parent's column type changes ALTER COLUMN TYPE drops an inherited CHECK constraint on a child and lets the parent's definition recreate it. If the parent's constraint is NOT ENFORCED and the child's was ENFORCED, the child silently became NOT ENFORCED. Enforce it again after it has been recreated. --- src/backend/commands/tablecmds.c | 22 ++++++++++++++++++++++ src/test/regress/expected/inherit.out | 20 ++++++++++++++++++++ src/test/regress/sql/inherit.sql | 12 ++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index fdfe4231ead..ccac26592c9 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16154,6 +16154,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) Oid relid; Oid confrelid; bool conislocal; + bool enforced_check; tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId)); if (!HeapTupleIsValid(tup)) /* should not happen */ @@ -16170,6 +16171,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) } confrelid = con->confrelid; conislocal = con->conislocal; + enforced_check = (con->contype == CONSTRAINT_CHECK && con->conenforced); ReleaseSysCache(tup); ObjectAddressSet(obj, ConstraintRelationId, oldId); @@ -16181,9 +16183,29 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) * ATAddCheckNNConstraint recurses from adding the parent table's * constraint. But we had to carry the info this far so that we can * drop the constraint below. + * + * The parent's constraint might be NOT ENFORCED while this one is + * ENFORCED, so enforce it again once all tables' constraints have + * been recreated. */ if (!conislocal) + { + if (enforced_check && relid == tab->relid) + { + AlterTableCmd *cmd = makeNode(AlterTableCmd); + ATAlterConstraint *altercon = makeNode(ATAlterConstraint); + + altercon->conname = get_constraint_name(oldId); + altercon->alterEnforceability = true; + altercon->is_enforced = true; + cmd->subtype = AT_AlterConstraint; + cmd->def = (Node *) altercon; + cmd->recurse = true; + tab->subcmds[AT_PASS_ADD_OTHERCONSTR] = + lappend(tab->subcmds[AT_PASS_ADD_OTHERCONSTR], cmd); + } continue; + } /* * When rebuilding another table's constraint that references the diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 7b30876866c..85e193cb65d 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1499,6 +1499,26 @@ order by conrelid::regclass::text collate "C"; p1_c1 | t | t (2 rows) +drop table p1 cascade; +NOTICE: drop cascades to table p1_c1 +-- ALTER COLUMN TYPE on a parent must not reset a child's constraint that is +-- enforced while the parent's is NOT ENFORCED +create table p1(f1 int constraint inh_check_constraint check (f1 > 0) not enforced); +create table p1_c1() inherits(p1); +alter table p1_c1 alter constraint inh_check_constraint enforced; +alter table p1 alter column f1 type bigint; +insert into p1_c1 values(-1); --error +ERROR: new row for relation "p1_c1" violates check constraint "inh_check_constraint" +DETAIL: Failing row contains (-1). +select conrelid::regclass, conislocal, conenforced, convalidated +from pg_constraint where conname = 'inh_check_constraint' +order by conrelid::regclass::text collate "C"; + conrelid | conislocal | conenforced | convalidated +----------+------------+-------------+-------------- + p1 | t | f | f + p1_c1 | f | t | t +(2 rows) + drop table p1 cascade; NOTICE: drop cascades to table p1_c1 -- an inherited CHECK constraint cannot be NOT ENFORCED under an ENFORCED parent diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 626d2cde378..102cabe58b7 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -550,6 +550,18 @@ from pg_constraint where conname = 'inh_check_constraint' order by conrelid::regclass::text collate "C"; drop table p1 cascade; +-- ALTER COLUMN TYPE on a parent must not reset a child's constraint that is +-- enforced while the parent's is NOT ENFORCED +create table p1(f1 int constraint inh_check_constraint check (f1 > 0) not enforced); +create table p1_c1() inherits(p1); +alter table p1_c1 alter constraint inh_check_constraint enforced; +alter table p1 alter column f1 type bigint; +insert into p1_c1 values(-1); --error +select conrelid::regclass, conislocal, conenforced, convalidated +from pg_constraint where conname = 'inh_check_constraint' +order by conrelid::regclass::text collate "C"; +drop table p1 cascade; + -- an inherited CHECK constraint cannot be NOT ENFORCED under an ENFORCED parent create table p1(f1 int constraint p1_a_check check (f1 > 0) enforced); create table p1_c1() inherits(p1); -- 2.37.1 (Apple Git-137.1)