Skip to content

Commit 8c8fb5c

Browse files
EDB-AmulSulCommitfest Bot
authored and
Commitfest Bot
committed
Merge the parent and child constraints with differing enforcibility.
If an ENFORCED parent constraint is attached to a NOT ENFORCED child constraint, the child constraint will be made ENFORCED, with validation applied if the parent constraint is validated as well. Otherwise, a new ENFORCED constraint (with validation, if the parent constraint is validated) would need to be created on the child table, which would be unnecessary if a similar constraint already exists and can be attached. On the other hand, having a NOT ENFORCED parent constraint with an ENFORCED child constraint does not cause any issues, and no changes are required. ---- NOTE: This patch is intended to reduce the diff noise from the main patch and is not meant to be committed separately. It should be squashed with the main patch that adds ENFORCED/NOT ENFORCED. ----
1 parent 60a3e9e commit 8c8fb5c

File tree

3 files changed

+248
-94
lines changed

3 files changed

+248
-94
lines changed

src/backend/commands/tablecmds.c

+155-25
Original file line numberDiff line numberDiff line change
@@ -11589,23 +11589,6 @@ tryAttachPartitionForeignKey(List **wqueue,
1158911589
if (!HeapTupleIsValid(partcontup))
1159011590
elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
1159111591
partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11592-
11593-
/*
11594-
* An error should be raised if the constraint enforceability is different.
11595-
* Returning false without raising an error, as we do for other attributes,
11596-
* could lead to a duplicate constraint with the same enforceability as the
11597-
* parent. While this may be acceptable, it may not be ideal. Therefore,
11598-
* it's better to raise an error and allow the user to correct the
11599-
* enforceability before proceeding.
11600-
*/
11601-
if (partConstr->conenforced != parentConstr->conenforced)
11602-
ereport(ERROR,
11603-
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11604-
errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11605-
NameStr(parentConstr->conname),
11606-
NameStr(partConstr->conname),
11607-
RelationGetRelationName(partition))));
11608-
1160911592
if (OidIsValid(partConstr->conparentid) ||
1161011593
partConstr->condeferrable != parentConstr->condeferrable ||
1161111594
partConstr->condeferred != parentConstr->condeferred ||
@@ -11653,6 +11636,8 @@ AttachPartitionForeignKey(List **wqueue,
1165311636
Oid partConstrFrelid;
1165411637
Oid partConstrRelid;
1165511638
bool parentConstrIsEnforced;
11639+
bool partConstrIsEnforced;
11640+
bool partConstrParentIsSet;
1165611641

1165711642
/* Fetch the parent constraint tuple */
1165811643
parentConstrTup = SearchSysCache1(CONSTROID,
@@ -11668,13 +11653,47 @@ AttachPartitionForeignKey(List **wqueue,
1166811653
if (!HeapTupleIsValid(partcontup))
1166911654
elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
1167011655
partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11656+
partConstrIsEnforced = partConstr->conenforced;
1167111657
partConstrFrelid = partConstr->confrelid;
1167211658
partConstrRelid = partConstr->conrelid;
1167311659

11660+
/*
11661+
* The case where the parent constraint is NOT ENFORCED and the child
11662+
* constraint is ENFORCED is acceptable because the not enforced parent
11663+
* constraint lacks triggers, eliminating any redundancy issues with the
11664+
* enforced child constraint. In this scenario, the child constraint
11665+
* remains enforced, and its trigger is retained, ensuring that
11666+
* referential integrity checks for the child continue as before, even
11667+
* with the parent constraint not enforced. The relationship between the
11668+
* two constraints is preserved by setting the parent constraint, which
11669+
* allows us to locate the child constraint. This becomes important if the
11670+
* parent constraint is later changed to enforced, at which point the
11671+
* necessary trigger will be created for the parent, and any redundancy
11672+
* from these triggers will be appropriately handled.
11673+
*/
11674+
if (!parentConstrIsEnforced && partConstrIsEnforced)
11675+
{
11676+
ReleaseSysCache(partcontup);
11677+
ReleaseSysCache(parentConstrTup);
11678+
11679+
ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11680+
RelationGetRelid(partition));
11681+
CommandCounterIncrement();
11682+
11683+
return;
11684+
}
11685+
1167411686
/*
1167511687
* If the referenced table is partitioned, then the partition we're
1167611688
* attaching now has extra pg_constraint rows and action triggers that are
1167711689
* no longer needed. Remove those.
11690+
*
11691+
* Note that this must be done beforehand, particularly in situations
11692+
* where we might decide to change the constraint to an ENFORCED state
11693+
* which will create the required triggers and add the child constraint to
11694+
* the validation queue. To avoid generating unnecessary triggers and
11695+
* adding them to the validation queue, it is crucial to eliminate any
11696+
* redundant constraints beforehand.
1167811697
*/
1167911698
if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
1168011699
{
@@ -11693,6 +11712,53 @@ AttachPartitionForeignKey(List **wqueue,
1169311712
*/
1169411713
queueValidation = parentConstr->convalidated && !partConstr->convalidated;
1169511714

11715+
/*
11716+
* The case where the parent constraint is ENFORCED and the child
11717+
* constraint is NOT ENFORCED is not acceptable, as it would violate
11718+
* referential integrity. In such cases, the child constraint will first
11719+
* be enforced before merging it with the enforced parent constraint.
11720+
* Subsequently, removing action triggers, setting up constraint triggers,
11721+
* and handling check triggers for the parent will be managed in the usual
11722+
* manner, similar to how two enforced constraints are merged.
11723+
*/
11724+
if (parentConstrIsEnforced && !partConstrIsEnforced)
11725+
{
11726+
ATAlterConstraint *cmdcon = makeNode(ATAlterConstraint);
11727+
Relation conrel;
11728+
11729+
cmdcon->conname = NameStr(partConstr->conname);
11730+
cmdcon->deferrable = partConstr->condeferrable;
11731+
cmdcon->initdeferred = partConstr->condeferred;
11732+
cmdcon->alterEnforceability = true;
11733+
cmdcon->is_enforced = true;
11734+
11735+
conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11736+
11737+
ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, trigrel,
11738+
partConstr->conrelid,
11739+
partConstr->confrelid,
11740+
partcontup, AccessExclusiveLock,
11741+
InvalidOid, InvalidOid, InvalidOid,
11742+
InvalidOid);
11743+
11744+
table_close(conrel, RowExclusiveLock);
11745+
11746+
CommandCounterIncrement();
11747+
11748+
/*
11749+
* No further validation is needed, as changing the constraint to
11750+
* enforced will implicitly trigger the same validation.
11751+
*/
11752+
queueValidation = false;
11753+
}
11754+
11755+
/*
11756+
* The constraint parent shouldn't be set beforehand, or if it's already
11757+
* set, it should be the specified parent.
11758+
*/
11759+
partConstrParentIsSet = OidIsValid(partConstr->conparentid);
11760+
Assert(!partConstrParentIsSet || partConstr->conparentid == parentConstrOid);
11761+
1169611762
ReleaseSysCache(partcontup);
1169711763
ReleaseSysCache(parentConstrTup);
1169811764

@@ -11705,8 +11771,10 @@ AttachPartitionForeignKey(List **wqueue,
1170511771
DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
1170611772
partConstrRelid);
1170711773

11708-
ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11709-
RelationGetRelid(partition));
11774+
/* Skip if the parent is already set */
11775+
if (!partConstrParentIsSet)
11776+
ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11777+
RelationGetRelid(partition));
1171011778

1171111779
/*
1171211780
* Like the constraint, attach partition's "check" triggers to the
@@ -12306,6 +12374,17 @@ ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
1230612374

1230712375
/* Drop all the triggers */
1230812376
DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12377+
12378+
/*
12379+
* If the referenced table is partitioned, the child constraint we're
12380+
* changing to NOT ENFORCED may have additional pg_constraint rows and
12381+
* action triggers that remain untouched while this child constraint
12382+
* is attached to the NOT ENFORCED parent. These must now be removed.
12383+
* For more details, see AttachPartitionForeignKey().
12384+
*/
12385+
if (OidIsValid(currcon->conparentid) &&
12386+
get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12387+
RemoveInheritedConstraint(conrel, tgrel, currcon->conrelid, conoid);
1230912388
}
1231012389
else if (changed) /* Create triggers */
1231112390
{
@@ -12631,13 +12710,40 @@ AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
1263112710
true, NULL, 1, &pkey);
1263212711

1263312712
while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12634-
ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12635-
pkrelid, childtup, lockmode,
12636-
ReferencedParentDelTrigger,
12637-
ReferencedParentUpdTrigger,
12638-
ReferencingParentInsTrigger,
12639-
ReferencingParentUpdTrigger);
12713+
{
12714+
Form_pg_constraint childcon;
12715+
12716+
childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12717+
12718+
/*
12719+
* When the parent constraint is modified to be ENFORCED, and the
12720+
* child constraint is attached to the parent constraint (which is
12721+
* already ENFORCED), some constraints and action triggers on the
12722+
* child table may become redundant and need to be removed.
12723+
*/
12724+
if (cmdcon->is_enforced && childcon->conenforced)
12725+
{
12726+
if (currcon->confrelid == pkrelid)
12727+
{
12728+
Relation rel = table_open(childcon->conrelid, lockmode);
1264012729

12730+
AttachPartitionForeignKey(wqueue, rel, childcon->oid,
12731+
conoid,
12732+
ReferencingParentInsTrigger,
12733+
ReferencingParentUpdTrigger,
12734+
tgrel);
12735+
12736+
table_close(rel, NoLock);
12737+
}
12738+
}
12739+
else
12740+
ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12741+
pkrelid, childtup, lockmode,
12742+
ReferencedParentDelTrigger,
12743+
ReferencedParentUpdTrigger,
12744+
ReferencingParentInsTrigger,
12745+
ReferencingParentUpdTrigger);
12746+
}
1264112747
systable_endscan(pscan);
1264212748
}
1264312749

@@ -20811,7 +20917,9 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
2081120917
{
2081220918
ForeignKeyCacheInfo *fk = lfirst(cell);
2081320919
HeapTuple contup;
20920+
HeapTuple parentContup;
2081420921
Form_pg_constraint conform;
20922+
Oid parentConstrIsEnforced;
2081520923

2081620924
contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
2081720925
if (!HeapTupleIsValid(contup))
@@ -20830,12 +20938,34 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
2083020938
continue;
2083120939
}
2083220940

20941+
/* Get the enforcibility of the parent constraint */
20942+
parentContup = SearchSysCache1(CONSTROID,
20943+
ObjectIdGetDatum(conform->conparentid));
20944+
if (!HeapTupleIsValid(parentContup))
20945+
elog(ERROR, "cache lookup failed for constraint %u",
20946+
conform->conparentid);
20947+
parentConstrIsEnforced =
20948+
((Form_pg_constraint) GETSTRUCT(parentContup))->conenforced;
20949+
ReleaseSysCache(parentContup);
20950+
2083320951
/*
2083420952
* The constraint on this table must be marked no longer a child of
2083520953
* the parent's constraint, as do its check triggers.
2083620954
*/
2083720955
ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
2083820956

20957+
/*
20958+
* Unsetting the parent is sufficient when the parent constraint is
20959+
* NOT ENFORCED and the child constraint is ENFORCED, as we link them
20960+
* by setting the constraint parent, while leaving the rest unchanged.
20961+
* For more details, see AttachPartitionForeignKey().
20962+
*/
20963+
if (!parentConstrIsEnforced && fk->conenforced)
20964+
{
20965+
ReleaseSysCache(contup);
20966+
continue;
20967+
}
20968+
2083920969
/*
2084020970
* Also, look up the partition's "check" triggers corresponding to the
2084120971
* ENFORCED constraint being detached and detach them from the parent

0 commit comments

Comments
 (0)