From d864e87864dce0e4e3f490d9f1ef1e016015a95d Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Fri, 9 Oct 2026 00:07:50 -0400 Subject: [PATCH v2 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 a69b21e8f58..175085b2009 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16157,6 +16157,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 */ @@ -16173,6 +16174,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); @@ -16184,9 +16186,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 97a8facac4d..6cd219cc918 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1498,6 +1498,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 adbbbe10b9d..938074b290e 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -549,6 +549,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)