Skip to content

Commit f4e53e1

Browse files
committed
Add ALTER TABLE ... ALTER CONSTRAINT ... SET [NO] INHERIT
This allows to redefine an existing non-inheritable constraint to be inheritable, which allows to straighten up situations with NO INHERIT constraints so that thay can become normal constraints without having to re-verify existing data. For existing inheritance children this may require creating additional constraints, if they don't exist already. It also allows to do the opposite, if only for symmetry. Author: Suraj Kharage <suraj.kharage@enterprisedb.com> Reviewed-by: jian he <jian.universality@gmail.com> Discussion: https://postgr.es/m/CAF1DzPVfOW6Kk=7SSh7LbneQDJWh=PbJrEC_Wkzc24tHOyQWGg@mail.gmail.com
1 parent f4694e0 commit f4e53e1

File tree

6 files changed

+359
-19
lines changed

6 files changed

+359
-19
lines changed

doc/src/sgml/ref/alter_table.sgml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
5959
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
6060
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
6161
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
62+
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> SET [ INHERIT | NO INHERIT ]
6263
VALIDATE CONSTRAINT <replaceable class="parameter">constraint_name</replaceable>
6364
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="parameter">constraint_name</replaceable> [ RESTRICT | CASCADE ]
6465
DISABLE TRIGGER [ <replaceable class="parameter">trigger_name</replaceable> | ALL | USER ]
@@ -556,11 +557,31 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
556557
<listitem>
557558
<para>
558559
This form alters the attributes of a constraint that was previously
559-
created. Currently only foreign key constraints may be altered.
560+
created. Currently only foreign key constraints may be altered in
561+
this fashion, but see below.
560562
</para>
561563
</listitem>
562564
</varlistentry>
563565

566+
<varlistentry id="sql-altertable-desc-alter-constraint-inherit">
567+
<term><literal>ALTER CONSTRAINT ... SET INHERIT</literal></term>
568+
<term><literal>ALTER CONSTRAINT ... SET NO INHERIT</literal></term>
569+
<listitem>
570+
<para>
571+
These forms modify a inheritable constraint so that it becomes not
572+
inheritable, or vice-versa. Only not-null constraints may be altered
573+
in this fashion at present.
574+
In addition to changing the inheritability status of the constraint,
575+
in the case where a non-inheritable constraint is being marked
576+
inheritable, if the table has children, an equivalent constraint
577+
will be added to them. If marking an inheritable constraint as
578+
non-inheritable on a table with children, then the corresponding
579+
constraint on children will be marked as no longer inherited,
580+
but not removed.
581+
</para>
582+
</listitem>
583+
</varlistentry>
584+
564585
<varlistentry id="sql-altertable-desc-validate-constraint">
565586
<term><literal>VALIDATE CONSTRAINT</literal></term>
566587
<listitem>

src/backend/commands/tablecmds.c

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,10 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
389389
static void AlterSeqNamespaces(Relation classRel, Relation rel,
390390
Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391391
LOCKMODE lockmode);
392-
static ObjectAddress ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon,
392+
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
393+
ATAlterConstraint *cmdcon,
393394
bool recurse, LOCKMODE lockmode);
394-
static bool ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
395+
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
395396
Relation tgrel, Relation rel, HeapTuple contuple,
396397
bool recurse, List **otherrelids, LOCKMODE lockmode);
397398
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
@@ -5437,8 +5438,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
54375438
lockmode);
54385439
break;
54395440
case AT_AlterConstraint: /* ALTER CONSTRAINT */
5440-
address = ATExecAlterConstraint(rel, castNode(ATAlterConstraint,
5441-
cmd->def),
5441+
address = ATExecAlterConstraint(wqueue, rel,
5442+
castNode(ATAlterConstraint, cmd->def),
54425443
cmd->recurse, lockmode);
54435444
break;
54445445
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
@@ -11813,14 +11814,14 @@ GetForeignKeyCheckTriggers(Relation trigrel,
1181311814
*
1181411815
* Update the attributes of a constraint.
1181511816
*
11816-
* Currently only works for Foreign Key constraints.
11817+
* Currently only works for Foreign Key and not null constraints.
1181711818
*
1181811819
* If the constraint is modified, returns its address; otherwise, return
1181911820
* InvalidObjectAddress.
1182011821
*/
1182111822
static ObjectAddress
11822-
ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
11823-
LOCKMODE lockmode)
11823+
ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
11824+
bool recurse, LOCKMODE lockmode)
1182411825
{
1182511826
Relation conrel;
1182611827
Relation tgrel;
@@ -11871,11 +11872,26 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
1187111872
cmdcon->conname, RelationGetRelationName(rel))));
1187211873

1187311874
currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11874-
if (currcon->contype != CONSTRAINT_FOREIGN)
11875+
if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
1187511876
ereport(ERROR,
1187611877
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1187711878
errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
1187811879
cmdcon->conname, RelationGetRelationName(rel))));
11880+
if (cmdcon->alterInheritability &&
11881+
currcon->contype != CONSTRAINT_NOTNULL)
11882+
ereport(ERROR,
11883+
errcode(ERRCODE_WRONG_OBJECT_TYPE),
11884+
errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
11885+
cmdcon->conname, RelationGetRelationName(rel)));
11886+
11887+
/* Refuse to modify inheritability of inherited constraints */
11888+
if (cmdcon->alterInheritability &&
11889+
cmdcon->noinherit && currcon->coninhcount > 0)
11890+
ereport(ERROR,
11891+
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11892+
errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
11893+
NameStr(currcon->conname),
11894+
RelationGetRelationName(rel)));
1187911895

1188011896
/*
1188111897
* If it's not the topmost constraint, raise an error.
@@ -11926,8 +11942,8 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
1192611942
/*
1192711943
* Do the actual catalog work, and recurse if necessary.
1192811944
*/
11929-
if (ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, rel, contuple,
11930-
recurse, &otherrelids, lockmode))
11945+
if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
11946+
contuple, recurse, &otherrelids, lockmode))
1193111947
ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
1193211948

1193311949
/*
@@ -11958,9 +11974,10 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
1195811974
* but existing releases don't do that.)
1195911975
*/
1196011976
static bool
11961-
ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
11962-
Relation tgrel, Relation rel, HeapTuple contuple,
11963-
bool recurse, List **otherrelids, LOCKMODE lockmode)
11977+
ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
11978+
Relation conrel, Relation tgrel, Relation rel,
11979+
HeapTuple contuple, bool recurse,
11980+
List **otherrelids, LOCKMODE lockmode)
1196411981
{
1196511982
Form_pg_constraint currcon;
1196611983
Oid refrelid = InvalidOid;
@@ -12040,14 +12057,82 @@ ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
1204012057
Relation childrel;
1204112058

1204212059
childrel = table_open(childcon->conrelid, lockmode);
12043-
ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, childrel, childtup,
12044-
recurse, otherrelids, lockmode);
12060+
ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, childrel,
12061+
childtup, recurse, otherrelids, lockmode);
1204512062
table_close(childrel, NoLock);
1204612063
}
1204712064

1204812065
systable_endscan(pscan);
1204912066
}
1205012067

12068+
/*
12069+
* Update the catalog for inheritability. No work if the constraint is
12070+
* already in the requested state.
12071+
*/
12072+
if (cmdcon->alterInheritability &&
12073+
(cmdcon->noinherit != currcon->connoinherit))
12074+
{
12075+
AttrNumber colNum;
12076+
char *colName;
12077+
List *children;
12078+
HeapTuple copyTuple;
12079+
Form_pg_constraint copy_con;
12080+
12081+
/* The current implementation only works for NOT NULL constraints */
12082+
Assert(currcon->contype == CONSTRAINT_NOTNULL);
12083+
12084+
copyTuple = heap_copytuple(contuple);
12085+
copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12086+
copy_con->connoinherit = cmdcon->noinherit;
12087+
12088+
CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12089+
CommandCounterIncrement();
12090+
heap_freetuple(copyTuple);
12091+
changed = true;
12092+
12093+
/* Fetch the column number and name */
12094+
colNum = extractNotNullColumn(contuple);
12095+
colName = get_attname(currcon->conrelid, colNum, false);
12096+
12097+
/*
12098+
* Propagate the change to children. For SET NO INHERIT, we don't
12099+
* recursively affect children, just the immediate level.
12100+
*/
12101+
children = find_inheritance_children(RelationGetRelid(rel),
12102+
lockmode);
12103+
foreach_oid(childoid, children)
12104+
{
12105+
ObjectAddress addr;
12106+
12107+
if (cmdcon->noinherit)
12108+
{
12109+
HeapTuple childtup;
12110+
Form_pg_constraint childcon;
12111+
12112+
childtup = findNotNullConstraint(childoid, colName);
12113+
if (!childtup)
12114+
elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12115+
colName, childoid);
12116+
childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12117+
Assert(childcon->coninhcount > 0);
12118+
childcon->coninhcount--;
12119+
childcon->conislocal = true;
12120+
CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12121+
heap_freetuple(childtup);
12122+
}
12123+
else
12124+
{
12125+
Relation childrel = table_open(childoid, NoLock);
12126+
12127+
addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12128+
colName, true, true, lockmode);
12129+
if (OidIsValid(addr.objectId))
12130+
CommandCounterIncrement();
12131+
table_close(childrel, NoLock);
12132+
}
12133+
}
12134+
}
12135+
1205112136
return changed;
1205212137
}
1205312138

src/backend/parser/gram.y

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,6 +2669,34 @@ alter_table_cmd:
26692669
NULL, NULL, NULL, yyscanner);
26702670
$$ = (Node *) n;
26712671
}
2672+
/* ALTER TABLE <name> ALTER CONSTRAINT SET INHERIT */
2673+
| ALTER CONSTRAINT name SET INHERIT
2674+
{
2675+
AlterTableCmd *n = makeNode(AlterTableCmd);
2676+
ATAlterConstraint *c = makeNode(ATAlterConstraint);
2677+
2678+
n->subtype = AT_AlterConstraint;
2679+
n->def = (Node *) c;
2680+
c->conname = $3;
2681+
c->alterInheritability = true;
2682+
c->noinherit = false;
2683+
2684+
$$ = (Node *) n;
2685+
}
2686+
/* ALTER TABLE <name> ALTER CONSTRAINT SET NO INHERIT */
2687+
| ALTER CONSTRAINT name SET NO INHERIT
2688+
{
2689+
AlterTableCmd *n = makeNode(AlterTableCmd);
2690+
ATAlterConstraint *c = makeNode(ATAlterConstraint);
2691+
2692+
n->subtype = AT_AlterConstraint;
2693+
n->def = (Node *) c;
2694+
c->conname = $3;
2695+
c->alterInheritability = true;
2696+
c->noinherit = true;
2697+
2698+
$$ = (Node *) n;
2699+
}
26722700
/* ALTER TABLE <name> VALIDATE CONSTRAINT ... */
26732701
| VALIDATE CONSTRAINT name
26742702
{

src/include/nodes/parsenodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,8 @@ typedef struct ATAlterConstraint
24932493
bool alterDeferrability; /* changing deferrability properties? */
24942494
bool deferrable; /* DEFERRABLE? */
24952495
bool initdeferred; /* INITIALLY DEFERRED? */
2496+
bool alterInheritability; /* changing inheritability properties */
2497+
bool noinherit;
24962498
} ATAlterConstraint;
24972499

24982500
/* Ad-hoc node for AT_ReplicaIdentity */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy