aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execPartition.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execPartition.c')
-rw-r--r--src/backend/executor/execPartition.c248
1 files changed, 130 insertions, 118 deletions
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index c83991c93c5..6c461c91b23 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -48,10 +48,10 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
bool *isnull,
int maxfieldlen);
static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
-static void find_subplans_for_params_recurse(PartitionPruneState *prunestate,
- PartitionPruningData *pprune,
- bool allparams,
- Bitmapset **validsubplans);
+static void find_matching_subplans_recurse(PartitionPruneState *prunestate,
+ PartitionPruningData *pprune,
+ bool initial_prune,
+ Bitmapset **validsubplans);
/*
@@ -1338,25 +1338,23 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
* here are designed to work with any node type which supports an arbitrary
* number of subnodes, e.g. Append, MergeAppend.
*
- * Normally this pruning work is performed by the query planner's partition
- * pruning code, however, the planner is limited to only being able to prune
- * away unneeded partitions using quals which compare the partition key to a
- * value which is known to be Const during planning. To allow the same
- * pruning to be performed for values which are only determined during
- * execution, we must make an additional pruning attempt during execution.
+ * When pruning involves comparison of a partition key to a constant, it's
+ * done by the planner. However, if we have a comparison to a non-constant
+ * but not volatile expression, that presents an opportunity for run-time
+ * pruning by the executor, allowing irrelevant partitions to be skipped
+ * dynamically.
*
- * Here we support pruning using both external and exec Params. The main
- * difference between these that we need to concern ourselves with is the
- * time when the values of the Params are known. External Param values are
- * known at any time of execution, including executor startup, but exec Param
- * values are only known when the executor is running.
+ * We must distinguish expressions containing PARAM_EXEC Params from
+ * expressions that don't contain those. Even though a PARAM_EXEC Param is
+ * considered to be a stable expression, it can change value from one node
+ * scan to the next during query execution. Stable comparison expressions
+ * that don't involve such Params allow partition pruning to be done once
+ * during executor startup. Expressions that do involve such Params require
+ * us to prune separately for each scan of the parent plan node.
*
- * For external Params we may be able to prune away unneeded partitions
- * during executor startup. This has the added benefit of not having to
- * initialize the unneeded subnodes at all. This is useful as it can save
- * quite a bit of effort during executor startup.
+ * Note that pruning away unneeded subnodes during executor startup has the
+ * added benefit of not having to initialize the unneeded subnodes at all.
*
- * For exec Params, we must delay pruning until the executor is running.
*
* Functions:
*
@@ -1369,19 +1367,20 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
* planner's partition prune function into subnode indexes.
*
* ExecFindInitialMatchingSubPlans:
- * Returns indexes of matching subnodes utilizing only external Params
- * to eliminate subnodes. The function must only be called during
- * executor startup for the given node before the subnodes themselves
- * are initialized. Subnodes which are found not to match by this
- * function must not be included in the node's list of subnodes as this
- * function performs a remap of the partition index to subplan index map
- * and the newly created map provides indexes only for subnodes which
- * remain after calling this function.
+ * Returns indexes of matching subnodes. Partition pruning is attempted
+ * without any evaluation of expressions containing PARAM_EXEC Params.
+ * This function must be called during executor startup for the given
+ * node before the subnodes themselves are initialized. Subnodes which
+ * are found not to match by this function must not be included in the
+ * node's list of subnodes as this function performs a remap of the
+ * partition index to subplan index map and the newly created map
+ * provides indexes only for subnodes which remain after calling this
+ * function.
*
* ExecFindMatchingSubPlans:
- * Returns indexes of matching subnodes utilizing all Params to eliminate
- * subnodes which can't possibly contain matching tuples. This function
- * can only be called while the executor is running.
+ * Returns indexes of matching subnodes after evaluating all available
+ * expressions. This function can only be called while the executor is
+ * running.
*-------------------------------------------------------------------------
*/
@@ -1416,8 +1415,9 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
*/
prunestate->partprunedata = prunedata;
prunestate->num_partprunedata = list_length(partitionpruneinfo);
- prunestate->extparams = NULL;
- prunestate->execparams = NULL;
+ prunestate->do_initial_prune = false; /* may be set below */
+ prunestate->do_exec_prune = false; /* may be set below */
+ prunestate->execparamids = NULL;
/*
* Create a sub memory context which we'll use when making calls to the
@@ -1444,19 +1444,20 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
int partnatts;
int n_steps;
- pprune->present_parts = bms_copy(pinfo->present_parts);
- pprune->subnode_map = palloc(sizeof(int) * pinfo->nparts);
-
/*
* We must make a copy of this rather than pointing directly to the
* plan's version as we may end up making modifications to it later.
*/
+ pprune->subnode_map = palloc(sizeof(int) * pinfo->nparts);
memcpy(pprune->subnode_map, pinfo->subnode_map,
sizeof(int) * pinfo->nparts);
/* We can use the subpart_map verbatim, since we never modify it */
pprune->subpart_map = pinfo->subpart_map;
+ /* present_parts is also subject to later modification */
+ pprune->present_parts = bms_copy(pinfo->present_parts);
+
/*
* Grab some info from the table's relcache; lock was already obtained
* by ExecLockNonLeafAppendTables.
@@ -1465,7 +1466,6 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
partkey = RelationGetPartitionKey(rel);
partdesc = RelationGetPartitionDesc(rel);
- n_steps = list_length(pinfo->pruning_steps);
context->strategy = partkey->strategy;
context->partnatts = partnatts = partkey->partnatts;
@@ -1476,10 +1476,11 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
context->nparts = pinfo->nparts;
context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
context->planstate = planstate;
- context->safeparams = NULL; /* empty for now */
- context->exprstates = palloc0(sizeof(ExprState *) * n_steps * partnatts);
- /* Initialize expression states for each expression */
+ /* Initialize expression state for each expression we need */
+ n_steps = list_length(pinfo->pruning_steps);
+ context->exprstates = (ExprState **)
+ palloc0(sizeof(ExprState *) * n_steps * partnatts);
foreach(lc2, pinfo->pruning_steps)
{
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc2);
@@ -1496,13 +1497,14 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
foreach(lc3, step->exprs)
{
Expr *expr = (Expr *) lfirst(lc3);
- int stateidx;
/* not needed for Consts */
if (!IsA(expr, Const))
{
- stateidx = PruneCxtStateIdx(partnatts,
- step->step.step_id, keyno);
+ int stateidx = PruneCxtStateIdx(partnatts,
+ step->step.step_id,
+ keyno);
+
context->exprstates[stateidx] =
ExecInitExpr(expr, context->planstate);
}
@@ -1510,32 +1512,29 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
}
}
+ /* Array is not modified at runtime, so just point to plan's copy */
+ context->exprhasexecparam = pinfo->hasexecparam;
+
pprune->pruning_steps = pinfo->pruning_steps;
- pprune->extparams = bms_copy(pinfo->extparams);
- pprune->allparams = bms_union(pinfo->extparams, pinfo->execparams);
+ pprune->do_initial_prune = pinfo->do_initial_prune;
+ pprune->do_exec_prune = pinfo->do_exec_prune;
+
+ /* Record if pruning would be useful at any level */
+ prunestate->do_initial_prune |= pinfo->do_initial_prune;
+ prunestate->do_exec_prune |= pinfo->do_exec_prune;
/*
- * Accumulate the paramids which match the partitioned keys of all
- * partitioned tables.
+ * Accumulate the IDs of all PARAM_EXEC Params affecting the
+ * partitioning decisions at this node.
*/
- prunestate->extparams = bms_add_members(prunestate->extparams,
- pinfo->extparams);
-
- prunestate->execparams = bms_add_members(prunestate->execparams,
- pinfo->execparams);
+ prunestate->execparamids = bms_add_members(prunestate->execparamids,
+ pinfo->execparamids);
relation_close(rel, NoLock);
i++;
}
- /*
- * Cache the union of the paramids of both types. This saves having to
- * recalculate it everytime we need to know what they are.
- */
- prunestate->allparams = bms_union(prunestate->extparams,
- prunestate->execparams);
-
return prunestate;
}
@@ -1543,9 +1542,8 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
* ExecFindInitialMatchingSubPlans
* Determine which subset of subplan nodes we need to initialize based
* on the details stored in 'prunestate'. Here we only determine the
- * matching partitions using values known during plan startup, which is
- * only external Params. Exec Params will be unknown at this time. We
- * must delay pruning using exec Params until the actual executor run.
+ * matching partitions using values known during plan startup, which
+ * excludes any expressions containing PARAM_EXEC Params.
*
* It is expected that callers of this function do so only once during their
* init plan. The caller must only initialize the subnodes which are returned
@@ -1554,8 +1552,6 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
* return its matching subnode indexes assuming that the caller discarded
* the original non-matching subnodes.
*
- * This function must only be called if 'prunestate' has any extparams.
- *
* 'nsubnodes' must be passed as the total number of unpruned subnodes.
*/
Bitmapset *
@@ -1565,11 +1561,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
MemoryContext oldcontext;
Bitmapset *result = NULL;
- /*
- * Ensure there's actually external params, or we've not been called
- * already.
- */
- Assert(!bms_is_empty(prunestate->extparams));
+ Assert(prunestate->do_initial_prune);
pprune = prunestate->partprunedata;
@@ -1579,27 +1571,17 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
*/
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
- /* Determine which subnodes match the external params */
- find_subplans_for_params_recurse(prunestate, pprune, false, &result);
+ /* Perform pruning without using PARAM_EXEC Params */
+ find_matching_subplans_recurse(prunestate, pprune, true, &result);
MemoryContextSwitchTo(oldcontext);
- /* Move to the correct memory context */
+ /* Copy result out of the temp context before we reset it */
result = bms_copy(result);
MemoryContextReset(prunestate->prune_context);
-
- /*
- * Record that partition pruning has been performed for external params.
- * These are not required again afterwards, and nullifying them helps
- * ensure nothing accidentally calls this function twice on the same
- * PartitionPruneState.
- *
- * (Note we keep prunestate->allparams, because we do use that one
- * repeatedly in ExecFindMatchingSubPlans).
- */
- bms_free(prunestate->extparams);
- prunestate->extparams = NULL;
+ /* Expression eval may have used space in node's ps_ExprContext too */
+ ResetExprContext(pprune->context.planstate->ps_ExprContext);
/*
* If any subnodes were pruned, we must re-sequence the subnode indexes so
@@ -1669,6 +1651,41 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
}
}
+ /*
+ * Now we must determine which sub-partitioned tables still have
+ * unpruned partitions. The easiest way to do this is to simply loop
+ * over each PartitionPruningData again checking if there are any
+ * 'present_parts' in the sub-partitioned table. We needn't bother
+ * doing this if there are no sub-partitioned tables.
+ */
+ if (prunestate->num_partprunedata > 1)
+ {
+ for (i = 0; i < prunestate->num_partprunedata; i++)
+ {
+ int nparts;
+ int j;
+
+ pprune = &prunestate->partprunedata[i];
+ nparts = pprune->context.nparts;
+
+ for (j = 0; j < nparts; j++)
+ {
+ int subidx = pprune->subpart_map[j];
+
+ if (subidx >= 0)
+ {
+ PartitionPruningData *subprune;
+
+ subprune = &prunestate->partprunedata[subidx];
+
+ if (!bms_is_empty(subprune->present_parts))
+ pprune->present_parts =
+ bms_add_member(pprune->present_parts, j);
+ }
+ }
+ }
+ }
+
pfree(new_subnode_indexes);
}
@@ -1678,9 +1695,9 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
/*
* ExecFindMatchingSubPlans
* Determine which subplans match the pruning steps detailed in
- * 'pprune' for the current Param values.
+ * 'pprune' for the current comparison expression values.
*
- * Here we utilize both external and exec Params for pruning.
+ * Here we assume we may evaluate PARAM_EXEC Params.
*/
Bitmapset *
ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
@@ -1697,63 +1714,58 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
*/
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
- find_subplans_for_params_recurse(prunestate, pprune, true, &result);
+ find_matching_subplans_recurse(prunestate, pprune, false, &result);
MemoryContextSwitchTo(oldcontext);
- /* Move to the correct memory context */
+ /* Copy result out of the temp context before we reset it */
result = bms_copy(result);
MemoryContextReset(prunestate->prune_context);
+ /* Expression eval may have used space in node's ps_ExprContext too */
+ ResetExprContext(pprune->context.planstate->ps_ExprContext);
return result;
}
/*
- * find_subplans_for_params_recurse
+ * find_matching_subplans_recurse
* Recursive worker function for ExecFindMatchingSubPlans and
* ExecFindInitialMatchingSubPlans
+ *
+ * Adds valid (non-prunable) subplan IDs to *validsubplans
*/
static void
-find_subplans_for_params_recurse(PartitionPruneState *prunestate,
- PartitionPruningData *pprune,
- bool allparams,
- Bitmapset **validsubplans)
+find_matching_subplans_recurse(PartitionPruneState *prunestate,
+ PartitionPruningData *pprune,
+ bool initial_prune,
+ Bitmapset **validsubplans)
{
- PartitionPruneContext *context = &pprune->context;
Bitmapset *partset;
- Bitmapset *pruneparams;
int i;
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
- /*
- * Use only external params unless we've been asked to also use exec
- * params too.
- */
- if (allparams)
- pruneparams = pprune->allparams;
- else
- pruneparams = pprune->extparams;
-
- /*
- * We only need to determine the matching partitions if there are any
- * params matching the partition key at this level. If there are no
- * matching params, then we can simply return all subnodes which belong to
- * this parent partition. The planner should have already determined
- * these to be the minimum possible set. We must still recursively visit
- * any subpartitioned tables as we may find their partition keys match
- * some Params at their level.
- */
- if (!bms_is_empty(pruneparams))
+ /* Only prune if pruning would be useful at this level. */
+ if (initial_prune ? pprune->do_initial_prune : pprune->do_exec_prune)
{
- context->safeparams = pruneparams;
+ PartitionPruneContext *context = &pprune->context;
+
+ /* Set whether we can evaluate PARAM_EXEC Params or not */
+ context->evalexecparams = !initial_prune;
+
partset = get_matching_partitions(context,
pprune->pruning_steps);
}
else
+ {
+ /*
+ * If no pruning is to be done, just include all partitions at this
+ * level.
+ */
partset = pprune->present_parts;
+ }
/* Translate partset into subnode indexes */
i = -1;
@@ -1767,9 +1779,9 @@ find_subplans_for_params_recurse(PartitionPruneState *prunestate,
int partidx = pprune->subpart_map[i];
if (partidx != -1)
- find_subplans_for_params_recurse(prunestate,
- &prunestate->partprunedata[partidx],
- allparams, validsubplans);
+ find_matching_subplans_recurse(prunestate,
+ &prunestate->partprunedata[partidx],
+ initial_prune, validsubplans);
else
{
/*