]> granicus.if.org Git - postgresql/commitdiff
Be lazier about partition tuple routing.
authorRobert Haas <rhaas@postgresql.org>
Thu, 22 Feb 2018 15:55:54 +0000 (10:55 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 22 Feb 2018 15:55:54 +0000 (10:55 -0500)
It's not necessary to fully initialize the executor data structures
for partitions to which no tuples are ever routed.  Consider, for
example, an INSERT statement that inserts only one row: it only cares
about the partition to which that one row is routed.  The new function
ExecInitPartitionInfo performs the initialization in question only
when a particular partition is about to receive a tuple. This includes
creating, validating, and saving a pointer to the ResultRelInfo,
setting up for speculative insertions, translating WCOs and
initializing the resulting expressions, translating returning lists
and building the appropriate projection information, and setting up a
tuple conversion map.

One thing that's not deferred is locking the child partitions; that
seems desirable but would need more thought.  Still, testing shows
that this makes single-row inserts significantly faster on a table
with many partitions without harming the bulk-insert case.

Amit Langote, reviewed by Etsuro Fujita, with a few changes by me

Discussion: http://postgr.es/m/8975331d-d961-cbdd-f862-fdd3d97dc2d0@lab.ntt.co.jp

src/backend/commands/copy.c
src/backend/executor/execPartition.c
src/backend/executor/nodeModifyTable.c
src/include/executor/execPartition.h

index d5883c98d150caa8ce1f429d7134e268e044cfe6..4562a5121d4d287a48fe4c6d5b7de39892a3c144 100644 (file)
@@ -2469,7 +2469,7 @@ CopyFrom(CopyState cstate)
                PartitionTupleRouting *proute;
 
                proute = cstate->partition_tuple_routing =
-                       ExecSetupPartitionTupleRouting(NULL, cstate->rel, 1, estate);
+                       ExecSetupPartitionTupleRouting(NULL, cstate->rel);
 
                /*
                 * If we are capturing transition tuples, they may need to be
@@ -2606,6 +2606,14 @@ CopyFrom(CopyState cstate)
                         */
                        saved_resultRelInfo = resultRelInfo;
                        resultRelInfo = proute->partitions[leaf_part_index];
+                       if (resultRelInfo == NULL)
+                       {
+                               resultRelInfo = ExecInitPartitionInfo(NULL,
+                                                                                                         saved_resultRelInfo,
+                                                                                                         proute, estate,
+                                                                                                         leaf_part_index);
+                               Assert(resultRelInfo != NULL);
+                       }
 
                        /* We do not yet have a way to insert into a foreign partition */
                        if (resultRelInfo->ri_FdwRoutine)
index beb2362ab0f781cf5f691b26c7fcd41faf13805c..54efc9e5452cef67799069775abc30898822dd75 100644 (file)
@@ -44,21 +44,25 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
  *
  * Note that all the relations in the partition tree are locked using the
  * RowExclusiveLock mode upon return from this function.
+ *
+ * While we allocate the arrays of pointers of ResultRelInfo and
+ * TupleConversionMap for all partitions here, actual objects themselves are
+ * lazily allocated for a given partition if a tuple is actually routed to it;
+ * see ExecInitPartitionInfo.  However, if the function is invoked for update
+ * tuple routing, caller would already have initialized ResultRelInfo's for
+ * some of the partitions, which are reused and assigned to their respective
+ * slot in the aforementioned array.
  */
 PartitionTupleRouting *
-ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
-                                                          Relation rel, Index resultRTindex,
-                                                          EState *estate)
+ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
 {
        TupleDesc       tupDesc = RelationGetDescr(rel);
        List       *leaf_parts;
        ListCell   *cell;
        int                     i;
-       ResultRelInfo *leaf_part_arr = NULL,
-                          *update_rri = NULL;
+       ResultRelInfo *update_rri = NULL;
        int                     num_update_rri = 0,
                                update_rri_index = 0;
-       bool            is_update = false;
        PartitionTupleRouting *proute;
 
        /*
@@ -76,13 +80,14 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
        proute->parent_child_tupconv_maps =
                (TupleConversionMap **) palloc0(proute->num_partitions *
                                                                                sizeof(TupleConversionMap *));
+       proute->partition_oids = (Oid *) palloc(proute->num_partitions *
+                                                                                       sizeof(Oid));
 
        /* Set up details specific to the type of tuple routing we are doing. */
        if (mtstate && mtstate->operation == CMD_UPDATE)
        {
                ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
 
-               is_update = true;
                update_rri = mtstate->resultRelInfo;
                num_update_rri = list_length(node->plans);
                proute->subplan_partition_offsets =
@@ -95,16 +100,6 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
                 */
                proute->root_tuple_slot = MakeTupleTableSlot(NULL);
        }
-       else
-       {
-               /*
-                * Since we are inserting tuples, we need to create all new result
-                * rels. Avoid repeated pallocs by allocating memory for all the
-                * result rels in bulk.
-                */
-               leaf_part_arr = (ResultRelInfo *) palloc0(proute->num_partitions *
-                                                                                                 sizeof(ResultRelInfo));
-       }
 
        /*
         * Initialize an empty slot that will be used to manipulate tuples of any
@@ -117,107 +112,58 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
        i = 0;
        foreach(cell, leaf_parts)
        {
-               ResultRelInfo *leaf_part_rri;
-               Relation        partrel = NULL;
-               TupleDesc       part_tupdesc;
+               ResultRelInfo *leaf_part_rri = NULL;
                Oid                     leaf_oid = lfirst_oid(cell);
 
-               if (is_update)
-               {
-                       /*
-                        * If the leaf partition is already present in the per-subplan
-                        * result rels, we re-use that rather than initialize a new result
-                        * rel. The per-subplan resultrels and the resultrels of the leaf
-                        * partitions are both in the same canonical order. So while going
-                        * through the leaf partition oids, we need to keep track of the
-                        * next per-subplan result rel to be looked for in the leaf
-                        * partition resultrels.
-                        */
-                       if (update_rri_index < num_update_rri &&
-                               RelationGetRelid(update_rri[update_rri_index].ri_RelationDesc) == leaf_oid)
-                       {
-                               leaf_part_rri = &update_rri[update_rri_index];
-                               partrel = leaf_part_rri->ri_RelationDesc;
-
-                               /*
-                                * This is required in order to convert the partition's tuple
-                                * to be compatible with the root partitioned table's tuple
-                                * descriptor.  When generating the per-subplan result rels,
-                                * this was not set.
-                                */
-                               leaf_part_rri->ri_PartitionRoot = rel;
-
-                               /* Remember the subplan offset for this ResultRelInfo */
-                               proute->subplan_partition_offsets[update_rri_index] = i;
-
-                               update_rri_index++;
-                       }
-                       else
-                               leaf_part_rri = (ResultRelInfo *) palloc0(sizeof(ResultRelInfo));
-               }
-               else
-               {
-                       /* For INSERTs, we already have an array of result rels allocated */
-                       leaf_part_rri = &leaf_part_arr[i];
-               }
+               proute->partition_oids[i] = leaf_oid;
 
                /*
-                * If we didn't open the partition rel, it means we haven't
-                * initialized the result rel either.
+                * If the leaf partition is already present in the per-subplan result
+                * rels, we re-use that rather than initialize a new result rel. The
+                * per-subplan resultrels and the resultrels of the leaf partitions
+                * are both in the same canonical order. So while going through the
+                * leaf partition oids, we need to keep track of the next per-subplan
+                * result rel to be looked for in the leaf partition resultrels.
                 */
-               if (!partrel)
+               if (update_rri_index < num_update_rri &&
+                       RelationGetRelid(update_rri[update_rri_index].ri_RelationDesc) == leaf_oid)
                {
-                       /*
-                        * We locked all the partitions above including the leaf
-                        * partitions. Note that each of the newly opened relations in
-                        * proute->partitions are eventually closed by the caller.
-                        */
-                       partrel = heap_open(leaf_oid, NoLock);
-                       InitResultRelInfo(leaf_part_rri,
-                                                         partrel,
-                                                         resultRTindex,
-                                                         rel,
-                                                         estate->es_instrument);
+                       Relation        partrel;
+                       TupleDesc       part_tupdesc;
+
+                       leaf_part_rri = &update_rri[update_rri_index];
+                       partrel = leaf_part_rri->ri_RelationDesc;
 
                        /*
-                        * Since we've just initialized this ResultRelInfo, it's not in
-                        * any list attached to the estate as yet.  Add it, so that it can
-                        * be found later.
+                        * This is required in order to convert the partition's tuple to
+                        * be compatible with the root partitioned table's tuple
+                        * descriptor.  When generating the per-subplan result rels, this
+                        * was not set.
                         */
-                       estate->es_tuple_routing_result_relations =
-                                               lappend(estate->es_tuple_routing_result_relations,
-                                                               leaf_part_rri);
-               }
+                       leaf_part_rri->ri_PartitionRoot = rel;
 
-               part_tupdesc = RelationGetDescr(partrel);
+                       /* Remember the subplan offset for this ResultRelInfo */
+                       proute->subplan_partition_offsets[update_rri_index] = i;
 
-               /*
-                * Save a tuple conversion map to convert a tuple routed to this
-                * partition from the parent's type to the partition's.
-                */
-               proute->parent_child_tupconv_maps[i] =
-                       convert_tuples_by_name(tupDesc, part_tupdesc,
-                                                                  gettext_noop("could not convert row type"));
+                       update_rri_index++;
 
-               /*
-                * Verify result relation is a valid target for an INSERT.  An UPDATE
-                * of a partition-key becomes a DELETE+INSERT operation, so this check
-                * is still required when the operation is CMD_UPDATE.
-                */
-               CheckValidResultRel(leaf_part_rri, CMD_INSERT);
+                       part_tupdesc = RelationGetDescr(partrel);
 
-               /*
-                * Open partition indices.  The user may have asked to check for
-                * conflicts within this leaf partition and do "nothing" instead of
-                * throwing an error.  Be prepared in that case by initializing the
-                * index information needed by ExecInsert() to perform speculative
-                * insertions.
-                */
-               if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
-                       leaf_part_rri->ri_IndexRelationDescs == NULL)
-                       ExecOpenIndices(leaf_part_rri,
-                                                       mtstate != NULL &&
-                                                       mtstate->mt_onconflict != ONCONFLICT_NONE);
+                       /*
+                        * Save a tuple conversion map to convert a tuple routed to this
+                        * partition from the parent's type to the partition's.
+                        */
+                       proute->parent_child_tupconv_maps[i] =
+                               convert_tuples_by_name(tupDesc, part_tupdesc,
+                                                                          gettext_noop("could not convert row type"));
+
+                       /*
+                        * Verify result relation is a valid target for an INSERT.  An
+                        * UPDATE of a partition-key becomes a DELETE+INSERT operation, so
+                        * this check is required even when the operation is CMD_UPDATE.
+                        */
+                       CheckValidResultRel(leaf_part_rri, CMD_INSERT);
+               }
 
                proute->partitions[i] = leaf_part_rri;
                i++;
@@ -225,9 +171,9 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
 
        /*
         * For UPDATE, we should have found all the per-subplan resultrels in the
-        * leaf partitions.
+        * leaf partitions.  (If this is an INSERT, both values will be zero.)
         */
-       Assert(!is_update || update_rri_index == num_update_rri);
+       Assert(update_rri_index == num_update_rri);
 
        return proute;
 }
@@ -351,6 +297,201 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
        return result;
 }
 
+/*
+ * ExecInitPartitionInfo
+ *             Initialize ResultRelInfo and other information for a partition if not
+ *             already done
+ *
+ * Returns the ResultRelInfo
+ */
+ResultRelInfo *
+ExecInitPartitionInfo(ModifyTableState *mtstate,
+                                         ResultRelInfo *resultRelInfo,
+                                         PartitionTupleRouting *proute,
+                                         EState *estate, int partidx)
+{
+       Relation        rootrel = resultRelInfo->ri_RelationDesc,
+                               partrel;
+       ResultRelInfo *leaf_part_rri;
+       ModifyTable *node = mtstate ? (ModifyTable *) mtstate->ps.plan : NULL;
+       MemoryContext oldContext;
+
+       /*
+        * We locked all the partitions in ExecSetupPartitionTupleRouting
+        * including the leaf partitions.
+        */
+       partrel = heap_open(proute->partition_oids[partidx], NoLock);
+
+       /*
+        * Keep ResultRelInfo and other information for this partition in the
+        * per-query memory context so they'll survive throughout the query.
+        */
+       oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+       leaf_part_rri = (ResultRelInfo *) palloc0(sizeof(ResultRelInfo));
+       InitResultRelInfo(leaf_part_rri,
+                                         partrel,
+                                         node ? node->nominalRelation : 1,
+                                         rootrel,
+                                         estate->es_instrument);
+
+       /*
+        * Verify result relation is a valid target for an INSERT.  An UPDATE of a
+        * partition-key becomes a DELETE+INSERT operation, so this check is still
+        * required when the operation is CMD_UPDATE.
+        */
+       CheckValidResultRel(leaf_part_rri, CMD_INSERT);
+
+       /*
+        * Since we've just initialized this ResultRelInfo, it's not in any list
+        * attached to the estate as yet.  Add it, so that it can be found later.
+        *
+        * Note that the entries in this list appear in no predetermined order,
+        * because partition result rels are initialized as and when they're
+        * needed.
+        */
+       estate->es_tuple_routing_result_relations =
+               lappend(estate->es_tuple_routing_result_relations,
+                               leaf_part_rri);
+
+       /*
+        * Open partition indices.  The user may have asked to check for conflicts
+        * within this leaf partition and do "nothing" instead of throwing an
+        * error.  Be prepared in that case by initializing the index information
+        * needed by ExecInsert() to perform speculative insertions.
+        */
+       if (partrel->rd_rel->relhasindex &&
+               leaf_part_rri->ri_IndexRelationDescs == NULL)
+               ExecOpenIndices(leaf_part_rri,
+                                               (mtstate != NULL &&
+                                                mtstate->mt_onconflict != ONCONFLICT_NONE));
+
+       /*
+        * Build WITH CHECK OPTION constraints for the partition.  Note that we
+        * didn't build the withCheckOptionList for partitions within the planner,
+        * but simple translation of varattnos will suffice.  This only occurs for
+        * the INSERT case or in the case of UPDATE tuple routing where we didn't
+        * find a result rel to reuse in ExecSetupPartitionTupleRouting().
+        */
+       if (node && node->withCheckOptionLists != NIL)
+       {
+               List       *wcoList;
+               List       *wcoExprs = NIL;
+               ListCell   *ll;
+               int                     firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+               Relation        firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+
+               /*
+                * In the case of INSERT on a partitioned table, there is only one
+                * plan.  Likewise, there is only one WCO list, not one per partition.
+                * For UPDATE, there are as many WCO lists as there are plans.
+                */
+               Assert((node->operation == CMD_INSERT &&
+                               list_length(node->withCheckOptionLists) == 1 &&
+                               list_length(node->plans) == 1) ||
+                          (node->operation == CMD_UPDATE &&
+                               list_length(node->withCheckOptionLists) ==
+                               list_length(node->plans)));
+
+               /*
+                * Use the WCO list of the first plan as a reference to calculate
+                * attno's for the WCO list of this partition.  In the INSERT case,
+                * that refers to the root partitioned table, whereas in the UPDATE
+                * tuple routing case, that refers to the first partition in the
+                * mtstate->resultRelInfo array.  In any case, both that relation and
+                * this partition should have the same columns, so we should be able
+                * to map attributes successfully.
+                */
+               wcoList = linitial(node->withCheckOptionLists);
+
+               /*
+                * Convert Vars in it to contain this partition's attribute numbers.
+                */
+               wcoList = map_partition_varattnos(wcoList, firstVarno,
+                                                                                 partrel, firstResultRel, NULL);
+               foreach(ll, wcoList)
+               {
+                       WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
+                       ExprState  *wcoExpr = ExecInitQual(castNode(List, wco->qual),
+                                                                                          mtstate->mt_plans[0]);
+
+                       wcoExprs = lappend(wcoExprs, wcoExpr);
+               }
+
+               leaf_part_rri->ri_WithCheckOptions = wcoList;
+               leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
+       }
+
+       /*
+        * Build the RETURNING projection for the partition.  Note that we didn't
+        * build the returningList for partitions within the planner, but simple
+        * translation of varattnos will suffice.  This only occurs for the INSERT
+        * case or in the case of UPDATE tuple routing where we didn't find a
+        * result rel to reuse in ExecSetupPartitionTupleRouting().
+        */
+       if (node && node->returningLists != NIL)
+       {
+               TupleTableSlot *slot;
+               ExprContext *econtext;
+               List       *returningList;
+               int                     firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+               Relation        firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+
+               /* See the comment above for WCO lists. */
+               Assert((node->operation == CMD_INSERT &&
+                               list_length(node->returningLists) == 1 &&
+                               list_length(node->plans) == 1) ||
+                          (node->operation == CMD_UPDATE &&
+                               list_length(node->returningLists) ==
+                               list_length(node->plans)));
+
+               /*
+                * Use the RETURNING list of the first plan as a reference to
+                * calculate attno's for the RETURNING list of this partition.  See
+                * the comment above for WCO lists for more details on why this is
+                * okay.
+                */
+               returningList = linitial(node->returningLists);
+
+               /*
+                * Convert Vars in it to contain this partition's attribute numbers.
+                */
+               returningList = map_partition_varattnos(returningList, firstVarno,
+                                                                                               partrel, firstResultRel,
+                                                                                               NULL);
+
+               /*
+                * Initialize the projection itself.
+                *
+                * Use the slot and the expression context that would have been set up
+                * in ExecInitModifyTable() for projection's output.
+                */
+               Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
+               slot = mtstate->ps.ps_ResultTupleSlot;
+               Assert(mtstate->ps.ps_ExprContext != NULL);
+               econtext = mtstate->ps.ps_ExprContext;
+               leaf_part_rri->ri_projectReturning =
+                       ExecBuildProjectionInfo(returningList, econtext, slot,
+                                                                       &mtstate->ps, RelationGetDescr(partrel));
+       }
+
+       Assert(proute->partitions[partidx] == NULL);
+       proute->partitions[partidx] = leaf_part_rri;
+
+       /*
+        * Save a tuple conversion map to convert a tuple routed to this partition
+        * from the parent's type to the partition's.
+        */
+       proute->parent_child_tupconv_maps[partidx] =
+               convert_tuples_by_name(RelationGetDescr(rootrel),
+                                                          RelationGetDescr(partrel),
+                                                          gettext_noop("could not convert row type"));
+
+       MemoryContextSwitchTo(oldContext);
+
+       return leaf_part_rri;
+}
+
 /*
  * ExecSetupChildParentMapForLeaf -- Initialize the per-leaf-partition
  * child-to-root tuple conversion map array.
@@ -477,6 +618,10 @@ ExecCleanupTupleRouting(PartitionTupleRouting *proute)
        {
                ResultRelInfo *resultRelInfo = proute->partitions[i];
 
+               /* skip further processsing for uninitialized partitions */
+               if (resultRelInfo == NULL)
+                       continue;
+
                /*
                 * If this result rel is one of the UPDATE subplan result rels, let
                 * ExecEndPlan() close it. For INSERT or COPY,
index 93c03cfb0716242bb8b0809469d9696e6f93541d..c32928d9bd7f116026d8158f12d3e9c6ea67bd47 100644 (file)
@@ -306,10 +306,18 @@ ExecInsert(ModifyTableState *mtstate,
 
                /*
                 * Save the old ResultRelInfo and switch to the one corresponding to
-                * the selected partition.
+                * the selected partition.  (We might need to initialize it first.)
                 */
                saved_resultRelInfo = resultRelInfo;
                resultRelInfo = proute->partitions[leaf_part_index];
+               if (resultRelInfo == NULL)
+               {
+                       resultRelInfo = ExecInitPartitionInfo(mtstate,
+                                                                                                 saved_resultRelInfo,
+                                                                                                 proute, estate,
+                                                                                                 leaf_part_index);
+                       Assert(resultRelInfo != NULL);
+               }
 
                /* We do not yet have a way to insert into a foreign partition */
                if (resultRelInfo->ri_FdwRoutine)
@@ -2098,14 +2106,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        ResultRelInfo *saved_resultRelInfo;
        ResultRelInfo *resultRelInfo;
        Plan       *subplan;
-       int                     firstVarno = 0;
-       Relation        firstResultRel = NULL;
        ListCell   *l;
        int                     i;
        Relation        rel;
        bool            update_tuple_routing_needed = node->partColsUpdated;
-       PartitionTupleRouting *proute = NULL;
-       int                     num_partitions = 0;
 
        /* check for unsupported flags */
        Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -2228,20 +2232,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
         */
        if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
                (operation == CMD_INSERT || update_tuple_routing_needed))
-       {
-               proute = mtstate->mt_partition_tuple_routing =
-                       ExecSetupPartitionTupleRouting(mtstate,
-                                                                                  rel, node->nominalRelation,
-                                                                                  estate);
-               num_partitions = proute->num_partitions;
-
-               /*
-                * Below are required as reference objects for mapping partition
-                * attno's in expressions such as WithCheckOptions and RETURNING.
-                */
-               firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
-               firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
-       }
+               mtstate->mt_partition_tuple_routing =
+                                               ExecSetupPartitionTupleRouting(mtstate, rel);
 
        /*
         * Build state for collecting transition tuples.  This requires having a
@@ -2287,70 +2279,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                i++;
        }
 
-       /*
-        * Build WITH CHECK OPTION constraints for each leaf partition rel. Note
-        * that we didn't build the withCheckOptionList for each partition within
-        * the planner, but simple translation of the varattnos for each partition
-        * will suffice.  This only occurs for the INSERT case or for UPDATE row
-        * movement. DELETEs and local UPDATEs are handled above.
-        */
-       if (node->withCheckOptionLists != NIL && num_partitions > 0)
-       {
-               List       *first_wcoList;
-
-               /*
-                * In case of INSERT on partitioned tables, there is only one plan.
-                * Likewise, there is only one WITH CHECK OPTIONS list, not one per
-                * partition. Whereas for UPDATE, there are as many WCOs as there are
-                * plans. So in either case, use the WCO expression of the first
-                * resultRelInfo as a reference to calculate attno's for the WCO
-                * expression of each of the partitions. We make a copy of the WCO
-                * qual for each partition. Note that, if there are SubPlans in there,
-                * they all end up attached to the one parent Plan node.
-                */
-               Assert(update_tuple_routing_needed ||
-                          (operation == CMD_INSERT &&
-                               list_length(node->withCheckOptionLists) == 1 &&
-                               mtstate->mt_nplans == 1));
-
-               first_wcoList = linitial(node->withCheckOptionLists);
-               for (i = 0; i < num_partitions; i++)
-               {
-                       Relation        partrel;
-                       List       *mapped_wcoList;
-                       List       *wcoExprs = NIL;
-                       ListCell   *ll;
-
-                       resultRelInfo = proute->partitions[i];
-
-                       /*
-                        * If we are referring to a resultRelInfo from one of the update
-                        * result rels, that result rel would already have
-                        * WithCheckOptions initialized.
-                        */
-                       if (resultRelInfo->ri_WithCheckOptions)
-                               continue;
-
-                       partrel = resultRelInfo->ri_RelationDesc;
-
-                       mapped_wcoList = map_partition_varattnos(first_wcoList,
-                                                                                                        firstVarno,
-                                                                                                        partrel, firstResultRel,
-                                                                                                        NULL);
-                       foreach(ll, mapped_wcoList)
-                       {
-                               WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
-                               ExprState  *wcoExpr = ExecInitQual(castNode(List, wco->qual),
-                                                                                                  &mtstate->ps);
-
-                               wcoExprs = lappend(wcoExprs, wcoExpr);
-                       }
-
-                       resultRelInfo->ri_WithCheckOptions = mapped_wcoList;
-                       resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
-               }
-       }
-
        /*
         * Initialize RETURNING projections if needed.
         */
@@ -2358,7 +2286,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        {
                TupleTableSlot *slot;
                ExprContext *econtext;
-               List       *firstReturningList;
 
                /*
                 * Initialize result tuple slot and assign its rowtype using the first
@@ -2388,44 +2315,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                                                                                resultRelInfo->ri_RelationDesc->rd_att);
                        resultRelInfo++;
                }
-
-               /*
-                * Build a projection for each leaf partition rel.  Note that we
-                * didn't build the returningList for each partition within the
-                * planner, but simple translation of the varattnos for each partition
-                * will suffice.  This only occurs for the INSERT case or for UPDATE
-                * row movement. DELETEs and local UPDATEs are handled above.
-                */
-               firstReturningList = linitial(node->returningLists);
-               for (i = 0; i < num_partitions; i++)
-               {
-                       Relation        partrel;
-                       List       *rlist;
-
-                       resultRelInfo = proute->partitions[i];
-
-                       /*
-                        * If we are referring to a resultRelInfo from one of the update
-                        * result rels, that result rel would already have a returningList
-                        * built.
-                        */
-                       if (resultRelInfo->ri_projectReturning)
-                               continue;
-
-                       partrel = resultRelInfo->ri_RelationDesc;
-
-                       /*
-                        * Use the returning expression of the first resultRelInfo as a
-                        * reference to calculate attno's for the returning expression of
-                        * each of the partitions.
-                        */
-                       rlist = map_partition_varattnos(firstReturningList,
-                                                                                       firstVarno,
-                                                                                       partrel, firstResultRel, NULL);
-                       resultRelInfo->ri_projectReturning =
-                               ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
-                                                                               resultRelInfo->ri_RelationDesc->rd_att);
-               }
        }
        else
        {
index 3df9c498bbf6d93e4183ec484783ccae542bba38..e94718608fb987504aefecf71e73a9be1cdc3c12 100644 (file)
@@ -58,6 +58,7 @@ typedef struct PartitionDispatchData *PartitionDispatch;
  *                                                             partition tree.
  * num_dispatch                                        number of partitioned tables in the partition
  *                                                             tree (= length of partition_dispatch_info[])
+ * partition_oids                              Array of leaf partitions OIDs
  * partitions                                  Array of ResultRelInfo* objects with one entry
  *                                                             for every leaf partition in the partition tree.
  * num_partitions                              Number of leaf partitions in the partition tree
@@ -91,6 +92,7 @@ typedef struct PartitionTupleRouting
 {
        PartitionDispatch *partition_dispatch_info;
        int                     num_dispatch;
+       Oid                *partition_oids;
        ResultRelInfo **partitions;
        int                     num_partitions;
        TupleConversionMap **parent_child_tupconv_maps;
@@ -103,12 +105,15 @@ typedef struct PartitionTupleRouting
 } PartitionTupleRouting;
 
 extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
-                                                          Relation rel, Index resultRTindex,
-                                                          EState *estate);
+                                                          Relation rel);
 extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
                                  PartitionDispatch *pd,
                                  TupleTableSlot *slot,
                                  EState *estate);
+extern ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
+                                       ResultRelInfo *resultRelInfo,
+                                       PartitionTupleRouting *proute,
+                                       EState *estate, int partidx);
 extern void ExecSetupChildParentMapForLeaf(PartitionTupleRouting *proute);
 extern TupleConversionMap *TupConvMapForLeaf(PartitionTupleRouting *proute,
                                  ResultRelInfo *rootRelInfo, int leaf_index);