]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
refactor: Split tryAttachPartitionForeignKey()
authorPeter Eisentraut <peter@eisentraut.org>
Tue, 11 Mar 2025 08:33:36 +0000 (09:33 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Tue, 11 Mar 2025 08:35:24 +0000 (09:35 +0100)
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

src/backend/commands/tablecmds.c

index e9b95018199a5b24742ca58609fd7361875ef0a5..1ada38908c19746278c9661b12a09515d71d0c71 100644 (file)
@@ -585,6 +585,14 @@ static bool tryAttachPartitionForeignKey(List **wqueue,
                                                                                 Oid parentInsTrigger,
                                                                                 Oid parentUpdTrigger,
                                                                                 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,
                                                                                Oid conoid, Oid confrelid, Oid conrelid,
                                                                                Oid *deleteTriggerOid,
@@ -11467,12 +11475,6 @@ tryAttachPartitionForeignKey(List **wqueue,
        Form_pg_constraint parentConstr;
        HeapTuple       partcontup;
        Form_pg_constraint partConstr;
-       bool            queueValidation;
-       ScanKeyData key;
-       SysScanDesc scan;
-       HeapTuple       trigtup;
-       Oid                     insertTriggerOid,
-                               updateTriggerOid;
 
        parentConstrTup = SearchSysCache1(CONSTROID,
                                                                          ObjectIdGetDatum(parentConstrOid));
@@ -11517,6 +11519,59 @@ tryAttachPartitionForeignKey(List **wqueue,
                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
         * implies that all child constraints have been validated, so if this one
@@ -11528,50 +11583,15 @@ tryAttachPartitionForeignKey(List **wqueue,
        ReleaseSysCache(parentConstrTup);
 
        /*
-        * Looks good!  Attach this constraint.  The action triggers in the new
-        * partition become redundant -- the parent table already has equivalent
-        * ones, and those will be able to reach the partition.  Remove the ones
-        * in the partition.  We identify them because they have our constraint
-        * OID, as well as being on the referenced rel.
+        * The action triggers in the new partition become redundant -- the parent
+        * table already has equivalent ones, and those will be able to reach the
+        * partition.  Remove the ones in the partition.  We identify them because
+        * they have our constraint OID, as well as being on the referenced rel.
         */
-       ScanKeyInit(&key,
-                               Anum_pg_trigger_tgconstraint,
-                               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;
+       DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
+                                                                        partConstrRelid);
 
-               if (trgform->tgconstrrelid != fk->conrelid)
-                       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,
+       ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
                                                                  RelationGetRelid(partition));
 
        /*
@@ -11579,7 +11599,7 @@ tryAttachPartitionForeignKey(List **wqueue,
         * corresponding parent triggers.
         */
        GetForeignKeyCheckTriggers(trigrel,
-                                                          fk->conoid, fk->confrelid, fk->conrelid,
+                                                          partConstrOid, partConstrFrelid, partConstrRelid,
                                                           &insertTriggerOid, &updateTriggerOid);
        Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
        TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
@@ -11593,72 +11613,12 @@ tryAttachPartitionForeignKey(List **wqueue,
         * attaching now has extra pg_constraint rows and action triggers that are
         * 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);
-               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);
-                               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,
-                                                                                                          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);
+               RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
+                                                                 partConstrRelid);
 
                table_close(pg_constraint, RowShareLock);
        }
@@ -11679,9 +11639,10 @@ tryAttachPartitionForeignKey(List **wqueue,
                Relation        conrel;
 
                conrel = table_open(ConstraintRelationId, RowExclusiveLock);
-               partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
+
+               partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
                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 */
                QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
@@ -11689,8 +11650,136 @@ tryAttachPartitionForeignKey(List **wqueue,
                ReleaseSysCache(partcontup);
                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);
 }
 
 /*