@@ -585,6 +585,14 @@ static bool tryAttachPartitionForeignKey(List **wqueue,
585
585
Oid parentInsTrigger,
586
586
Oid parentUpdTrigger,
587
587
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);
588
596
static void GetForeignKeyActionTriggers(Relation trigrel,
589
597
Oid conoid, Oid confrelid, Oid conrelid,
590
598
Oid *deleteTriggerOid,
@@ -11467,12 +11475,6 @@ tryAttachPartitionForeignKey(List **wqueue,
11467
11475
Form_pg_constraint parentConstr;
11468
11476
HeapTuple partcontup;
11469
11477
Form_pg_constraint partConstr;
11470
- bool queueValidation;
11471
- ScanKeyData key;
11472
- SysScanDesc scan;
11473
- HeapTuple trigtup;
11474
- Oid insertTriggerOid,
11475
- updateTriggerOid;
11476
11478
11477
11479
parentConstrTup = SearchSysCache1(CONSTROID,
11478
11480
ObjectIdGetDatum(parentConstrOid));
@@ -11517,6 +11519,59 @@ tryAttachPartitionForeignKey(List **wqueue,
11517
11519
return false;
11518
11520
}
11519
11521
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
+
11520
11575
/*
11521
11576
* Will we need to validate this constraint? A valid parent constraint
11522
11577
* implies that all child constraints have been validated, so if this one
@@ -11528,58 +11583,23 @@ tryAttachPartitionForeignKey(List **wqueue,
11528
11583
ReleaseSysCache(parentConstrTup);
11529
11584
11530
11585
/*
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.
11536
11590
*/
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);
11547
11593
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,
11575
11595
RelationGetRelid(partition));
11576
11596
11577
11597
/*
11578
11598
* Like the constraint, attach partition's "check" triggers to the
11579
11599
* corresponding parent triggers.
11580
11600
*/
11581
11601
GetForeignKeyCheckTriggers(trigrel,
11582
- fk->conoid, fk->confrelid, fk->conrelid ,
11602
+ partConstrOid, partConstrFrelid, partConstrRelid ,
11583
11603
&insertTriggerOid, &updateTriggerOid);
11584
11604
Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11585
11605
TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
@@ -11593,72 +11613,12 @@ tryAttachPartitionForeignKey(List **wqueue,
11593
11613
* attaching now has extra pg_constraint rows and action triggers that are
11594
11614
* no longer needed. Remove those.
11595
11615
*/
11596
- if (get_rel_relkind(fk->confrelid ) == RELKIND_PARTITIONED_TABLE)
11616
+ if (get_rel_relkind(partConstrFrelid ) == RELKIND_PARTITIONED_TABLE)
11597
11617
{
11598
11618
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;
11623
11619
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);
11662
11622
11663
11623
table_close(pg_constraint, RowShareLock);
11664
11624
}
@@ -11679,18 +11639,147 @@ tryAttachPartitionForeignKey(List **wqueue,
11679
11639
Relation conrel;
11680
11640
11681
11641
conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11682
- partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11642
+
11643
+ partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11683
11644
if (!HeapTupleIsValid(partcontup))
11684
- elog(ERROR, "cache lookup failed for constraint %u", fk->conoid );
11645
+ elog(ERROR, "cache lookup failed for constraint %u", partConstrOid );
11685
11646
11686
11647
/* Use the same lock as for AT_ValidateConstraint */
11687
11648
QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11688
11649
ShareUpdateExclusiveLock);
11689
11650
ReleaseSysCache(partcontup);
11690
11651
table_close(conrel, RowExclusiveLock);
11691
11652
}
11653
+ }
11692
11654
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);
11694
11783
}
11695
11784
11696
11785
/*
0 commit comments