Clean up code to resolve the "root target relation" in nodeModifyTable.c
authorHeikki Linnakangas
Mon, 19 Oct 2020 11:11:44 +0000 (14:11 +0300)
committerHeikki Linnakangas
Mon, 19 Oct 2020 11:42:40 +0000 (14:42 +0300)
When executing DDL on a partitioned table or on a table with inheritance
children, statement-level triggers must be fired against the table given
in the original statement. The code to look that up was a bit messy and
duplicative. Commit 501ed02cf6 added a helper function,
getASTriggerResultRelInfo() (later renamed to getTargetResultRelInfo())
for it, but for some reason it was only used when firing AFTER STATEMENT
triggers and the code to fire BEFORE STATEMENT triggers duplicated the
logic.

Determine the target relation in ExecInitModifyTable(), and set it always
in ModifyTableState. Code that used to call getTargetResultRelInfo() can
now use ModifyTableState->rootResultRelInfo directly.

Discussion: https://www.postgresql.org/message-id/CA%2BHiwqFViT47Zbr_ASBejiK7iDG8%3DQ1swQ-tjM6caRPQ67pT%3Dw%40mail.gmail.com

src/backend/executor/nodeModifyTable.c
src/include/nodes/execnodes.h

index 0c055ed40805dd6b7aa2cbbb31379f0d6d2df3a2..ae209bc774a316546cb8e4fe685198e81d0de968 100644 (file)
@@ -72,7 +72,6 @@ static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
                                               ResultRelInfo *targetRelInfo,
                                               TupleTableSlot *slot,
                                               ResultRelInfo **partRelInfo);
-static ResultRelInfo *getTargetResultRelInfo(ModifyTableState *node);
 static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate);
 static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
                                                   int whichplan);
@@ -1186,7 +1185,6 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
        saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
 
    /* Tuple routing starts from the root table. */
-   Assert(mtstate->rootResultRelInfo != NULL);
    *inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
                                 planSlot, estate, canSetTag);
 
@@ -1796,15 +1794,7 @@ static void
 fireBSTriggers(ModifyTableState *node)
 {
    ModifyTable *plan = (ModifyTable *) node->ps.plan;
-   ResultRelInfo *resultRelInfo = node->resultRelInfo;
-
-   /*
-    * If the node modifies a partitioned table, we must fire its triggers.
-    * Note that in that case, node->resultRelInfo points to the first leaf
-    * partition, not the root table.
-    */
-   if (node->rootResultRelInfo != NULL)
-       resultRelInfo = node->rootResultRelInfo;
+   ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
 
    switch (node->operation)
    {
@@ -1826,28 +1816,6 @@ fireBSTriggers(ModifyTableState *node)
    }
 }
 
-/*
- * Return the target rel ResultRelInfo.
- *
- * This relation is the same as :
- * - the relation for which we will fire AFTER STATEMENT triggers.
- * - the relation into whose tuple format all captured transition tuples must
- *   be converted.
- * - the root partitioned table.
- */
-static ResultRelInfo *
-getTargetResultRelInfo(ModifyTableState *node)
-{
-   /*
-    * Note that if the node modifies a partitioned table, node->resultRelInfo
-    * points to the first leaf partition, not the root table.
-    */
-   if (node->rootResultRelInfo != NULL)
-       return node->rootResultRelInfo;
-   else
-       return node->resultRelInfo;
-}
-
 /*
  * Process AFTER EACH STATEMENT triggers
  */
@@ -1855,7 +1823,7 @@ static void
 fireASTriggers(ModifyTableState *node)
 {
    ModifyTable *plan = (ModifyTable *) node->ps.plan;
-   ResultRelInfo *resultRelInfo = getTargetResultRelInfo(node);
+   ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
 
    switch (node->operation)
    {
@@ -1889,7 +1857,7 @@ static void
 ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
 {
    ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
-   ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
+   ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
 
    /* Check for transition tables on the directly targeted relation. */
    mtstate->mt_transition_capture =
@@ -2019,7 +1987,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
 static void
 ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
 {
-   ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
+   ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
    ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
    TupleDesc   outdesc;
    int         numResultRelInfos = mtstate->mt_nplans;
@@ -2355,13 +2323,31 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        palloc(nplans * sizeof(ResultRelInfo));
    mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
 
-   /* If modifying a partitioned table, initialize the root table info */
+   /*----------
+    * Resolve the target relation. This is the same as:
+    *
+    * - the relation for which we will fire FOR STATEMENT triggers,
+    * - the relation into whose tuple format all captured transition tuples
+    *   must be converted, and
+    * - the root partitioned table used for tuple routing.
+    *
+    * If it's a partitioned table, the root partition doesn't appear
+    * elsewhere in the plan and its RT index is given explicitly in
+    * node->rootRelation.  Otherwise (i.e. table inheritance) the target
+    * relation is the first relation in the node->resultRelations list, and
+    * we will initialize it in the loop below.
+    *----------
+    */
    if (node->rootRelation > 0)
    {
        mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
        ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
                               node->rootRelation);
    }
+   else
+   {
+       mtstate->rootResultRelInfo = mtstate->resultRelInfo;
+   }
 
    mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
    mtstate->mt_nplans = nplans;
@@ -2446,7 +2432,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
    }
 
    /* Get the target relation */
-   rel = (getTargetResultRelInfo(mtstate))->ri_RelationDesc;
+   rel = mtstate->rootResultRelInfo->ri_RelationDesc;
 
    /*
     * If it's not a partitioned table after all, UPDATE tuple routing should
index b7e9e5d539de3ba32e984b717c0f896701cffa30..dff34fbc14e294e9fe4b48cee1dba85972774b41 100644 (file)
@@ -1154,8 +1154,13 @@ typedef struct ModifyTableState
    TupleTableSlot **mt_scans;  /* input tuple corresponding to underlying
                                 * plans */
    ResultRelInfo *resultRelInfo;   /* per-subplan target relations */
-   ResultRelInfo *rootResultRelInfo;   /* root target relation (partitioned
-                                        * table root) */
+
+   /*
+    * Target relation mentioned in the original statement, used to fire
+    * statement-level triggers and as the root for tuple routing.
+    */
+   ResultRelInfo *rootResultRelInfo;
+
    List      **mt_arowmarks;   /* per-subplan ExecAuxRowMark lists */
    EPQState    mt_epqstate;    /* for evaluating EvalPlanQual rechecks */
    bool        fireBSTriggers; /* do we need to fire stmt triggers? */