aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execPartition.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-04-06 15:56:55 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-04-06 15:57:11 -0400
commitc5b7ba4e67aeb5d6f824b74f94114d99ed6e42b7 (patch)
tree49af7ec3889470f92b7eab567ad990028bae6177 /src/backend/executor/execPartition.c
parenta3740c48eb2f91663c7c06c948dfcfb6493d2588 (diff)
downloadpostgresql-c5b7ba4e67aeb5d6f824b74f94114d99ed6e42b7.tar.gz
postgresql-c5b7ba4e67aeb5d6f824b74f94114d99ed6e42b7.zip
Postpone some stuff out of ExecInitModifyTable.
Arrange to do some things on-demand, rather than immediately during executor startup, because there's a fair chance of never having to do them at all: * Don't open result relations' indexes until needed. * Don't initialize partition tuple routing, nor the child-to-root tuple conversion map, until needed. This wins in UPDATEs on partitioned tables when only some of the partitions will actually receive updates; with larger partition counts the savings is quite noticeable. Also, we can remove some sketchy heuristics in ExecInitModifyTable about whether to set up tuple routing. Also, remove execPartition.c's private hash table tracking which partitions were already opened by the ModifyTable node. Instead use the hash added to ModifyTable itself by commit 86dc90056. To allow lazy computation of the conversion maps, we now set ri_RootResultRelInfo in all child ResultRelInfos. We formerly set it only in some, not terribly well-defined, cases. This has user-visible side effects in that now more error messages refer to the root relation instead of some partition (and provide error data in the root's column order, too). It looks to me like this is a strict improvement in consistency, so I don't have a problem with the output changes visible in this commit. Extracted from a larger patch, which seemed to me to be too messy to push in one commit. Amit Langote, reviewed at different times by Heikki Linnakangas and myself Discussion: https://postgr.es/m/CA+HiwqG7ZruBmmih3wPsBZ4s0H2EhywrnXEduckY5Hr3fWzPWA@mail.gmail.com
Diffstat (limited to 'src/backend/executor/execPartition.c')
-rw-r--r--src/backend/executor/execPartition.c183
1 files changed, 48 insertions, 135 deletions
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 558060e080f..99780ebb961 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -66,11 +66,17 @@
*
* partitions
* Array of 'max_partitions' elements containing a pointer to a
- * ResultRelInfo for every leaf partitions touched by tuple routing.
+ * ResultRelInfo for every leaf partition touched by tuple routing.
* Some of these are pointers to ResultRelInfos which are borrowed out of
- * 'subplan_resultrel_htab'. The remainder have been built especially
- * for tuple routing. See comment for PartitionDispatchData->indexes for
- * details on how this array is indexed.
+ * the owning ModifyTableState node. The remainder have been built
+ * especially for tuple routing. See comment for
+ * PartitionDispatchData->indexes for details on how this array is
+ * indexed.
+ *
+ * is_borrowed_rel
+ * Array of 'max_partitions' booleans recording whether a given entry
+ * in 'partitions' is a ResultRelInfo pointer borrowed from the owning
+ * ModifyTableState node, rather than being built here.
*
* num_partitions
* The current number of items stored in the 'partitions' array. Also
@@ -80,12 +86,6 @@
* max_partitions
* The current allocated size of the 'partitions' array.
*
- * subplan_resultrel_htab
- * Hash table to store subplan ResultRelInfos by Oid. This is used to
- * cache ResultRelInfos from targets of an UPDATE ModifyTable node;
- * NULL in other cases. Some of these may be useful for tuple routing
- * to save having to build duplicates.
- *
* memcxt
* Memory context used to allocate subsidiary structs.
*-----------------------
@@ -98,9 +98,9 @@ struct PartitionTupleRouting
int num_dispatch;
int max_dispatch;
ResultRelInfo **partitions;
+ bool *is_borrowed_rel;
int num_partitions;
int max_partitions;
- HTAB *subplan_resultrel_htab;
MemoryContext memcxt;
};
@@ -153,16 +153,7 @@ typedef struct PartitionDispatchData
int indexes[FLEXIBLE_ARRAY_MEMBER];
} PartitionDispatchData;
-/* struct to hold result relations coming from UPDATE subplans */
-typedef struct SubplanResultRelHashElem
-{
- Oid relid; /* hash key -- must be first */
- ResultRelInfo *rri;
-} SubplanResultRelHashElem;
-
-static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate,
- PartitionTupleRouting *proute);
static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
EState *estate, PartitionTupleRouting *proute,
PartitionDispatch dispatch,
@@ -173,7 +164,8 @@ static void ExecInitRoutingInfo(ModifyTableState *mtstate,
PartitionTupleRouting *proute,
PartitionDispatch dispatch,
ResultRelInfo *partRelInfo,
- int partidx);
+ int partidx,
+ bool is_borrowed_rel);
static PartitionDispatch ExecInitPartitionDispatchInfo(EState *estate,
PartitionTupleRouting *proute,
Oid partoid, PartitionDispatch parent_pd,
@@ -215,11 +207,9 @@ static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
* it should be estate->es_query_cxt.
*/
PartitionTupleRouting *
-ExecSetupPartitionTupleRouting(EState *estate, ModifyTableState *mtstate,
- Relation rel)
+ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
{
PartitionTupleRouting *proute;
- ModifyTable *node = mtstate ? (ModifyTable *) mtstate->ps.plan : NULL;
/*
* Here we attempt to expend as little effort as possible in setting up
@@ -241,17 +231,6 @@ ExecSetupPartitionTupleRouting(EState *estate, ModifyTableState *mtstate,
ExecInitPartitionDispatchInfo(estate, proute, RelationGetRelid(rel),
NULL, 0, NULL);
- /*
- * If performing an UPDATE with tuple routing, we can reuse partition
- * sub-plan result rels. We build a hash table to map the OIDs of
- * partitions present in mtstate->resultRelInfo to their ResultRelInfos.
- * Every time a tuple is routed to a partition that we've yet to set the
- * ResultRelInfo for, before we go to the trouble of making one, we check
- * for a pre-made one in the hash table.
- */
- if (node && node->operation == CMD_UPDATE)
- ExecHashSubPlanResultRelsByOid(mtstate, proute);
-
return proute;
}
@@ -351,7 +330,6 @@ ExecFindPartition(ModifyTableState *mtstate,
is_leaf = partdesc->is_leaf[partidx];
if (is_leaf)
{
-
/*
* We've reached the leaf -- hurray, we're done. Look to see if
* we've already got a ResultRelInfo for this partition.
@@ -364,42 +342,33 @@ ExecFindPartition(ModifyTableState *mtstate,
}
else
{
- bool found = false;
-
/*
- * We have not yet set up a ResultRelInfo for this partition,
- * but if we have a subplan hash table, we might have one
- * there. If not, we'll have to create one.
+ * If the partition is known in the owning ModifyTableState
+ * node, we can re-use that ResultRelInfo instead of creating
+ * a new one with ExecInitPartitionInfo().
*/
- if (proute->subplan_resultrel_htab)
+ rri = ExecLookupResultRelByOid(mtstate,
+ partdesc->oids[partidx],
+ true, false);
+ if (rri)
{
- Oid partoid = partdesc->oids[partidx];
- SubplanResultRelHashElem *elem;
+ /* Verify this ResultRelInfo allows INSERTs */
+ CheckValidResultRel(rri, CMD_INSERT);
- elem = hash_search(proute->subplan_resultrel_htab,
- &partoid, HASH_FIND, NULL);
- if (elem)
- {
- found = true;
- rri = elem->rri;
-
- /* Verify this ResultRelInfo allows INSERTs */
- CheckValidResultRel(rri, CMD_INSERT);
-
- /*
- * Initialize information needed to insert this and
- * subsequent tuples routed to this partition.
- */
- ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
- rri, partidx);
- }
+ /*
+ * Initialize information needed to insert this and
+ * subsequent tuples routed to this partition.
+ */
+ ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
+ rri, partidx, true);
}
-
- /* We need to create a new one. */
- if (!found)
+ else
+ {
+ /* We need to create a new one. */
rri = ExecInitPartitionInfo(mtstate, estate, proute,
dispatch,
rootResultRelInfo, partidx);
+ }
}
Assert(rri != NULL);
@@ -510,50 +479,6 @@ ExecFindPartition(ModifyTableState *mtstate,
}
/*
- * ExecHashSubPlanResultRelsByOid
- * Build a hash table to allow fast lookups of subplan ResultRelInfos by
- * partition Oid. We also populate the subplan ResultRelInfo with an
- * ri_PartitionRoot.
- */
-static void
-ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate,
- PartitionTupleRouting *proute)
-{
- HASHCTL ctl;
- HTAB *htab;
- int i;
-
- ctl.keysize = sizeof(Oid);
- ctl.entrysize = sizeof(SubplanResultRelHashElem);
- ctl.hcxt = CurrentMemoryContext;
-
- htab = hash_create("PartitionTupleRouting table", mtstate->mt_nrels,
- &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
- proute->subplan_resultrel_htab = htab;
-
- /* Hash all subplans by their Oid */
- for (i = 0; i < mtstate->mt_nrels; i++)
- {
- ResultRelInfo *rri = &mtstate->resultRelInfo[i];
- bool found;
- Oid partoid = RelationGetRelid(rri->ri_RelationDesc);
- SubplanResultRelHashElem *elem;
-
- elem = (SubplanResultRelHashElem *)
- hash_search(htab, &partoid, HASH_ENTER, &found);
- Assert(!found);
- elem->rri = rri;
-
- /*
- * 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.
- */
- rri->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
- }
-}
-
-/*
* ExecInitPartitionInfo
* Lock the partition and initialize ResultRelInfo. Also setup other
* information for the partition and store it in the next empty slot in
@@ -613,7 +538,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
* 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().
+ * find a result rel to reuse.
*/
if (node && node->withCheckOptionLists != NIL)
{
@@ -676,7 +601,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
* 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().
+ * result rel to reuse.
*/
if (node && node->returningLists != NIL)
{
@@ -734,7 +659,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
/* Set up information needed for routing tuples to the partition. */
ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
- leaf_part_rri, partidx);
+ leaf_part_rri, partidx, false);
/*
* If there is an ON CONFLICT clause, initialize state for it.
@@ -911,15 +836,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
}
/*
- * Also, if transition capture is required, store a map to convert tuples
- * from partition's rowtype to the root partition table's.
- */
- if (mtstate->mt_transition_capture || mtstate->mt_oc_transition_capture)
- leaf_part_rri->ri_ChildToRootMap =
- convert_tuples_by_name(RelationGetDescr(leaf_part_rri->ri_RelationDesc),
- RelationGetDescr(rootResultRelInfo->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.
*
@@ -949,7 +865,8 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
PartitionTupleRouting *proute,
PartitionDispatch dispatch,
ResultRelInfo *partRelInfo,
- int partidx)
+ int partidx,
+ bool is_borrowed_rel)
{
ResultRelInfo *rootRelInfo = partRelInfo->ri_RootResultRelInfo;
MemoryContext oldcxt;
@@ -1029,6 +946,8 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
proute->max_partitions = 8;
proute->partitions = (ResultRelInfo **)
palloc(sizeof(ResultRelInfo *) * proute->max_partitions);
+ proute->is_borrowed_rel = (bool *)
+ palloc(sizeof(bool) * proute->max_partitions);
}
else
{
@@ -1036,10 +955,14 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
proute->partitions = (ResultRelInfo **)
repalloc(proute->partitions, sizeof(ResultRelInfo *) *
proute->max_partitions);
+ proute->is_borrowed_rel = (bool *)
+ repalloc(proute->is_borrowed_rel, sizeof(bool) *
+ proute->max_partitions);
}
}
proute->partitions[rri_index] = partRelInfo;
+ proute->is_borrowed_rel[rri_index] = is_borrowed_rel;
dispatch->indexes[partidx] = rri_index;
MemoryContextSwitchTo(oldcxt);
@@ -1199,7 +1122,6 @@ void
ExecCleanupTupleRouting(ModifyTableState *mtstate,
PartitionTupleRouting *proute)
{
- HTAB *htab = proute->subplan_resultrel_htab;
int i;
/*
@@ -1230,20 +1152,11 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate,
resultRelInfo);
/*
- * Check if this result rel is one belonging to the node's subplans,
- * if so, let ExecEndPlan() clean it up.
+ * Close it if it's not one of the result relations borrowed from the
+ * owning ModifyTableState; those will be closed by ExecEndPlan().
*/
- if (htab)
- {
- Oid partoid;
- bool found;
-
- partoid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
-
- (void) hash_search(htab, &partoid, HASH_FIND, &found);
- if (found)
- continue;
- }
+ if (proute->is_borrowed_rel[i])
+ continue;
ExecCloseIndices(resultRelInfo);
table_close(resultRelInfo->ri_RelationDesc, NoLock);