Thanks for the review. On 2018/08/17 15:00, Amit Khandekar wrote: > Thanks for the patch. I did some review of the patch (needs a rebase). > Below are my comments. > > @@ -1936,12 +1936,11 @@ ExecPartitionCheckEmitError(ResultRelInfo > *resultRelInfo, > + /* Input slot might be of a partition, which has a fixed tupdesc. */ > + slot = MakeTupleTableSlot(tupdesc); > if (map != NULL) > - { > tuple = do_convert_tuple(tuple, map); > - ExecSetSlotDescriptor(slot, tupdesc); > - ExecStoreTuple(tuple, slot, InvalidBuffer, false); > - } > + ExecStoreTuple(tuple, slot, InvalidBuffer, false); > > Both MakeTupleTableSlot() and ExecStoreTuple() can be inside the (map > != NULL) if condition. > This also applies for similar changes in ExecConstraints() and > ExecWithCheckOptions().
Ah, okay. I guess that means we'll allocate a new slot here only if we had to switch to a partition-specific slot in the first place. > + * Initialize an empty slot that will be used to manipulate tuples of any > + * this partition's rowtype. > of any this => of this > > + * Initialized the array where these slots are stored, if not already > Initialized => Initialize Fixed. > + proute->partition_tuple_slots_alloced = > + lappend(proute->partition_tuple_slots_alloced, > + proute->partition_tuple_slots[partidx]); > > Instead of doing the above, I think we can collect those slots in > estate->es_tupleTable using ExecInitExtraTupleSlot() so that they > don't have to be explicitly dropped in ExecCleanupTupleRouting(). And > also then we won't require the new field > proute->partition_tuple_slots_alloced. Although I was slightly uncomfortable of the idea at first, thinking that it's not good for tuple routing specific resources to be released by generic executor code, doesn't seem too bad to do it the way you suggest. Attached updated patch. By the way, I think it might be a good idea to try to merge this patch with the effort in the following thread. * Reduce partition tuple routing overheads * https://commitfest.postgresql.org/19/1690/ Thanks, Amit
From a543b15f83f5e1adaef2a46de59022b0b21711e4 Mon Sep 17 00:00:00 2001 From: amit <amitlangot...@gmail.com> Date: Thu, 28 Jun 2018 15:47:47 +0900 Subject: [PATCH v2] Allocate dedicated slots of partition tuple conversion Currently there is just one slot called partition_tuple_slot and we do ExecSetSlotDescriptor every time a tuple is inserted into a partition that requires tuple conversion. That's excessively inefficient during bulk inserts. Fix that by allocating a dedicated slot for each partitions that requires it. --- src/backend/commands/copy.c | 17 +++++++++---- src/backend/executor/execMain.c | 24 +++++++++++++++--- src/backend/executor/execPartition.c | 45 +++++++++++++++++++++++----------- src/backend/executor/nodeModifyTable.c | 27 ++++++++++++-------- src/include/executor/execPartition.h | 13 ++++++---- 5 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 9bc67ce60f..f61db3e173 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2690,6 +2690,7 @@ CopyFrom(CopyState cstate) if (proute) { int leaf_part_index; + TupleConversionMap *map; /* * Away we go ... If we end up not finding a partition after all, @@ -2864,11 +2865,17 @@ CopyFrom(CopyState cstate) * partition rowtype. Don't free the already stored tuple as it * may still be required for a multi-insert batch. */ - tuple = ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[leaf_part_index], - tuple, - proute->partition_tuple_slot, - &slot, - false); + map = proute->parent_child_tupconv_maps[leaf_part_index]; + if (map != NULL) + { + TupleTableSlot *new_slot; + + Assert(proute->partition_tuple_slots != NULL && + proute->partition_tuple_slots[leaf_part_index] != NULL); + new_slot = proute->partition_tuple_slots[leaf_part_index]; + tuple = ConvertPartitionTupleSlot(map, tuple, new_slot, &slot, + false); + } tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index c583e020a0..415670ed7e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1938,8 +1938,12 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, gettext_noop("could not convert row type")); if (map != NULL) { + /* + * Partition-specific slot's tupdesc can't be changed, so allocate + * a new one. + */ + slot = MakeTupleTableSlot(tupdesc); tuple = do_convert_tuple(tuple, map); - ExecSetSlotDescriptor(slot, tupdesc); ExecStoreTuple(tuple, slot, InvalidBuffer, false); } } @@ -2017,8 +2021,12 @@ ExecConstraints(ResultRelInfo *resultRelInfo, gettext_noop("could not convert row type")); if (map != NULL) { + /* + * Partition-specific slot's tupdesc can't be changed, + * so allocate a new one. + */ + slot = MakeTupleTableSlot(tupdesc); tuple = do_convert_tuple(tuple, map); - ExecSetSlotDescriptor(slot, tupdesc); ExecStoreTuple(tuple, slot, InvalidBuffer, false); } } @@ -2065,8 +2073,12 @@ ExecConstraints(ResultRelInfo *resultRelInfo, gettext_noop("could not convert row type")); if (map != NULL) { + /* + * Partition-specific slot's tupdesc can't be changed, + * so allocate a new one. + */ + slot = MakeTupleTableSlot(tupdesc); tuple = do_convert_tuple(tuple, map); - ExecSetSlotDescriptor(slot, tupdesc); ExecStoreTuple(tuple, slot, InvalidBuffer, false); } } @@ -2171,8 +2183,12 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, gettext_noop("could not convert row type")); if (map != NULL) { + /* + * Partition-specific slot's tupdesc can't be + * changed, so allocate a new one. + */ + slot = MakeTupleTableSlot(tupdesc); tuple = do_convert_tuple(tuple, map); - ExecSetSlotDescriptor(slot, tupdesc); ExecStoreTuple(tuple, slot, InvalidBuffer, false); } } diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 1a9943c3aa..2af7599152 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -114,17 +114,9 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) * We need an additional tuple slot for storing transient tuples that * are converted to the root table descriptor. */ - proute->root_tuple_slot = MakeTupleTableSlot(NULL); + proute->root_tuple_slot = MakeTupleTableSlot(RelationGetDescr(rel)); } - /* - * Initialize an empty slot that will be used to manipulate tuples of any - * given partition's rowtype. It is attached to the caller-specified node - * (such as ModifyTableState) and released when the node finishes - * processing. - */ - proute->partition_tuple_slot = MakeTupleTableSlot(NULL); - i = 0; foreach(cell, leaf_parts) { @@ -709,6 +701,35 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, gettext_noop("could not convert row type")); /* + * If partition has different rowtype than root parent, initialize a slot + * dedicated to storing this partition's tuples. The slot is used for + * various operations that are applied to tuple after routing, such as + * checking constraints. + */ + if (proute->parent_child_tupconv_maps[partidx] != NULL) + { + Relation partrel = partRelInfo->ri_RelationDesc; + + /* + * Initialize the array in proute where these slots are stored, if not + * already done. + */ + if (proute->partition_tuple_slots == NULL) + proute->partition_tuple_slots = (TupleTableSlot **) + palloc0(proute->num_partitions * + sizeof(TupleTableSlot *)); + + /* + * Initialize the slot itself setting its descriptor to this + * partition's TupleDesc; TupleDesc reference will be released + * at the end of the command. + */ + proute->partition_tuple_slots[partidx] = + ExecInitExtraTupleSlot(estate, + RelationGetDescr(partrel)); + } + + /* * If the partition is a foreign table, let the FDW init itself for * routing tuples to the partition. */ @@ -801,8 +822,7 @@ ConvertPartitionTupleSlot(TupleConversionMap *map, TupleTableSlot **p_my_slot, bool shouldFree) { - if (!map) - return tuple; + Assert(map != NULL && new_slot != NULL); tuple = do_convert_tuple(tuple, map); @@ -811,7 +831,6 @@ ConvertPartitionTupleSlot(TupleConversionMap *map, */ *p_my_slot = new_slot; Assert(new_slot != NULL); - ExecSetSlotDescriptor(new_slot, map->outdesc); ExecStoreTuple(tuple, new_slot, InvalidBuffer, shouldFree); return tuple; @@ -885,8 +904,6 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate, /* Release the standalone partition tuple descriptors, if any */ if (proute->root_tuple_slot) ExecDropSingleTupleTableSlot(proute->root_tuple_slot); - if (proute->partition_tuple_slot) - ExecDropSingleTupleTableSlot(proute->partition_tuple_slot); } /* diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index d8d89c7983..e4b2d374b4 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1161,11 +1161,12 @@ lreplace:; map_index = resultRelInfo - mtstate->resultRelInfo; Assert(map_index >= 0 && map_index < mtstate->mt_nplans); tupconv_map = tupconv_map_for_subplan(mtstate, map_index); - tuple = ConvertPartitionTupleSlot(tupconv_map, - tuple, - proute->root_tuple_slot, - &slot, - true); + if (tupconv_map != NULL) + tuple = ConvertPartitionTupleSlot(tupconv_map, + tuple, + proute->root_tuple_slot, + &slot, + true); /* * Prepare for tuple routing, making it look like we're inserting @@ -1703,6 +1704,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, int partidx; ResultRelInfo *partrel; HeapTuple tuple; + TupleConversionMap *map; /* * Determine the target partition. If ExecFindPartition does not find a @@ -1790,11 +1792,16 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, /* * Convert the tuple, if necessary. */ - ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[partidx], - tuple, - proute->partition_tuple_slot, - &slot, - true); + map = proute->parent_child_tupconv_maps[partidx]; + if (map != NULL) + { + TupleTableSlot *new_slot; + + Assert(proute->partition_tuple_slots != NULL && + proute->partition_tuple_slots[partidx] != NULL); + new_slot = proute->partition_tuple_slots[partidx]; + ConvertPartitionTupleSlot(map, tuple, new_slot, &slot, true); + } /* Initialize information needed to handle ON CONFLICT DO UPDATE. */ Assert(mtstate != NULL); diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index f6cd842cc9..c2a2dc563e 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -86,10 +86,13 @@ typedef struct PartitionDispatchData *PartitionDispatch; * element of this array has the index into the * corresponding partition in partitions array. * num_subplan_partition_offsets Length of 'subplan_partition_offsets' array - * partition_tuple_slot TupleTableSlot to be used to manipulate any - * given leaf partition's rowtype after that - * partition is chosen for insertion by - * tuple-routing. + * 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 * root_tuple_slot TupleTableSlot to be used to transiently hold * copy of a tuple that's being moved across * partitions in the root partitioned table's @@ -108,7 +111,7 @@ typedef struct PartitionTupleRouting bool *child_parent_map_not_required; int *subplan_partition_offsets; int num_subplan_partition_offsets; - TupleTableSlot *partition_tuple_slot; + TupleTableSlot **partition_tuple_slots; TupleTableSlot *root_tuple_slot; } PartitionTupleRouting; -- 2.11.0