On 2018/11/01 10:30, David Rowley wrote:
> It's great to know the patch is now so perfect that we've only the
> macro naming left to debate ;-)
I looked over v12 again and noticed a couple minor issues.
+ * table then we store the index into parenting
+ * PartitionTupleRouting 'partition_dispatch_info' array. An
s/PartitionTupleRouting/PartitionTupleRouting's/g
Also, I got a bit concerned about "parenting". Does it mean something
like "enclosing", because the PartitionDispatch is a member of
PartitionTupleRouting? I got concerned because using "parent" like this
may be confusing as this is the partitioning code we're talking about,
where "parent" is generally used to mean "parent" table.
+ * the partitioned table that's the target of the command. If we must
+ * route tuple via some sub-partitioned table, then the PartitionDispatch
+ * for those is only built the first time it's required.
... via some sub-partitioned table"s"
Or perhaps rewrite a bit as:
If we must route the tuple via some sub-partitioned table, then its
PartitionDispatch is built the first time it's required.
The macro naming discussion got me thinking today about the macro itself.
It encapsulates access to the various PartitionTupleRouting arrays
containing the maps, but maybe we've got the interface of tuple routing a
bit (maybe a lot given this thread!) wrong to begin with. Instead of
ExecFindPartition returning indexes into various PartitionTupleRouting
arrays and its callers then using those indexes to fetch various objects
from those arrays, why doesn't it return those objects itself? Although
we made sure that the callers don't need to worry about the meaning of
these indexes changing with this patch, it still seems a bit odd for them
to have to go back to those arrays to get various objects.
How about we change ExecFindPartition's interface so that it returns the
ResultRelInfo, the two maps, and the partition slot? So, the arrays
simply become a cache for ExecFindPartition et al and are no longer
accessed outside execPartition.c. Although that makes the interface of
ExecFindPartition longer, I think it reduces overall complexity.
I've implemented that in the attached patch
1-revise-ExecFindPartition-interface.patch.
Also, since all members of PartitionTupleRouting are only accessed within
execPartition.c save root_tuple_slot, we can move it to execPartition.c to
make its internals private, after doing something about root_tuple_slot.
Looking at the code related to root_tuple_slot, it seems the field really
belongs in ModifyTableState, because it got nothing to do with routing.
Attached 2-make-PartitionTupleRouting-private.patch does that.
These patches 1 and 2 apply on top of v12-0001.. patch.
Thanks,
Amit
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0b0696e61e..b45972682f 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2316,6 +2316,7 @@ CopyFrom(CopyState cstate)
bool *nulls;
ResultRelInfo *resultRelInfo;
ResultRelInfo *target_resultRelInfo;
+ ResultRelInfo *prev_part_rel = NULL;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
ModifyTableState *mtstate;
ExprContext *econtext;
@@ -2331,7 +2332,6 @@ CopyFrom(CopyState cstate)
CopyInsertMethod insertMethod;
uint64 processed = 0;
int nBufferedTuples = 0;
- int prev_leaf_part_index = -1;
bool has_before_insert_row_trig;
bool has_instead_insert_row_trig;
bool leafpart_use_multi_insert = false;
@@ -2685,19 +2685,24 @@ CopyFrom(CopyState cstate)
/* Determine the partition to heap_insert the tuple into */
if (proute)
{
- int leaf_part_index;
- TupleConversionMap *map;
+ TupleTableSlot *partition_slot = NULL;
+ TupleConversionMap *child_to_parent_map,
+ *parent_to_child_map;
/*
* Attempt to find a partition suitable for this tuple.
* ExecFindPartition() will raise an error if none can
be found.
+ * This replaces the original target ResultRelInfo with
+ * partition's.
*/
- leaf_part_index = ExecFindPartition(mtstate,
target_resultRelInfo,
-
proute, slot, estate);
- Assert(leaf_part_index >= 0 &&
- leaf_part_index < proute->num_partitions);
+ resultRelInfo = ExecFindPartition(mtstate,
target_resultRelInfo,
+
proute, slot, estate,
+
&parent_to_child_map,
+
&partition_slot,
+
&child_to_parent_map);
+ Assert(resultRelInfo != NULL);
- if (prev_leaf_part_index != leaf_part_index)
+ if (prev_part_rel != resultRelInfo)
{
/* Check if we can multi-insert into this
partition */
if (insertMethod == CIM_MULTI_CONDITIONAL)
@@ -2710,12 +2715,9 @@ CopyFrom(CopyState cstate)
if (nBufferedTuples > 0)
{
ExprContext *swapcontext;
- ResultRelInfo *presultRelInfo;
-
- presultRelInfo =
proute->partitions[prev_leaf_part_index];
CopyFromInsertBatch(cstate,
estate, mycid, hi_options,
-
presultRelInfo, myslot, bistate,
+
prev_part_rel, myslot, bistate,
nBufferedTuples, bufferedTuples,
firstBufferedLineNo);
nBufferedTuples = 0;
@@ -2772,13 +2774,6 @@ CopyFrom(CopyState cstate)
}
}
- /*
- * Overwrite resultRelInfo with the
corresponding partition's
- * one.
- */
- resultRelInfo =
proute->partitions[leaf_part_index];
- Assert(resultRelInfo != NULL);
-
/* Determine which triggers exist on this
partition */
has_before_insert_row_trig =
(resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row);
@@ -2804,7 +2799,7 @@ CopyFrom(CopyState cstate)
* buffer when the partition being inserted
into changes.
*/
ReleaseBulkInsertStatePin(bistate);
- prev_leaf_part_index = leaf_part_index;
+ prev_part_rel = resultRelInfo;
}
/*
@@ -2826,8 +2821,7 @@ CopyFrom(CopyState cstate)
* tuplestore format.
*/
cstate->transition_capture->tcs_original_insert_tuple = NULL;
- cstate->transition_capture->tcs_map =
-
PartitionChildToParentMap(proute, leaf_part_index);
+ cstate->transition_capture->tcs_map =
child_to_parent_map;
}
else
{
@@ -2844,16 +2838,13 @@ CopyFrom(CopyState cstate)
* We might need to convert from the parent rowtype to
the
* partition rowtype.
*/
- map = PartitionParentToChildMap(proute,
leaf_part_index);
- if (map != NULL)
+ if (parent_to_child_map != NULL)
{
- TupleTableSlot *new_slot;
MemoryContext oldcontext;
- Assert(proute->partition_tuple_slots != NULL &&
-
proute->partition_tuple_slots[leaf_part_index] != NULL);
- new_slot =
proute->partition_tuple_slots[leaf_part_index];
- slot = execute_attr_map_slot(map->attrMap,
slot, new_slot);
+ Assert(partition_slot != NULL);
+ slot =
execute_attr_map_slot(parent_to_child_map->attrMap,
+
slot, partition_slot);
/*
* Get the tuple in the per-tuple context, so
that it will be
@@ -2997,12 +2988,8 @@ CopyFrom(CopyState cstate)
{
if (insertMethod == CIM_MULTI_CONDITIONAL)
{
- ResultRelInfo *presultRelInfo;
-
- presultRelInfo =
proute->partitions[prev_leaf_part_index];
-
CopyFromInsertBatch(cstate, estate, mycid, hi_options,
- presultRelInfo,
myslot, bistate,
+ prev_part_rel,
myslot, bistate,
nBufferedTuples, bufferedTuples,
firstBufferedLineNo);
}
diff --git a/src/backend/executor/execPartition.c
b/src/backend/executor/execPartition.c
index 542578102f..4b27874f01 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -70,11 +70,16 @@ typedef struct PartitionDispatchData
static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate,
PartitionTupleRouting *proute);
static void ExecExpandRoutingArrays(PartitionTupleRouting *proute);
-static int ExecInitPartitionInfo(ModifyTableState *mtstate,
+static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
ResultRelInfo *rootResultRelInfo,
PartitionTupleRouting *proute,
EState *estate,
PartitionDispatch dispatch, int
partidx);
+static void ExecInitRoutingInfo(ModifyTableState *mtstate,
+ EState *estate,
+ PartitionTupleRouting *proute,
+ ResultRelInfo *partRelInfo,
+ int partidx);
static PartitionDispatch ExecInitPartitionDispatchInfo(PartitionTupleRouting
*proute,
Oid partoid,
PartitionDispatch parent_pd, int partidx);
static void FormPartitionKeyDatum(PartitionDispatch pd,
@@ -194,14 +199,25 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
Relation rel)
* partition key(s)
*
* If no leaf partition is found, this routine errors out with the appropriate
- * error message, else it returns the index of the leaf partition's
- * ResultRelInfo in the proute->partitions array.
+ * error message, else it returns the leaf partition's ResultRelInfo.
+ *
+ * *parent_to_child_map is set if the parent tuples would need to be converted
+ * before inserting into the chosen partition. In that case,
+ * *partition_tuple_slot is also set.
+ *
+ * *child_to_parent_map is set if the tuples inserted into the partition after
+ * routing would need to be converted back to parent's rowtype for storing
+ * into the transition tuple store, that is, only if transition capture is
+ * active for the command.
*/
-int
+ResultRelInfo *
ExecFindPartition(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
PartitionTupleRouting *proute,
- TupleTableSlot *slot, EState *estate)
+ TupleTableSlot *slot, EState *estate,
+ TupleConversionMap **parent_to_child_map,
+ TupleTableSlot **partition_slot,
+ TupleConversionMap **child_to_parent_map)
{
PartitionDispatch *pd = proute->partition_dispatch_info;
Datum values[PARTITION_MAX_KEYS];
@@ -214,6 +230,9 @@ ExecFindPartition(ModifyTableState *mtstate,
TupleTableSlot *myslot = NULL;
MemoryContext oldcxt;
+ *parent_to_child_map = *child_to_parent_map = NULL;
+ *partition_slot = NULL;
+
/* use per-tuple context here to avoid leaking memory */
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
@@ -274,7 +293,8 @@ ExecFindPartition(ModifyTableState *mtstate,
if (partdesc->is_leaf[partidx])
{
- int result = -1;
+ int index = -1;
+ ResultRelInfo *result = NULL;
/*
* Get this leaf partition's index in the
@@ -285,7 +305,8 @@ ExecFindPartition(ModifyTableState *mtstate,
{
/* ResultRelInfo already built */
Assert(dispatch->indexes[partidx] <
proute->num_partitions);
- result = dispatch->indexes[partidx];
+ index = dispatch->indexes[partidx];
+ result = proute->partitions[index];
}
else
{
@@ -296,36 +317,58 @@ ExecFindPartition(ModifyTableState *mtstate,
*/
if (proute->subplan_resultrel_hash)
{
- ResultRelInfo *rri;
Oid partoid =
partdesc->oids[partidx];
- rri =
hash_search(proute->subplan_resultrel_hash,
-
&partoid, HASH_FIND, NULL);
+ result =
hash_search(proute->subplan_resultrel_hash,
+
&partoid, HASH_FIND, NULL);
- if (rri)
+ if (result)
{
- result =
proute->num_partitions++;
- dispatch->indexes[partidx] =
result;
+ index =
proute->num_partitions++;
+ dispatch->indexes[partidx] =
index;
/* Allocate more space in the
arrays, if required */
- if (result >=
proute->partitions_allocsize)
+ if (index >=
proute->partitions_allocsize)
ExecExpandRoutingArrays(proute);
/* Save here for later use. */
- proute->partitions[result] =
rri;
+ proute->partitions[index] =
result;
+
+ /*
+ * We need to make this result
rel "routable" if it's
+ * the first time is is being
used for routing. Also,
+ * we would've only checked if
the relation is a valid
+ * target for UPDATE when
creating this ResultRelInfo
+ * and now we're about to
insert the routed tuple into
+ * it, so we need to check if
it's a valid target for
+ * INSERT as well.
+ */
+ if
(!result->ri_PartitionReadyForRouting)
+ {
+
CheckValidResultRel(result, CMD_INSERT);
+
+ /*
+ * Also set up
information needed for routing
+ * tuples to the
partition.
+ */
+
ExecInitRoutingInfo(mtstate, estate, proute,
+
result, index);
+ }
}
}
/* We need to create a new one. */
- if (result < 0)
+ if (result == NULL)
{
MemoryContextSwitchTo(oldcxt);
result = ExecInitPartitionInfo(mtstate,
resultRelInfo,
proute, estate,
dispatch, partidx);
MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- Assert(result >= 0 && result <
proute->num_partitions);
+ Assert(result != NULL);
+ index = dispatch->indexes[partidx];
+ Assert(index >= 0 && index <
proute->num_partitions);
}
}
@@ -335,6 +378,26 @@ ExecFindPartition(ModifyTableState *mtstate,
MemoryContextSwitchTo(oldcxt);
ecxt->ecxt_scantuple = ecxt_scantuple_old;
+
+ /* Set the values of result maps and the slot if
needed. */
+ if (proute->parent_child_tupconv_maps)
+ {
+ *parent_to_child_map =
proute->parent_child_tupconv_maps[index];
+
+ /*
+ * If non-NULL, correponding tuple slot must
have been
+ * initialized for the partition.
+ */
+ if (*parent_to_child_map != NULL)
+ {
+ *partition_slot =
proute->partition_tuple_slots[index];
+ Assert(*partition_slot != NULL);
+ }
+ }
+
+ if (proute->child_parent_tupconv_maps)
+ *child_to_parent_map =
proute->child_parent_tupconv_maps[index];
+
return result;
}
else
@@ -374,6 +437,9 @@ ExecFindPartition(ModifyTableState *mtstate,
}
}
}
+
+ Assert(false);
+ return NULL; /* keep compiler quiet */
}
/*
@@ -475,9 +541,10 @@ ExecExpandRoutingArrays(PartitionTupleRouting *proute)
* ExecInitPartitionInfo
* Initialize ResultRelInfo and other information for a partition
* and store it in the next empty slot in proute's partitions
array.
- * Return the index of the array element.
+ *
+ * Returns the ResultRelInfo
*/
-static int
+static ResultRelInfo *
ExecInitPartitionInfo(ModifyTableState *mtstate,
ResultRelInfo *rootResultRelInfo,
PartitionTupleRouting *proute,
@@ -742,9 +809,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
*/
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
- TupleConversionMap *map;
+ TupleConversionMap *map = NULL;
- map = PartitionParentToChildMap(proute,
part_result_rel_index);
+ if (proute->parent_child_tupconv_maps)
+ map =
proute->parent_child_tupconv_maps[part_result_rel_index];
Assert(node->onConflictSet != NIL);
Assert(rootResultRelInfo->ri_onConflict != NULL);
@@ -849,14 +917,14 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
MemoryContextSwitchTo(oldContext);
- return part_result_rel_index;
+ return leaf_part_rri;
}
/*
* ExecInitRoutingInfo
* Set up information needed for routing tuples to a leaf partition
*/
-void
+static void
ExecInitRoutingInfo(ModifyTableState *mtstate,
EState *estate,
PartitionTupleRouting *proute,
diff --git a/src/backend/executor/nodeModifyTable.c
b/src/backend/executor/nodeModifyTable.c
index 840b98811f..18c55e2b19 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1697,46 +1697,22 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
TupleTableSlot *slot)
{
ModifyTable *node;
- int partidx;
ResultRelInfo *partrel;
HeapTuple tuple;
- TupleConversionMap *map;
+ TupleTableSlot *partition_slot = NULL;
+ TupleConversionMap *child_to_parent_map,
+ *parent_to_child_map;
/*
* Determine the target partition. If ExecFindPartition does not find a
- * partition after all, it doesn't return here; otherwise, the returned
- * value is to be used as an index into the arrays for the ResultRelInfo
- * and TupleConversionMap for the partition.
+ * partition after all, it doesn't return here.
*/
- partidx = ExecFindPartition(mtstate, targetRelInfo, proute, slot,
estate);
- Assert(partidx >= 0 && partidx < proute->num_partitions);
-
- Assert(proute->partitions[partidx] != NULL);
- /* Get the ResultRelInfo corresponding to the selected partition. */
- partrel = proute->partitions[partidx];
+ partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot,
estate,
+
&parent_to_child_map, &partition_slot,
+
&child_to_parent_map);
Assert(partrel != NULL);
/*
- * Check whether the partition is routable if we didn't yet
- *
- * Note: an UPDATE of a partition key invokes an INSERT that moves the
- * tuple to a new partition. This check would be applied to a subplan
- * partition of such an UPDATE that is chosen as the partition to route
- * the tuple to. The reason we do this check here rather than in
- * ExecSetupPartitionTupleRouting is to avoid aborting such an UPDATE
- * unnecessarily due to non-routable subplan partitions that may not be
- * chosen for update tuple movement after all.
- */
- if (!partrel->ri_PartitionReadyForRouting)
- {
- /* Verify the partition is a valid target for INSERT. */
- CheckValidResultRel(partrel, CMD_INSERT);
-
- /* Set up information needed for routing tuples to the
partition. */
- ExecInitRoutingInfo(mtstate, estate, proute, partrel, partidx);
- }
-
- /*
* Make it look like we are inserting into the partition.
*/
estate->es_result_relation_info = partrel;
@@ -1758,8 +1734,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
* to be ready to convert their result back to
tuplestore format.
*/
mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
- mtstate->mt_transition_capture->tcs_map =
-
PartitionChildToParentMap(proute, partidx);
+ mtstate->mt_transition_capture->tcs_map =
child_to_parent_map;
}
else
{
@@ -1772,23 +1747,17 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
}
}
if (mtstate->mt_oc_transition_capture != NULL)
- {
- mtstate->mt_oc_transition_capture->tcs_map =
-
PartitionChildToParentMap(proute, partidx);
- }
+ mtstate->mt_oc_transition_capture->tcs_map =
child_to_parent_map;
/*
* Convert the tuple, if necessary.
*/
- map = PartitionParentToChildMap(proute, partidx);
- if (map != NULL)
+ if (parent_to_child_map != NULL)
{
- TupleTableSlot *new_slot;
- Assert(proute->partition_tuple_slots != NULL &&
- proute->partition_tuple_slots[partidx] != NULL);
- new_slot = proute->partition_tuple_slots[partidx];
- slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
+ Assert(partition_slot != NULL);
+ slot = execute_attr_map_slot(parent_to_child_map->attrMap, slot,
+
partition_slot);
}
/* Initialize information needed to handle ON CONFLICT DO UPDATE. */
diff --git a/src/include/executor/execPartition.h
b/src/include/executor/execPartition.h
index 45d5f6a8d0..7c8314362c 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -122,20 +122,6 @@ typedef struct PartitionTupleRouting
} PartitionTupleRouting;
/*
- * Accessor macros for tuple conversion maps contained in
- * PartitionTupleRouting. Beware of multiple evaluations of p!
- */
-#define PartitionChildToParentMap(p, i) \
- ((p)->child_parent_tupconv_maps != NULL ? \
- (p)->child_parent_tupconv_maps[(i)] : \
- NULL)
-
-#define PartitionParentToChildMap(p, i) \
- ((p)->parent_child_tupconv_maps != NULL ? \
- (p)->parent_child_tupconv_maps[(i)] : \
- NULL)
-
-/*
* PartitionedRelPruningData - Per-partitioned-table data for run-time pruning
* of partitions. For a multilevel partitioned table, we have one of these
* for the topmost partition plus one for each non-leaf child partition.
@@ -223,20 +209,14 @@ typedef struct PartitionPruneState
extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState
*mtstate,
Relation rel);
-extern int ExecFindPartition(ModifyTableState *mtstate,
+extern ResultRelInfo *ExecFindPartition(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
PartitionTupleRouting *proute,
TupleTableSlot *slot,
- EState *estate);
-extern ResultRelInfo *ExecGetPartitionInfo(ModifyTableState *mtstate,
- ResultRelInfo *resultRelInfo,
- PartitionTupleRouting *proute,
- EState *estate, int partidx);
-extern void ExecInitRoutingInfo(ModifyTableState *mtstate,
- EState *estate,
- PartitionTupleRouting *proute,
- ResultRelInfo *partRelInfo,
- int partidx);
+ EState *estate,
+ TupleConversionMap **parent_to_child_map,
+ TupleTableSlot **partition_slot,
+ TupleConversionMap **child_to_parent_map);
extern void ExecCleanupTupleRouting(ModifyTableState *mtstate,
PartitionTupleRouting *proute);
extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate,
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b45972682f..2b5e71b843 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2528,7 +2528,7 @@ CopyFrom(CopyState cstate)
* CopyFrom tuple routing.
*/
if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- proute = ExecSetupPartitionTupleRouting(NULL, cstate->rel);
+ proute = ExecSetupPartitionTupleRouting(mtstate, cstate->rel);
/*
* It's more efficient to prepare a bunch of tuples for insertion, and
diff --git a/src/backend/executor/execPartition.c
b/src/backend/executor/execPartition.c
index 4b27874f01..0978a55f48 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -66,6 +66,98 @@ typedef struct PartitionDispatchData
int indexes[FLEXIBLE_ARRAY_MEMBER];
} PartitionDispatchData;
+/*-----------------------
+ * PartitionTupleRouting - Encapsulates all information required to
+ * route a tuple inserted into a partitioned table to one of its leaf
+ * partitions
+ *
+ * partition_root The partitioned table that's the target
of the
+ * command.
+ *
+ * partition_dispatch_info Array of 'dispatch_allocsize' elements
containing
+ * a pointer to a
PartitionDispatch objects for every
+ * partitioned table
touched by tuple routing. The
+ * entry for the target
partitioned table is *always*
+ * present in the 0th
element of this array. See
+ * comment for
PartitionDispatchData->indexes for
+ * details on how this
array is indexed.
+ *
+ * num_dispatch The current number of items
stored in the
+ *
'partition_dispatch_info' array. Also serves as
+ * the index of the next
free array element for new
+ * PartitionDispatch which
need to be stored.
+ *
+ * dispatch_allocsize The current allocated size of the
+ *
'partition_dispatch_info' array.
+ *
+ * partitions Array of 'partitions_allocsize' elements
+ * containing pointers to
a ResultRelInfos of all
+ * leaf partitions touched
by tuple routing. Some of
+ * these are pointers to
ResultRelInfos which are
+ * borrowed out of
'subplan_resultrel_hash'. The
+ * remainder have been
built especially for tuple
+ * routing. See comment
for
+ *
PartitionDispatchData->indexes for details on how
+ * this array is indexed.
+ *
+ * num_partitions The current number of items stored in
the
+ * 'partitions' array.
Also serves as the index of
+ * the next free array
element for new ResultRelInfos
+ * which need to be stored.
+ *
+ * partitions_allocsize The current allocated size of the
'partitions'
+ * array. Also, if
they're non-NULL, marks the size
+ * of the
'parent_child_tupconv_maps',
+ *
'child_parent_tupconv_maps',
+ *
'child_parent_map_not_required' and
+ * 'partition_tuple_slots'
arrays.
+ *
+ * parent_child_tupconv_maps Array of partitions_allocsize elements
+ * containing information
on how to convert tuples of
+ * partition_root's
rowtype to the rowtype of the
+ * corresponding partition
as stored in 'partitions',
+ * or NULL if no
conversion is required. The entire
+ * array is only allocated
when the first conversion
+ * map needs to stored.
When not allocated it's set
+ * to NULL.
+ *
+ * partition_tuple_slots Array of TupleTableSlot objects; if non-NULL,
+ * contains one entry for
every leaf partition,
+ * of which only those of
the leaf partitions
+ * whose attribute numbers
differ from the root
+ * parent have a non-NULL
value. NULL if all of
+ * the partitions
encountered by a given command
+ * happen to have same
rowtype as the root parent
+ *
+ * child_parent_tupconv_maps As 'parent_child_tupconv_maps' but stores
+ * conversion maps to
translate partition tuples into
+ * partition_root's
rowtype, needed if transition
+ * capture is active
+ *
+ * Note: The following fields are used only when UPDATE ends up needing to
+ * do tuple routing.
+ *
+ * subplan_resultrel_hash Hash table to store subplan ResultRelInfos by
Oid.
+ * This is used to cache
ResultRelInfos from subplans
+ * of a ModifyTable node.
Some of these may be
+ * useful for tuple
routing to save having to build
+ * duplicates.
+ *-----------------------
+ */
+typedef struct PartitionTupleRouting
+{
+ Relation partition_root;
+ PartitionDispatch *partition_dispatch_info;
+ int num_dispatch;
+ int dispatch_allocsize;
+ ResultRelInfo **partitions;
+ int num_partitions;
+ int partitions_allocsize;
+ TupleConversionMap **parent_child_tupconv_maps;
+ TupleConversionMap **child_parent_tupconv_maps;
+ TupleTableSlot **partition_tuple_slots;
+ HTAB *subplan_resultrel_hash;
+} PartitionTupleRouting;
static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate,
PartitionTupleRouting *proute);
@@ -179,12 +271,12 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
Relation rel)
if (node && node->operation == CMD_UPDATE)
{
ExecHashSubPlanResultRelsByOid(mtstate, proute);
- proute->root_tuple_slot =
MakeTupleTableSlot(RelationGetDescr(rel));
+ mtstate->mt_root_tuple_slot =
MakeTupleTableSlot(RelationGetDescr(rel));
}
else
{
proute->subplan_resultrel_hash = NULL;
- proute->root_tuple_slot = NULL;
+ mtstate->mt_root_tuple_slot = NULL;
}
return proute;
@@ -1175,10 +1267,6 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate,
ExecCloseIndices(resultRelInfo);
heap_close(resultRelInfo->ri_RelationDesc, NoLock);
}
-
- /* Release the standalone partition tuple descriptors, if any */
- if (proute->root_tuple_slot)
- ExecDropSingleTupleTableSlot(proute->root_tuple_slot);
}
/* ----------------
diff --git a/src/backend/executor/nodeModifyTable.c
b/src/backend/executor/nodeModifyTable.c
index 18c55e2b19..390191bdc8 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1161,8 +1161,8 @@ lreplace:;
Assert(map_index >= 0 && map_index <
mtstate->mt_nplans);
tupconv_map = tupconv_map_for_subplan(mtstate,
map_index);
if (tupconv_map != NULL)
- slot =
execute_attr_map_slot(tupconv_map->attrMap,
-
slot, proute->root_tuple_slot);
+ slot =
execute_attr_map_slot(tupconv_map->attrMap, slot,
+
mtstate->mt_root_tuple_slot);
/*
* Prepare for tuple routing, making it look like we're
inserting
@@ -2616,6 +2616,10 @@ ExecEndModifyTable(ModifyTableState *node)
*/
for (i = 0; i < node->mt_nplans; i++)
ExecEndNode(node->mt_plans[i]);
+
+ /* Release the standalone partition tuple descriptors, if any */
+ if (node->mt_root_tuple_slot)
+ ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
}
void
diff --git a/src/include/executor/execPartition.h
b/src/include/executor/execPartition.h
index 7c8314362c..91886f1b19 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -18,108 +18,9 @@
#include "nodes/plannodes.h"
#include "partitioning/partprune.h"
-/* See execPartition.c for the definition. */
+/* See execPartition.c for the definitions. */
typedef struct PartitionDispatchData *PartitionDispatch;
-
-/*-----------------------
- * PartitionTupleRouting - Encapsulates all information required to
- * route a tuple inserted into a partitioned table to one of its leaf
- * partitions
- *
- * partition_root The partitioned table that's the target
of the
- * command.
- *
- * partition_dispatch_info Array of 'dispatch_allocsize' elements
containing
- * a pointer to a
PartitionDispatch objects for every
- * partitioned table
touched by tuple routing. The
- * entry for the target
partitioned table is *always*
- * present in the 0th
element of this array. See
- * comment for
PartitionDispatchData->indexes for
- * details on how this
array is indexed.
- *
- * num_dispatch The current number of items
stored in the
- *
'partition_dispatch_info' array. Also serves as
- * the index of the next
free array element for new
- * PartitionDispatch which
need to be stored.
- *
- * dispatch_allocsize The current allocated size of the
- *
'partition_dispatch_info' array.
- *
- * partitions Array of 'partitions_allocsize' elements
- * containing pointers to
a ResultRelInfos of all
- * leaf partitions touched
by tuple routing. Some of
- * these are pointers to
ResultRelInfos which are
- * borrowed out of
'subplan_resultrel_hash'. The
- * remainder have been
built especially for tuple
- * routing. See comment
for
- *
PartitionDispatchData->indexes for details on how
- * this array is indexed.
- *
- * num_partitions The current number of items stored in
the
- * 'partitions' array.
Also serves as the index of
- * the next free array
element for new ResultRelInfos
- * which need to be stored.
- *
- * partitions_allocsize The current allocated size of the
'partitions'
- * array. Also, if
they're non-NULL, marks the size
- * of the
'parent_child_tupconv_maps',
- *
'child_parent_tupconv_maps',
- *
'child_parent_map_not_required' and
- * 'partition_tuple_slots'
arrays.
- *
- * parent_child_tupconv_maps Array of partitions_allocsize elements
- * containing information
on how to convert tuples of
- * partition_root's
rowtype to the rowtype of the
- * corresponding partition
as stored in 'partitions',
- * or NULL if no
conversion is required. The entire
- * array is only allocated
when the first conversion
- * map needs to stored.
When not allocated it's set
- * to NULL.
- *
- * partition_tuple_slots Array of TupleTableSlot objects; if non-NULL,
- * contains one entry for
every leaf partition,
- * of which only those of
the leaf partitions
- * whose attribute numbers
differ from the root
- * parent have a non-NULL
value. NULL if all of
- * the partitions
encountered by a given command
- * happen to have same
rowtype as the root parent
- *
- * child_parent_tupconv_maps As 'parent_child_tupconv_maps' but stores
- * conversion maps to
translate partition tuples into
- * partition_root's
rowtype, needed if transition
- * capture is active
- *
- * Note: The following fields are used only when UPDATE ends up needing to
- * do tuple routing.
- *
- * subplan_resultrel_hash Hash table to store subplan ResultRelInfos by
Oid.
- * This is used to cache
ResultRelInfos from subplans
- * of a ModifyTable node.
Some of these may be
- * useful for tuple
routing to save having to build
- * duplicates.
- *
- * root_tuple_slot During UPDATE tuple routing, this tuple
slot is
- * used to transiently
store a tuple using the root
- * table's rowtype after
converting it from the
- * tuple's source leaf
partition's rowtype. That is,
- * if leaf partition's
rowtype is different.
- *-----------------------
- */
-typedef struct PartitionTupleRouting
-{
- Relation partition_root;
- PartitionDispatch *partition_dispatch_info;
- int num_dispatch;
- int dispatch_allocsize;
- ResultRelInfo **partitions;
- int num_partitions;
- int partitions_allocsize;
- TupleConversionMap **parent_child_tupconv_maps;
- TupleConversionMap **child_parent_tupconv_maps;
- TupleTableSlot **partition_tuple_slots;
- TupleTableSlot *root_tuple_slot;
- HTAB *subplan_resultrel_hash;
-} PartitionTupleRouting;
+typedef struct PartitionTupleRouting PartitionTupleRouting;
/*
* PartitionedRelPruningData - Per-partitioned-table data for run-time pruning
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 880a03e4e4..73ecc20074 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1084,6 +1084,14 @@ typedef struct ModifyTableState
/* Per plan map for tuple conversion from child to root */
TupleConversionMap **mt_per_subplan_tupconv_maps;
+
+ /*
+ * During UPDATE tuple routing, this tuple slot is used to transiently
+ * store a tuple using the root table's rowtype after converting it from
+ * the tuple's source leaf partition's rowtype. That is, if leaf
+ * partition's rowtype is different.
+ */
+ TupleTableSlot *mt_root_tuple_slot;
} ModifyTableState;
/* ----------------