aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execPartition.c
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2019-03-06 15:43:33 -0800
committerAndres Freund <andres@anarazel.de>2019-03-06 15:43:33 -0800
commit277cb789836b5ddf81aabb80c2058268c70e2f36 (patch)
tree92b675a011892e1317b095ed25c4c6557116ad07 /src/backend/executor/execPartition.c
parentd16a74c20ce3485d43902b0b1fb8ec1c11ec84a5 (diff)
downloadpostgresql-277cb789836b5ddf81aabb80c2058268c70e2f36.tar.gz
postgresql-277cb789836b5ddf81aabb80c2058268c70e2f36.zip
Don't reuse slots between root and partition in ON CONFLICT ... UPDATE.
Until now the the slot to store the conflicting tuple, and the result of the ON CONFLICT SET, where reused between partitions. That necessitated changing slots descriptor when switching partitions. Besides the overhead of switching descriptors on a slot (which requires memory allocations and prevents JITing), that's importantly also problematic for tableam. There individual partitions might belong to different tableams, needing different kinds of slots. In passing also fix ExecOnConflictUpdate to clear the existing slot at exit. Otherwise that slot could continue to hold a pin till the query ends, which could be far too long if the input data set is large, and there's no further conflicts. While previously also problematic, it's now more important as there will be more such slots when partitioned. Author: Andres Freund Reviewed-By: Robert Haas, David Rowley Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
Diffstat (limited to 'src/backend/executor/execPartition.c')
-rw-r--r--src/backend/executor/execPartition.c52
1 files changed, 38 insertions, 14 deletions
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index e41801662b3..4491ee69912 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -723,28 +723,55 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
TupleConversionMap *map;
+ TupleDesc leaf_desc;
map = leaf_part_rri->ri_PartitionInfo->pi_RootToPartitionMap;
+ leaf_desc = RelationGetDescr(leaf_part_rri->ri_RelationDesc);
Assert(node->onConflictSet != NIL);
Assert(rootResultRelInfo->ri_onConflict != NULL);
+ leaf_part_rri->ri_onConflict = makeNode(OnConflictSetState);
+
+ /*
+ * Need a separate existing slot for each partition, as the
+ * partition could be of a different AM, even if the tuple
+ * descriptors match.
+ */
+ leaf_part_rri->ri_onConflict->oc_Existing =
+ ExecInitExtraTupleSlot(mtstate->ps.state,
+ leaf_desc,
+ &TTSOpsBufferHeapTuple);
+
/*
* If the partition's tuple descriptor matches exactly the root
- * parent (the common case), we can simply re-use the parent's ON
+ * parent (the common case), we can re-use most of the parent's ON
* CONFLICT SET state, skipping a bunch of work. Otherwise, we
* need to create state specific to this partition.
*/
if (map == NULL)
- leaf_part_rri->ri_onConflict = rootResultRelInfo->ri_onConflict;
+ {
+ /*
+ * It's safe to reuse these from the partition root, as we
+ * only process one tuple at a time (therefore we won't
+ * overwrite needed data in slots), and the results of
+ * projections are independent of the underlying
+ * storage. Projections and where clauses themselves don't
+ * store state / are independent of the underlying storage.
+ */
+ leaf_part_rri->ri_onConflict->oc_ProjSlot =
+ rootResultRelInfo->ri_onConflict->oc_ProjSlot;
+ leaf_part_rri->ri_onConflict->oc_ProjInfo =
+ rootResultRelInfo->ri_onConflict->oc_ProjInfo;
+ leaf_part_rri->ri_onConflict->oc_WhereClause =
+ rootResultRelInfo->ri_onConflict->oc_WhereClause;
+ }
else
{
List *onconflset;
TupleDesc tupDesc;
bool found_whole_row;
- leaf_part_rri->ri_onConflict = makeNode(OnConflictSetState);
-
/*
* Translate expressions in onConflictSet to account for
* different attribute numbers. For that, map partition
@@ -778,20 +805,17 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
/* Finally, adjust this tlist to match the partition. */
onconflset = adjust_partition_tlist(onconflset, map);
- /*
- * Build UPDATE SET's projection info. The user of this
- * projection is responsible for setting the slot's tupdesc!
- * We set aside a tupdesc that's good for the common case of a
- * partition that's tupdesc-equal to the partitioned table;
- * partitions of different tupdescs must generate their own.
- */
+ /* create the tuple slot for the UPDATE SET projection */
tupDesc = ExecTypeFromTL(onconflset);
- ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
+ leaf_part_rri->ri_onConflict->oc_ProjSlot =
+ ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
+ &TTSOpsVirtual);
+
+ /* build UPDATE SET projection state */
leaf_part_rri->ri_onConflict->oc_ProjInfo =
ExecBuildProjectionInfo(onconflset, econtext,
- mtstate->mt_conflproj,
+ leaf_part_rri->ri_onConflict->oc_ProjSlot,
&mtstate->ps, partrelDesc);
- leaf_part_rri->ri_onConflict->oc_ProjTupdesc = tupDesc;
/*
* If there is a WHERE clause, initialize state where it will