refactor: Split tryAttachPartitionForeignKey()

Split tryAttachPartitionForeignKey() into three functions:
AttachPartitionForeignKey(), RemoveInheritedConstraint(), and
DropForeignKeyConstraintTriggers(), so they can be reused in some
subsequent patches for the NOT ENFORCED feature.

Author: Amul Sul <amul.sul@enterprisedb.com>
Reviewed-by: Alexandra Wang <alexandra.wang.oss@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CAAJ_b962c5AcYW9KUt_R_ER5qs3fUGbe4az-SP-vuwPS-w-AGA%40mail.gmail.com
pull/207/head
Peter Eisentraut 6 months ago
parent 64224a834c
commit 1d26c2d2c4
  1. 319
      src/backend/commands/tablecmds.c

@ -585,6 +585,14 @@ static bool tryAttachPartitionForeignKey(List **wqueue,
Oid parentInsTrigger, Oid parentInsTrigger,
Oid parentUpdTrigger, Oid parentUpdTrigger,
Relation trigrel); Relation trigrel);
static void AttachPartitionForeignKey(List **wqueue, Relation partition,
Oid partConstrOid, Oid parentConstrOid,
Oid parentInsTrigger, Oid parentUpdTrigger,
Relation trigrel);
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
Oid conoid, Oid conrelid);
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
Oid confrelid, Oid conrelid);
static void GetForeignKeyActionTriggers(Relation trigrel, static void GetForeignKeyActionTriggers(Relation trigrel,
Oid conoid, Oid confrelid, Oid conrelid, Oid conoid, Oid confrelid, Oid conrelid,
Oid *deleteTriggerOid, Oid *deleteTriggerOid,
@ -11467,12 +11475,6 @@ tryAttachPartitionForeignKey(List **wqueue,
Form_pg_constraint parentConstr; Form_pg_constraint parentConstr;
HeapTuple partcontup; HeapTuple partcontup;
Form_pg_constraint partConstr; Form_pg_constraint partConstr;
bool queueValidation;
ScanKeyData key;
SysScanDesc scan;
HeapTuple trigtup;
Oid insertTriggerOid,
updateTriggerOid;
parentConstrTup = SearchSysCache1(CONSTROID, parentConstrTup = SearchSysCache1(CONSTROID,
ObjectIdGetDatum(parentConstrOid)); ObjectIdGetDatum(parentConstrOid));
@ -11517,6 +11519,59 @@ tryAttachPartitionForeignKey(List **wqueue,
return false; return false;
} }
ReleaseSysCache(parentConstrTup);
ReleaseSysCache(partcontup);
/* Looks good! Attach this constraint. */
AttachPartitionForeignKey(wqueue, partition, fk->conoid,
parentConstrOid, parentInsTrigger,
parentUpdTrigger, trigrel);
return true;
}
/*
* AttachPartitionForeignKey
*
* The subroutine for tryAttachPartitionForeignKey performs the final tasks of
* attaching the constraint, removing redundant triggers and entries from
* pg_constraint, and setting the constraint's parent.
*/
static void
AttachPartitionForeignKey(List **wqueue,
Relation partition,
Oid partConstrOid,
Oid parentConstrOid,
Oid parentInsTrigger,
Oid parentUpdTrigger,
Relation trigrel)
{
HeapTuple parentConstrTup;
Form_pg_constraint parentConstr;
HeapTuple partcontup;
Form_pg_constraint partConstr;
bool queueValidation;
Oid partConstrFrelid;
Oid partConstrRelid;
Oid insertTriggerOid,
updateTriggerOid;
/* Fetch the parent constraint tuple */
parentConstrTup = SearchSysCache1(CONSTROID,
ObjectIdGetDatum(parentConstrOid));
if (!HeapTupleIsValid(parentConstrTup))
elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
/* Fetch the child constraint tuple */
partcontup = SearchSysCache1(CONSTROID,
ObjectIdGetDatum(partConstrOid));
if (!HeapTupleIsValid(partcontup))
elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
partConstrFrelid = partConstr->confrelid;
partConstrRelid = partConstr->conrelid;
/* /*
* Will we need to validate this constraint? A valid parent constraint * Will we need to validate this constraint? A valid parent constraint
* implies that all child constraints have been validated, so if this one * implies that all child constraints have been validated, so if this one
@ -11528,50 +11583,15 @@ tryAttachPartitionForeignKey(List **wqueue,
ReleaseSysCache(parentConstrTup); ReleaseSysCache(parentConstrTup);
/* /*
* Looks good! Attach this constraint. The action triggers in the new * The action triggers in the new partition become redundant -- the parent
* partition become redundant -- the parent table already has equivalent * table already has equivalent ones, and those will be able to reach the
* ones, and those will be able to reach the partition. Remove the ones * partition. Remove the ones in the partition. We identify them because
* in the partition. We identify them because they have our constraint * they have our constraint OID, as well as being on the referenced rel.
* OID, as well as being on the referenced rel.
*/ */
ScanKeyInit(&key, DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
Anum_pg_trigger_tgconstraint, partConstrRelid);
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(fk->conoid));
scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
NULL, 1, &key);
while ((trigtup = systable_getnext(scan)) != NULL)
{
Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
ObjectAddress trigger;
if (trgform->tgconstrrelid != fk->conrelid) ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
continue;
if (trgform->tgrelid != fk->confrelid)
continue;
/*
* The constraint is originally set up to contain this trigger as an
* implementation object, so there's a dependency record that links
* the two; however, since the trigger is no longer needed, we remove
* the dependency link in order to be able to drop the trigger while
* keeping the constraint intact.
*/
deleteDependencyRecordsFor(TriggerRelationId,
trgform->oid,
false);
/* make dependency deletion visible to performDeletion */
CommandCounterIncrement();
ObjectAddressSet(trigger, TriggerRelationId,
trgform->oid);
performDeletion(&trigger, DROP_RESTRICT, 0);
/* make trigger drop visible, in case the loop iterates */
CommandCounterIncrement();
}
systable_endscan(scan);
ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
RelationGetRelid(partition)); RelationGetRelid(partition));
/* /*
@ -11579,7 +11599,7 @@ tryAttachPartitionForeignKey(List **wqueue,
* corresponding parent triggers. * corresponding parent triggers.
*/ */
GetForeignKeyCheckTriggers(trigrel, GetForeignKeyCheckTriggers(trigrel,
fk->conoid, fk->confrelid, fk->conrelid, partConstrOid, partConstrFrelid, partConstrRelid,
&insertTriggerOid, &updateTriggerOid); &insertTriggerOid, &updateTriggerOid);
Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger)); Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger, TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
@ -11593,72 +11613,12 @@ tryAttachPartitionForeignKey(List **wqueue,
* attaching now has extra pg_constraint rows and action triggers that are * attaching now has extra pg_constraint rows and action triggers that are
* no longer needed. Remove those. * no longer needed. Remove those.
*/ */
if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE) if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
{ {
Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock); Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
ObjectAddresses *objs;
HeapTuple consttup;
ScanKeyInit(&key,
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(fk->conrelid));
scan = systable_beginscan(pg_constraint,
ConstraintRelidTypidNameIndexId,
true, NULL, 1, &key);
objs = new_object_addresses();
while ((consttup = systable_getnext(scan)) != NULL)
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
if (conform->conparentid != fk->conoid)
continue;
else
{
ObjectAddress addr;
SysScanDesc scan2;
ScanKeyData key2;
int n PG_USED_FOR_ASSERTS_ONLY;
ObjectAddressSet(addr, ConstraintRelationId, conform->oid); RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
add_exact_object_address(&addr, objs); partConstrRelid);
/*
* First we must delete the dependency record that binds the
* constraint records together.
*/
n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
conform->oid,
DEPENDENCY_INTERNAL,
ConstraintRelationId,
fk->conoid);
Assert(n == 1); /* actually only one is expected */
/*
* Now search for the triggers for this constraint and set
* them up for deletion too
*/
ScanKeyInit(&key2,
Anum_pg_trigger_tgconstraint,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conform->oid));
scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
true, NULL, 1, &key2);
while ((trigtup = systable_getnext(scan2)) != NULL)
{
ObjectAddressSet(addr, TriggerRelationId,
((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
add_exact_object_address(&addr, objs);
}
systable_endscan(scan2);
}
}
/* make the dependency deletions visible */
CommandCounterIncrement();
performMultipleDeletions(objs, DROP_RESTRICT,
PERFORM_DELETION_INTERNAL);
systable_endscan(scan);
table_close(pg_constraint, RowShareLock); table_close(pg_constraint, RowShareLock);
} }
@ -11679,9 +11639,10 @@ tryAttachPartitionForeignKey(List **wqueue,
Relation conrel; Relation conrel;
conrel = table_open(ConstraintRelationId, RowExclusiveLock); conrel = table_open(ConstraintRelationId, RowExclusiveLock);
partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
if (!HeapTupleIsValid(partcontup)) if (!HeapTupleIsValid(partcontup))
elog(ERROR, "cache lookup failed for constraint %u", fk->conoid); elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
/* Use the same lock as for AT_ValidateConstraint */ /* Use the same lock as for AT_ValidateConstraint */
QueueFKConstraintValidation(wqueue, conrel, partition, partcontup, QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
@ -11689,8 +11650,136 @@ tryAttachPartitionForeignKey(List **wqueue,
ReleaseSysCache(partcontup); ReleaseSysCache(partcontup);
table_close(conrel, RowExclusiveLock); table_close(conrel, RowExclusiveLock);
} }
}
return true; /*
* RemoveInheritedConstraint
*
* Removes the constraint and its associated trigger from the specified
* relation, which inherited the given constraint.
*/
static void
RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
Oid conrelid)
{
ObjectAddresses *objs;
HeapTuple consttup;
ScanKeyData key;
SysScanDesc scan;
HeapTuple trigtup;
ScanKeyInit(&key,
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conrelid));
scan = systable_beginscan(conrel,
ConstraintRelidTypidNameIndexId,
true, NULL, 1, &key);
objs = new_object_addresses();
while ((consttup = systable_getnext(scan)) != NULL)
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
if (conform->conparentid != conoid)
continue;
else
{
ObjectAddress addr;
SysScanDesc scan2;
ScanKeyData key2;
int n PG_USED_FOR_ASSERTS_ONLY;
ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
add_exact_object_address(&addr, objs);
/*
* First we must delete the dependency record that binds the
* constraint records together.
*/
n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
conform->oid,
DEPENDENCY_INTERNAL,
ConstraintRelationId,
conoid);
Assert(n == 1); /* actually only one is expected */
/*
* Now search for the triggers for this constraint and set them up
* for deletion too
*/
ScanKeyInit(&key2,
Anum_pg_trigger_tgconstraint,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conform->oid));
scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
true, NULL, 1, &key2);
while ((trigtup = systable_getnext(scan2)) != NULL)
{
ObjectAddressSet(addr, TriggerRelationId,
((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
add_exact_object_address(&addr, objs);
}
systable_endscan(scan2);
}
}
/* make the dependency deletions visible */
CommandCounterIncrement();
performMultipleDeletions(objs, DROP_RESTRICT,
PERFORM_DELETION_INTERNAL);
systable_endscan(scan);
}
/*
* DropForeignKeyConstraintTriggers
*
* The subroutine for tryAttachPartitionForeignKey handles the deletion of
* action triggers for the foreign key constraint.
*/
static void
DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
Oid conrelid)
{
ScanKeyData key;
SysScanDesc scan;
HeapTuple trigtup;
ScanKeyInit(&key,
Anum_pg_trigger_tgconstraint,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conoid));
scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
NULL, 1, &key);
while ((trigtup = systable_getnext(scan)) != NULL)
{
Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
ObjectAddress trigger;
if (trgform->tgconstrrelid != conrelid)
continue;
if (trgform->tgrelid != confrelid)
continue;
/*
* The constraint is originally set up to contain this trigger as an
* implementation object, so there's a dependency record that links
* the two; however, since the trigger is no longer needed, we remove
* the dependency link in order to be able to drop the trigger while
* keeping the constraint intact.
*/
deleteDependencyRecordsFor(TriggerRelationId,
trgform->oid,
false);
/* make dependency deletion visible to performDeletion */
CommandCounterIncrement();
ObjectAddressSet(trigger, TriggerRelationId,
trgform->oid);
performDeletion(&trigger, DROP_RESTRICT, 0);
/* make trigger drop visible, in case the loop iterates */
CommandCounterIncrement();
}
systable_endscan(scan);
} }
/* /*

Loading…
Cancel
Save