@@ -585,6 +585,14 @@ static bool tryAttachPartitionForeignKey(List **wqueue,
585585 Oid parentInsTrigger,
586586 Oid parentUpdTrigger,
587587 Relation trigrel);
588+ static void AttachPartitionForeignKey(List **wqueue, Relation partition,
589+ Oid partConstrOid, Oid parentConstrOid,
590+ Oid parentInsTrigger, Oid parentUpdTrigger,
591+ Relation trigrel);
592+ static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
593+ Oid conoid, Oid conrelid);
594+ static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
595+ Oid confrelid, Oid conrelid);
588596static void GetForeignKeyActionTriggers(Relation trigrel,
589597 Oid conoid, Oid confrelid, Oid conrelid,
590598 Oid *deleteTriggerOid,
@@ -11467,12 +11475,6 @@ tryAttachPartitionForeignKey(List **wqueue,
1146711475 Form_pg_constraint parentConstr;
1146811476 HeapTuple partcontup;
1146911477 Form_pg_constraint partConstr;
11470- bool queueValidation;
11471- ScanKeyData key;
11472- SysScanDesc scan;
11473- HeapTuple trigtup;
11474- Oid insertTriggerOid,
11475- updateTriggerOid;
1147611478
1147711479 parentConstrTup = SearchSysCache1(CONSTROID,
1147811480 ObjectIdGetDatum(parentConstrOid));
@@ -11517,6 +11519,59 @@ tryAttachPartitionForeignKey(List **wqueue,
1151711519 return false;
1151811520 }
1151911521
11522+ ReleaseSysCache(parentConstrTup);
11523+ ReleaseSysCache(partcontup);
11524+
11525+ /* Looks good! Attach this constraint. */
11526+ AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11527+ parentConstrOid, parentInsTrigger,
11528+ parentUpdTrigger, trigrel);
11529+
11530+ return true;
11531+ }
11532+
11533+ /*
11534+ * AttachPartitionForeignKey
11535+ *
11536+ * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11537+ * attaching the constraint, removing redundant triggers and entries from
11538+ * pg_constraint, and setting the constraint's parent.
11539+ */
11540+ static void
11541+ AttachPartitionForeignKey(List **wqueue,
11542+ Relation partition,
11543+ Oid partConstrOid,
11544+ Oid parentConstrOid,
11545+ Oid parentInsTrigger,
11546+ Oid parentUpdTrigger,
11547+ Relation trigrel)
11548+ {
11549+ HeapTuple parentConstrTup;
11550+ Form_pg_constraint parentConstr;
11551+ HeapTuple partcontup;
11552+ Form_pg_constraint partConstr;
11553+ bool queueValidation;
11554+ Oid partConstrFrelid;
11555+ Oid partConstrRelid;
11556+ Oid insertTriggerOid,
11557+ updateTriggerOid;
11558+
11559+ /* Fetch the parent constraint tuple */
11560+ parentConstrTup = SearchSysCache1(CONSTROID,
11561+ ObjectIdGetDatum(parentConstrOid));
11562+ if (!HeapTupleIsValid(parentConstrTup))
11563+ elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11564+ parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11565+
11566+ /* Fetch the child constraint tuple */
11567+ partcontup = SearchSysCache1(CONSTROID,
11568+ ObjectIdGetDatum(partConstrOid));
11569+ if (!HeapTupleIsValid(partcontup))
11570+ elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11571+ partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11572+ partConstrFrelid = partConstr->confrelid;
11573+ partConstrRelid = partConstr->conrelid;
11574+
1152011575 /*
1152111576 * Will we need to validate this constraint? A valid parent constraint
1152211577 * implies that all child constraints have been validated, so if this one
@@ -11528,58 +11583,23 @@ tryAttachPartitionForeignKey(List **wqueue,
1152811583 ReleaseSysCache(parentConstrTup);
1152911584
1153011585 /*
11531- * Looks good! Attach this constraint. The action triggers in the new
11532- * partition become redundant -- the parent table already has equivalent
11533- * ones, and those will be able to reach the partition. Remove the ones
11534- * in the partition. We identify them because they have our constraint
11535- * OID, as well as being on the referenced rel.
11586+ * The action triggers in the new partition become redundant -- the parent
11587+ * table already has equivalent ones, and those will be able to reach the
11588+ * partition. Remove the ones in the partition. We identify them because
11589+ * they have our constraint OID, as well as being on the referenced rel.
1153611590 */
11537- ScanKeyInit(&key,
11538- Anum_pg_trigger_tgconstraint,
11539- BTEqualStrategyNumber, F_OIDEQ,
11540- ObjectIdGetDatum(fk->conoid));
11541- scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11542- NULL, 1, &key);
11543- while ((trigtup = systable_getnext(scan)) != NULL)
11544- {
11545- Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11546- ObjectAddress trigger;
11591+ DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11592+ partConstrRelid);
1154711593
11548- if (trgform->tgconstrrelid != fk->conrelid)
11549- continue;
11550- if (trgform->tgrelid != fk->confrelid)
11551- continue;
11552-
11553- /*
11554- * The constraint is originally set up to contain this trigger as an
11555- * implementation object, so there's a dependency record that links
11556- * the two; however, since the trigger is no longer needed, we remove
11557- * the dependency link in order to be able to drop the trigger while
11558- * keeping the constraint intact.
11559- */
11560- deleteDependencyRecordsFor(TriggerRelationId,
11561- trgform->oid,
11562- false);
11563- /* make dependency deletion visible to performDeletion */
11564- CommandCounterIncrement();
11565- ObjectAddressSet(trigger, TriggerRelationId,
11566- trgform->oid);
11567- performDeletion(&trigger, DROP_RESTRICT, 0);
11568- /* make trigger drop visible, in case the loop iterates */
11569- CommandCounterIncrement();
11570- }
11571-
11572- systable_endscan(scan);
11573-
11574- ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
11594+ ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
1157511595 RelationGetRelid(partition));
1157611596
1157711597 /*
1157811598 * Like the constraint, attach partition's "check" triggers to the
1157911599 * corresponding parent triggers.
1158011600 */
1158111601 GetForeignKeyCheckTriggers(trigrel,
11582- fk->conoid, fk->confrelid, fk->conrelid ,
11602+ partConstrOid, partConstrFrelid, partConstrRelid ,
1158311603 &insertTriggerOid, &updateTriggerOid);
1158411604 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
1158511605 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
@@ -11593,72 +11613,12 @@ tryAttachPartitionForeignKey(List **wqueue,
1159311613 * attaching now has extra pg_constraint rows and action triggers that are
1159411614 * no longer needed. Remove those.
1159511615 */
11596- if (get_rel_relkind(fk->confrelid ) == RELKIND_PARTITIONED_TABLE)
11616+ if (get_rel_relkind(partConstrFrelid ) == RELKIND_PARTITIONED_TABLE)
1159711617 {
1159811618 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11599- ObjectAddresses *objs;
11600- HeapTuple consttup;
11601-
11602- ScanKeyInit(&key,
11603- Anum_pg_constraint_conrelid,
11604- BTEqualStrategyNumber, F_OIDEQ,
11605- ObjectIdGetDatum(fk->conrelid));
11606-
11607- scan = systable_beginscan(pg_constraint,
11608- ConstraintRelidTypidNameIndexId,
11609- true, NULL, 1, &key);
11610- objs = new_object_addresses();
11611- while ((consttup = systable_getnext(scan)) != NULL)
11612- {
11613- Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11614-
11615- if (conform->conparentid != fk->conoid)
11616- continue;
11617- else
11618- {
11619- ObjectAddress addr;
11620- SysScanDesc scan2;
11621- ScanKeyData key2;
11622- int n PG_USED_FOR_ASSERTS_ONLY;
1162311619
11624- ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11625- add_exact_object_address(&addr, objs);
11626-
11627- /*
11628- * First we must delete the dependency record that binds the
11629- * constraint records together.
11630- */
11631- n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11632- conform->oid,
11633- DEPENDENCY_INTERNAL,
11634- ConstraintRelationId,
11635- fk->conoid);
11636- Assert(n == 1); /* actually only one is expected */
11637-
11638- /*
11639- * Now search for the triggers for this constraint and set
11640- * them up for deletion too
11641- */
11642- ScanKeyInit(&key2,
11643- Anum_pg_trigger_tgconstraint,
11644- BTEqualStrategyNumber, F_OIDEQ,
11645- ObjectIdGetDatum(conform->oid));
11646- scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11647- true, NULL, 1, &key2);
11648- while ((trigtup = systable_getnext(scan2)) != NULL)
11649- {
11650- ObjectAddressSet(addr, TriggerRelationId,
11651- ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11652- add_exact_object_address(&addr, objs);
11653- }
11654- systable_endscan(scan2);
11655- }
11656- }
11657- /* make the dependency deletions visible */
11658- CommandCounterIncrement();
11659- performMultipleDeletions(objs, DROP_RESTRICT,
11660- PERFORM_DELETION_INTERNAL);
11661- systable_endscan(scan);
11620+ RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11621+ partConstrRelid);
1166211622
1166311623 table_close(pg_constraint, RowShareLock);
1166411624 }
@@ -11679,18 +11639,147 @@ tryAttachPartitionForeignKey(List **wqueue,
1167911639 Relation conrel;
1168011640
1168111641 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11682- partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11642+
11643+ partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
1168311644 if (!HeapTupleIsValid(partcontup))
11684- elog(ERROR, "cache lookup failed for constraint %u", fk->conoid );
11645+ elog(ERROR, "cache lookup failed for constraint %u", partConstrOid );
1168511646
1168611647 /* Use the same lock as for AT_ValidateConstraint */
1168711648 QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
1168811649 ShareUpdateExclusiveLock);
1168911650 ReleaseSysCache(partcontup);
1169011651 table_close(conrel, RowExclusiveLock);
1169111652 }
11653+ }
1169211654
11693- return true;
11655+ /*
11656+ * RemoveInheritedConstraint
11657+ *
11658+ * Removes the constraint and its associated trigger from the specified
11659+ * relation, which inherited the given constraint.
11660+ */
11661+ static void
11662+ RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11663+ Oid conrelid)
11664+ {
11665+ ObjectAddresses *objs;
11666+ HeapTuple consttup;
11667+ ScanKeyData key;
11668+ SysScanDesc scan;
11669+ HeapTuple trigtup;
11670+
11671+ ScanKeyInit(&key,
11672+ Anum_pg_constraint_conrelid,
11673+ BTEqualStrategyNumber, F_OIDEQ,
11674+ ObjectIdGetDatum(conrelid));
11675+
11676+ scan = systable_beginscan(conrel,
11677+ ConstraintRelidTypidNameIndexId,
11678+ true, NULL, 1, &key);
11679+ objs = new_object_addresses();
11680+ while ((consttup = systable_getnext(scan)) != NULL)
11681+ {
11682+ Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11683+
11684+ if (conform->conparentid != conoid)
11685+ continue;
11686+ else
11687+ {
11688+ ObjectAddress addr;
11689+ SysScanDesc scan2;
11690+ ScanKeyData key2;
11691+ int n PG_USED_FOR_ASSERTS_ONLY;
11692+
11693+ ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11694+ add_exact_object_address(&addr, objs);
11695+
11696+ /*
11697+ * First we must delete the dependency record that binds the
11698+ * constraint records together.
11699+ */
11700+ n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11701+ conform->oid,
11702+ DEPENDENCY_INTERNAL,
11703+ ConstraintRelationId,
11704+ conoid);
11705+ Assert(n == 1); /* actually only one is expected */
11706+
11707+ /*
11708+ * Now search for the triggers for this constraint and set them up
11709+ * for deletion too
11710+ */
11711+ ScanKeyInit(&key2,
11712+ Anum_pg_trigger_tgconstraint,
11713+ BTEqualStrategyNumber, F_OIDEQ,
11714+ ObjectIdGetDatum(conform->oid));
11715+ scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11716+ true, NULL, 1, &key2);
11717+ while ((trigtup = systable_getnext(scan2)) != NULL)
11718+ {
11719+ ObjectAddressSet(addr, TriggerRelationId,
11720+ ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11721+ add_exact_object_address(&addr, objs);
11722+ }
11723+ systable_endscan(scan2);
11724+ }
11725+ }
11726+ /* make the dependency deletions visible */
11727+ CommandCounterIncrement();
11728+ performMultipleDeletions(objs, DROP_RESTRICT,
11729+ PERFORM_DELETION_INTERNAL);
11730+ systable_endscan(scan);
11731+ }
11732+
11733+ /*
11734+ * DropForeignKeyConstraintTriggers
11735+ *
11736+ * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11737+ * action triggers for the foreign key constraint.
11738+ */
11739+ static void
11740+ DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11741+ Oid conrelid)
11742+ {
11743+ ScanKeyData key;
11744+ SysScanDesc scan;
11745+ HeapTuple trigtup;
11746+
11747+ ScanKeyInit(&key,
11748+ Anum_pg_trigger_tgconstraint,
11749+ BTEqualStrategyNumber, F_OIDEQ,
11750+ ObjectIdGetDatum(conoid));
11751+ scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11752+ NULL, 1, &key);
11753+ while ((trigtup = systable_getnext(scan)) != NULL)
11754+ {
11755+ Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11756+ ObjectAddress trigger;
11757+
11758+ if (trgform->tgconstrrelid != conrelid)
11759+ continue;
11760+ if (trgform->tgrelid != confrelid)
11761+ continue;
11762+
11763+ /*
11764+ * The constraint is originally set up to contain this trigger as an
11765+ * implementation object, so there's a dependency record that links
11766+ * the two; however, since the trigger is no longer needed, we remove
11767+ * the dependency link in order to be able to drop the trigger while
11768+ * keeping the constraint intact.
11769+ */
11770+ deleteDependencyRecordsFor(TriggerRelationId,
11771+ trgform->oid,
11772+ false);
11773+ /* make dependency deletion visible to performDeletion */
11774+ CommandCounterIncrement();
11775+ ObjectAddressSet(trigger, TriggerRelationId,
11776+ trgform->oid);
11777+ performDeletion(&trigger, DROP_RESTRICT, 0);
11778+ /* make trigger drop visible, in case the loop iterates */
11779+ CommandCounterIncrement();
11780+ }
11781+
11782+ systable_endscan(scan);
1169411783}
1169511784
1169611785/*
0 commit comments