Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+22-1
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

+100-15
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

+28
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

+2
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)