Improve run-time partition pruning to handle any stable expression.
The initial coding of the run-time-pruning feature only coped with cases where the partition key(s) are compared to Params. That is a bit silly; we can allow it to work with any non-Var-containing stable expression, as long as we take special care with expressions containing PARAM_EXEC Params. The code is hardly any longer this way, and it's considerably clearer (IMO at least). Per gripe from Pavel Stehule. David Rowley, whacked around a bit by me Discussion: https://postgr.es/m/CAFj8pRBjrufA3ocDm8o4LPGNye9Y+pm1b9kCwode4X04CULG3g@mail.gmail.com
This commit is contained in:
parent
c83e202990
commit
73b7f48f78
|
@ -48,10 +48,10 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
|
||||||
bool *isnull,
|
bool *isnull,
|
||||||
int maxfieldlen);
|
int maxfieldlen);
|
||||||
static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
|
static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
|
||||||
static void find_subplans_for_params_recurse(PartitionPruneState *prunestate,
|
static void find_matching_subplans_recurse(PartitionPruneState *prunestate,
|
||||||
PartitionPruningData *pprune,
|
PartitionPruningData *pprune,
|
||||||
bool allparams,
|
bool initial_prune,
|
||||||
Bitmapset **validsubplans);
|
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
|
* here are designed to work with any node type which supports an arbitrary
|
||||||
* number of subnodes, e.g. Append, MergeAppend.
|
* number of subnodes, e.g. Append, MergeAppend.
|
||||||
*
|
*
|
||||||
* Normally this pruning work is performed by the query planner's partition
|
* When pruning involves comparison of a partition key to a constant, it's
|
||||||
* pruning code, however, the planner is limited to only being able to prune
|
* done by the planner. However, if we have a comparison to a non-constant
|
||||||
* away unneeded partitions using quals which compare the partition key to a
|
* but not volatile expression, that presents an opportunity for run-time
|
||||||
* value which is known to be Const during planning. To allow the same
|
* pruning by the executor, allowing irrelevant partitions to be skipped
|
||||||
* pruning to be performed for values which are only determined during
|
* dynamically.
|
||||||
* execution, we must make an additional pruning attempt during execution.
|
|
||||||
*
|
*
|
||||||
* Here we support pruning using both external and exec Params. The main
|
* We must distinguish expressions containing PARAM_EXEC Params from
|
||||||
* difference between these that we need to concern ourselves with is the
|
* expressions that don't contain those. Even though a PARAM_EXEC Param is
|
||||||
* time when the values of the Params are known. External Param values are
|
* considered to be a stable expression, it can change value from one node
|
||||||
* known at any time of execution, including executor startup, but exec Param
|
* scan to the next during query execution. Stable comparison expressions
|
||||||
* values are only known when the executor is running.
|
* 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
|
* Note that pruning away unneeded subnodes during executor startup has the
|
||||||
* during executor startup. This has the added benefit of not having to
|
* added benefit of not having to initialize the unneeded subnodes at all.
|
||||||
* initialize the unneeded subnodes at all. This is useful as it can save
|
|
||||||
* quite a bit of effort during executor startup.
|
|
||||||
*
|
*
|
||||||
* For exec Params, we must delay pruning until the executor is running.
|
|
||||||
*
|
*
|
||||||
* Functions:
|
* Functions:
|
||||||
*
|
*
|
||||||
|
@ -1369,19 +1367,20 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
|
||||||
* planner's partition prune function into subnode indexes.
|
* planner's partition prune function into subnode indexes.
|
||||||
*
|
*
|
||||||
* ExecFindInitialMatchingSubPlans:
|
* ExecFindInitialMatchingSubPlans:
|
||||||
* Returns indexes of matching subnodes utilizing only external Params
|
* Returns indexes of matching subnodes. Partition pruning is attempted
|
||||||
* to eliminate subnodes. The function must only be called during
|
* without any evaluation of expressions containing PARAM_EXEC Params.
|
||||||
* executor startup for the given node before the subnodes themselves
|
* This function must be called during executor startup for the given
|
||||||
* are initialized. Subnodes which are found not to match by this
|
* node before the subnodes themselves are initialized. Subnodes which
|
||||||
* function must not be included in the node's list of subnodes as this
|
* are found not to match by this function must not be included in the
|
||||||
* function performs a remap of the partition index to subplan index map
|
* node's list of subnodes as this function performs a remap of the
|
||||||
* and the newly created map provides indexes only for subnodes which
|
* partition index to subplan index map and the newly created map
|
||||||
* remain after calling this function.
|
* provides indexes only for subnodes which remain after calling this
|
||||||
|
* function.
|
||||||
*
|
*
|
||||||
* ExecFindMatchingSubPlans:
|
* ExecFindMatchingSubPlans:
|
||||||
* Returns indexes of matching subnodes utilizing all Params to eliminate
|
* Returns indexes of matching subnodes after evaluating all available
|
||||||
* subnodes which can't possibly contain matching tuples. This function
|
* expressions. This function can only be called while the executor is
|
||||||
* can only be called while the executor is running.
|
* running.
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1416,8 +1415,9 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
|
||||||
*/
|
*/
|
||||||
prunestate->partprunedata = prunedata;
|
prunestate->partprunedata = prunedata;
|
||||||
prunestate->num_partprunedata = list_length(partitionpruneinfo);
|
prunestate->num_partprunedata = list_length(partitionpruneinfo);
|
||||||
prunestate->extparams = NULL;
|
prunestate->do_initial_prune = false; /* may be set below */
|
||||||
prunestate->execparams = NULL;
|
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
|
* 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 partnatts;
|
||||||
int n_steps;
|
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
|
* 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.
|
* 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,
|
memcpy(pprune->subnode_map, pinfo->subnode_map,
|
||||||
sizeof(int) * pinfo->nparts);
|
sizeof(int) * pinfo->nparts);
|
||||||
|
|
||||||
/* We can use the subpart_map verbatim, since we never modify it */
|
/* We can use the subpart_map verbatim, since we never modify it */
|
||||||
pprune->subpart_map = pinfo->subpart_map;
|
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
|
* Grab some info from the table's relcache; lock was already obtained
|
||||||
* by ExecLockNonLeafAppendTables.
|
* by ExecLockNonLeafAppendTables.
|
||||||
|
@ -1465,7 +1466,6 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
|
||||||
|
|
||||||
partkey = RelationGetPartitionKey(rel);
|
partkey = RelationGetPartitionKey(rel);
|
||||||
partdesc = RelationGetPartitionDesc(rel);
|
partdesc = RelationGetPartitionDesc(rel);
|
||||||
n_steps = list_length(pinfo->pruning_steps);
|
|
||||||
|
|
||||||
context->strategy = partkey->strategy;
|
context->strategy = partkey->strategy;
|
||||||
context->partnatts = partnatts = partkey->partnatts;
|
context->partnatts = partnatts = partkey->partnatts;
|
||||||
|
@ -1476,10 +1476,11 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
|
||||||
context->nparts = pinfo->nparts;
|
context->nparts = pinfo->nparts;
|
||||||
context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
|
context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
|
||||||
context->planstate = planstate;
|
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)
|
foreach(lc2, pinfo->pruning_steps)
|
||||||
{
|
{
|
||||||
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc2);
|
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc2);
|
||||||
|
@ -1496,13 +1497,14 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
|
||||||
foreach(lc3, step->exprs)
|
foreach(lc3, step->exprs)
|
||||||
{
|
{
|
||||||
Expr *expr = (Expr *) lfirst(lc3);
|
Expr *expr = (Expr *) lfirst(lc3);
|
||||||
int stateidx;
|
|
||||||
|
|
||||||
/* not needed for Consts */
|
/* not needed for Consts */
|
||||||
if (!IsA(expr, Const))
|
if (!IsA(expr, Const))
|
||||||
{
|
{
|
||||||
stateidx = PruneCxtStateIdx(partnatts,
|
int stateidx = PruneCxtStateIdx(partnatts,
|
||||||
step->step.step_id, keyno);
|
step->step.step_id,
|
||||||
|
keyno);
|
||||||
|
|
||||||
context->exprstates[stateidx] =
|
context->exprstates[stateidx] =
|
||||||
ExecInitExpr(expr, context->planstate);
|
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->pruning_steps = pinfo->pruning_steps;
|
||||||
pprune->extparams = bms_copy(pinfo->extparams);
|
pprune->do_initial_prune = pinfo->do_initial_prune;
|
||||||
pprune->allparams = bms_union(pinfo->extparams, pinfo->execparams);
|
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
|
* Accumulate the IDs of all PARAM_EXEC Params affecting the
|
||||||
* partitioned tables.
|
* partitioning decisions at this node.
|
||||||
*/
|
*/
|
||||||
prunestate->extparams = bms_add_members(prunestate->extparams,
|
prunestate->execparamids = bms_add_members(prunestate->execparamids,
|
||||||
pinfo->extparams);
|
pinfo->execparamids);
|
||||||
|
|
||||||
prunestate->execparams = bms_add_members(prunestate->execparams,
|
|
||||||
pinfo->execparams);
|
|
||||||
|
|
||||||
relation_close(rel, NoLock);
|
relation_close(rel, NoLock);
|
||||||
|
|
||||||
i++;
|
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;
|
return prunestate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,9 +1542,8 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
|
||||||
* ExecFindInitialMatchingSubPlans
|
* ExecFindInitialMatchingSubPlans
|
||||||
* Determine which subset of subplan nodes we need to initialize based
|
* Determine which subset of subplan nodes we need to initialize based
|
||||||
* on the details stored in 'prunestate'. Here we only determine the
|
* on the details stored in 'prunestate'. Here we only determine the
|
||||||
* matching partitions using values known during plan startup, which is
|
* matching partitions using values known during plan startup, which
|
||||||
* only external Params. Exec Params will be unknown at this time. We
|
* excludes any expressions containing PARAM_EXEC Params.
|
||||||
* must delay pruning using exec Params until the actual executor run.
|
|
||||||
*
|
*
|
||||||
* It is expected that callers of this function do so only once during their
|
* 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
|
* 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
|
* return its matching subnode indexes assuming that the caller discarded
|
||||||
* the original non-matching subnodes.
|
* 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.
|
* 'nsubnodes' must be passed as the total number of unpruned subnodes.
|
||||||
*/
|
*/
|
||||||
Bitmapset *
|
Bitmapset *
|
||||||
|
@ -1565,11 +1561,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
Bitmapset *result = NULL;
|
Bitmapset *result = NULL;
|
||||||
|
|
||||||
/*
|
Assert(prunestate->do_initial_prune);
|
||||||
* Ensure there's actually external params, or we've not been called
|
|
||||||
* already.
|
|
||||||
*/
|
|
||||||
Assert(!bms_is_empty(prunestate->extparams));
|
|
||||||
|
|
||||||
pprune = prunestate->partprunedata;
|
pprune = prunestate->partprunedata;
|
||||||
|
|
||||||
|
@ -1579,27 +1571,17 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
|
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
|
||||||
|
|
||||||
/* Determine which subnodes match the external params */
|
/* Perform pruning without using PARAM_EXEC Params */
|
||||||
find_subplans_for_params_recurse(prunestate, pprune, false, &result);
|
find_matching_subplans_recurse(prunestate, pprune, true, &result);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
/* Move to the correct memory context */
|
/* Copy result out of the temp context before we reset it */
|
||||||
result = bms_copy(result);
|
result = bms_copy(result);
|
||||||
|
|
||||||
MemoryContextReset(prunestate->prune_context);
|
MemoryContextReset(prunestate->prune_context);
|
||||||
|
/* Expression eval may have used space in node's ps_ExprContext too */
|
||||||
/*
|
ResetExprContext(pprune->context.planstate->ps_ExprContext);
|
||||||
* 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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any subnodes were pruned, we must re-sequence the subnode indexes so
|
* 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);
|
pfree(new_subnode_indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,9 +1695,9 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubnodes)
|
||||||
/*
|
/*
|
||||||
* ExecFindMatchingSubPlans
|
* ExecFindMatchingSubPlans
|
||||||
* Determine which subplans match the pruning steps detailed in
|
* 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 *
|
Bitmapset *
|
||||||
ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
|
ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
|
||||||
|
@ -1697,63 +1714,58 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
|
oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
|
||||||
|
|
||||||
find_subplans_for_params_recurse(prunestate, pprune, true, &result);
|
find_matching_subplans_recurse(prunestate, pprune, false, &result);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
/* Move to the correct memory context */
|
/* Copy result out of the temp context before we reset it */
|
||||||
result = bms_copy(result);
|
result = bms_copy(result);
|
||||||
|
|
||||||
MemoryContextReset(prunestate->prune_context);
|
MemoryContextReset(prunestate->prune_context);
|
||||||
|
/* Expression eval may have used space in node's ps_ExprContext too */
|
||||||
|
ResetExprContext(pprune->context.planstate->ps_ExprContext);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find_subplans_for_params_recurse
|
* find_matching_subplans_recurse
|
||||||
* Recursive worker function for ExecFindMatchingSubPlans and
|
* Recursive worker function for ExecFindMatchingSubPlans and
|
||||||
* ExecFindInitialMatchingSubPlans
|
* ExecFindInitialMatchingSubPlans
|
||||||
|
*
|
||||||
|
* Adds valid (non-prunable) subplan IDs to *validsubplans
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
find_subplans_for_params_recurse(PartitionPruneState *prunestate,
|
find_matching_subplans_recurse(PartitionPruneState *prunestate,
|
||||||
PartitionPruningData *pprune,
|
PartitionPruningData *pprune,
|
||||||
bool allparams,
|
bool initial_prune,
|
||||||
Bitmapset **validsubplans)
|
Bitmapset **validsubplans)
|
||||||
{
|
{
|
||||||
PartitionPruneContext *context = &pprune->context;
|
|
||||||
Bitmapset *partset;
|
Bitmapset *partset;
|
||||||
Bitmapset *pruneparams;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Guard against stack overflow due to overly deep partition hierarchy. */
|
/* Guard against stack overflow due to overly deep partition hierarchy. */
|
||||||
check_stack_depth();
|
check_stack_depth();
|
||||||
|
|
||||||
/*
|
/* Only prune if pruning would be useful at this level. */
|
||||||
* Use only external params unless we've been asked to also use exec
|
if (initial_prune ? pprune->do_initial_prune : pprune->do_exec_prune)
|
||||||
* 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))
|
|
||||||
{
|
{
|
||||||
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,
|
partset = get_matching_partitions(context,
|
||||||
pprune->pruning_steps);
|
pprune->pruning_steps);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If no pruning is to be done, just include all partitions at this
|
||||||
|
* level.
|
||||||
|
*/
|
||||||
partset = pprune->present_parts;
|
partset = pprune->present_parts;
|
||||||
|
}
|
||||||
|
|
||||||
/* Translate partset into subnode indexes */
|
/* Translate partset into subnode indexes */
|
||||||
i = -1;
|
i = -1;
|
||||||
|
@ -1767,9 +1779,9 @@ find_subplans_for_params_recurse(PartitionPruneState *prunestate,
|
||||||
int partidx = pprune->subpart_map[i];
|
int partidx = pprune->subpart_map[i];
|
||||||
|
|
||||||
if (partidx != -1)
|
if (partidx != -1)
|
||||||
find_subplans_for_params_recurse(prunestate,
|
find_matching_subplans_recurse(prunestate,
|
||||||
&prunestate->partprunedata[partidx],
|
&prunestate->partprunedata[partidx],
|
||||||
allparams, validsubplans);
|
initial_prune, validsubplans);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -133,29 +133,27 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
|
||||||
{
|
{
|
||||||
PartitionPruneState *prunestate;
|
PartitionPruneState *prunestate;
|
||||||
|
|
||||||
|
/* We may need an expression context to evaluate partition exprs */
|
||||||
ExecAssignExprContext(estate, &appendstate->ps);
|
ExecAssignExprContext(estate, &appendstate->ps);
|
||||||
|
|
||||||
prunestate = ExecSetupPartitionPruneState(&appendstate->ps,
|
prunestate = ExecSetupPartitionPruneState(&appendstate->ps,
|
||||||
node->part_prune_infos);
|
node->part_prune_infos);
|
||||||
|
|
||||||
/*
|
/* Perform an initial partition prune, if required. */
|
||||||
* When there are external params matching the partition key we may be
|
if (prunestate->do_initial_prune)
|
||||||
* able to prune away Append subplans now.
|
|
||||||
*/
|
|
||||||
if (!bms_is_empty(prunestate->extparams))
|
|
||||||
{
|
{
|
||||||
/* Determine which subplans match the external params */
|
/* Determine which subplans survive initial pruning */
|
||||||
validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
|
validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
|
||||||
list_length(node->appendplans));
|
list_length(node->appendplans));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If no subplans match the given parameters then we must handle
|
* The case where no subplans survive pruning must be handled
|
||||||
* this case in a special way. The problem here is that code in
|
* specially. The problem here is that code in explain.c requires
|
||||||
* explain.c requires an Append to have at least one subplan in
|
* an Append to have at least one subplan in order for it to
|
||||||
* order for it to properly determine the Vars in that subplan's
|
* properly determine the Vars in that subplan's targetlist. We
|
||||||
* targetlist. We sidestep this issue by just initializing the
|
* sidestep this issue by just initializing the first subplan and
|
||||||
* first subplan and setting as_whichplan to NO_MATCHING_SUBPLANS
|
* setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that
|
||||||
* to indicate that we don't need to scan any subnodes.
|
* we don't really need to scan any subnodes.
|
||||||
*/
|
*/
|
||||||
if (bms_is_empty(validsubplans))
|
if (bms_is_empty(validsubplans))
|
||||||
{
|
{
|
||||||
|
@ -175,14 +173,13 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there's no exec params then no further pruning can be done, we
|
* If no runtime pruning is required, we can fill as_valid_subplans
|
||||||
* can just set the valid subplans to all remaining subplans.
|
* immediately, preventing later calls to ExecFindMatchingSubPlans.
|
||||||
*/
|
*/
|
||||||
if (bms_is_empty(prunestate->execparams))
|
if (!prunestate->do_exec_prune)
|
||||||
appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
|
appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
|
||||||
|
|
||||||
appendstate->as_prune_state = prunestate;
|
appendstate->as_prune_state = prunestate;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -190,7 +187,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When run-time partition pruning is not enabled we can just mark all
|
* When run-time partition pruning is not enabled we can just mark all
|
||||||
* subplans as valid, they must also all be initialized.
|
* subplans as valid; they must also all be initialized.
|
||||||
*/
|
*/
|
||||||
appendstate->as_valid_subplans = validsubplans =
|
appendstate->as_valid_subplans = validsubplans =
|
||||||
bms_add_range(NULL, 0, nplans - 1);
|
bms_add_range(NULL, 0, nplans - 1);
|
||||||
|
@ -341,13 +338,13 @@ ExecReScanAppend(AppendState *node)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any of the parameters being used for partition pruning have changed,
|
* If any PARAM_EXEC Params used in pruning expressions have changed, then
|
||||||
* then we'd better unset the valid subplans so that they are reselected
|
* we'd better unset the valid subplans so that they are reselected for
|
||||||
* for the new parameter values.
|
* the new parameter values.
|
||||||
*/
|
*/
|
||||||
if (node->as_prune_state &&
|
if (node->as_prune_state &&
|
||||||
bms_overlap(node->ps.chgParam,
|
bms_overlap(node->ps.chgParam,
|
||||||
node->as_prune_state->execparams))
|
node->as_prune_state->execparamids))
|
||||||
{
|
{
|
||||||
bms_free(node->as_valid_subplans);
|
bms_free(node->as_valid_subplans);
|
||||||
node->as_valid_subplans = NULL;
|
node->as_valid_subplans = NULL;
|
||||||
|
@ -531,9 +528,9 @@ choose_next_subplan_for_leader(AppendState *node)
|
||||||
node->as_whichplan = node->as_nplans - 1;
|
node->as_whichplan = node->as_nplans - 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we've yet to determine the valid subplans for these parameters
|
* If we've yet to determine the valid subplans then do so now. If
|
||||||
* then do so now. If run-time pruning is disabled then the valid
|
* run-time pruning is disabled then the valid subplans will always be
|
||||||
* subplans will always be set to all subplans.
|
* set to all subplans.
|
||||||
*/
|
*/
|
||||||
if (node->as_valid_subplans == NULL)
|
if (node->as_valid_subplans == NULL)
|
||||||
{
|
{
|
||||||
|
@ -606,9 +603,9 @@ choose_next_subplan_for_worker(AppendState *node)
|
||||||
node->as_pstate->pa_finished[node->as_whichplan] = true;
|
node->as_pstate->pa_finished[node->as_whichplan] = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we've yet to determine the valid subplans for these parameters then
|
* If we've yet to determine the valid subplans then do so now. If
|
||||||
* do so now. If run-time pruning is disabled then the valid subplans
|
* run-time pruning is disabled then the valid subplans will always be set
|
||||||
* will always be set to all subplans.
|
* to all subplans.
|
||||||
*/
|
*/
|
||||||
else if (node->as_valid_subplans == NULL)
|
else if (node->as_valid_subplans == NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2175,10 +2175,13 @@ _copyPartitionPruneInfo(const PartitionPruneInfo *from)
|
||||||
COPY_NODE_FIELD(pruning_steps);
|
COPY_NODE_FIELD(pruning_steps);
|
||||||
COPY_BITMAPSET_FIELD(present_parts);
|
COPY_BITMAPSET_FIELD(present_parts);
|
||||||
COPY_SCALAR_FIELD(nparts);
|
COPY_SCALAR_FIELD(nparts);
|
||||||
|
COPY_SCALAR_FIELD(nexprs);
|
||||||
COPY_POINTER_FIELD(subnode_map, from->nparts * sizeof(int));
|
COPY_POINTER_FIELD(subnode_map, from->nparts * sizeof(int));
|
||||||
COPY_POINTER_FIELD(subpart_map, from->nparts * sizeof(int));
|
COPY_POINTER_FIELD(subpart_map, from->nparts * sizeof(int));
|
||||||
COPY_BITMAPSET_FIELD(extparams);
|
COPY_POINTER_FIELD(hasexecparam, from->nexprs * sizeof(bool));
|
||||||
COPY_BITMAPSET_FIELD(execparams);
|
COPY_SCALAR_FIELD(do_initial_prune);
|
||||||
|
COPY_SCALAR_FIELD(do_exec_prune);
|
||||||
|
COPY_BITMAPSET_FIELD(execparamids);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1742,6 +1742,7 @@ _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node)
|
||||||
WRITE_NODE_FIELD(pruning_steps);
|
WRITE_NODE_FIELD(pruning_steps);
|
||||||
WRITE_BITMAPSET_FIELD(present_parts);
|
WRITE_BITMAPSET_FIELD(present_parts);
|
||||||
WRITE_INT_FIELD(nparts);
|
WRITE_INT_FIELD(nparts);
|
||||||
|
WRITE_INT_FIELD(nexprs);
|
||||||
|
|
||||||
appendStringInfoString(str, " :subnode_map");
|
appendStringInfoString(str, " :subnode_map");
|
||||||
for (i = 0; i < node->nparts; i++)
|
for (i = 0; i < node->nparts; i++)
|
||||||
|
@ -1751,8 +1752,13 @@ _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node)
|
||||||
for (i = 0; i < node->nparts; i++)
|
for (i = 0; i < node->nparts; i++)
|
||||||
appendStringInfo(str, " %d", node->subpart_map[i]);
|
appendStringInfo(str, " %d", node->subpart_map[i]);
|
||||||
|
|
||||||
WRITE_BITMAPSET_FIELD(extparams);
|
appendStringInfoString(str, " :hasexecparam");
|
||||||
WRITE_BITMAPSET_FIELD(execparams);
|
for (i = 0; i < node->nexprs; i++)
|
||||||
|
appendStringInfo(str, " %s", booltostr(node->hasexecparam[i]));
|
||||||
|
|
||||||
|
WRITE_BOOL_FIELD(do_initial_prune);
|
||||||
|
WRITE_BOOL_FIELD(do_exec_prune);
|
||||||
|
WRITE_BITMAPSET_FIELD(execparamids);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
|
@ -1363,10 +1363,13 @@ _readPartitionPruneInfo(void)
|
||||||
READ_NODE_FIELD(pruning_steps);
|
READ_NODE_FIELD(pruning_steps);
|
||||||
READ_BITMAPSET_FIELD(present_parts);
|
READ_BITMAPSET_FIELD(present_parts);
|
||||||
READ_INT_FIELD(nparts);
|
READ_INT_FIELD(nparts);
|
||||||
|
READ_INT_FIELD(nexprs);
|
||||||
READ_INT_ARRAY(subnode_map, local_node->nparts);
|
READ_INT_ARRAY(subnode_map, local_node->nparts);
|
||||||
READ_INT_ARRAY(subpart_map, local_node->nparts);
|
READ_INT_ARRAY(subpart_map, local_node->nparts);
|
||||||
READ_BITMAPSET_FIELD(extparams);
|
READ_BOOL_ARRAY(hasexecparam, local_node->nexprs);
|
||||||
READ_BITMAPSET_FIELD(execparams);
|
READ_BOOL_FIELD(do_initial_prune);
|
||||||
|
READ_BOOL_FIELD(do_exec_prune);
|
||||||
|
READ_BITMAPSET_FIELD(execparamids);
|
||||||
|
|
||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "optimizer/planner.h"
|
#include "optimizer/planner.h"
|
||||||
#include "optimizer/predtest.h"
|
#include "optimizer/predtest.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
|
#include "optimizer/var.h"
|
||||||
#include "partitioning/partprune.h"
|
#include "partitioning/partprune.h"
|
||||||
#include "partitioning/partbounds.h"
|
#include "partitioning/partbounds.h"
|
||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
|
@ -162,7 +163,10 @@ static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context,
|
||||||
static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
|
static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
|
||||||
StrategyNumber opstrategy, Datum *values, int nvalues,
|
StrategyNumber opstrategy, Datum *values, int nvalues,
|
||||||
FmgrInfo *partsupfunc, Bitmapset *nullkeys);
|
FmgrInfo *partsupfunc, Bitmapset *nullkeys);
|
||||||
static bool pull_partkey_params(PartitionPruneInfo *pinfo, List *steps);
|
static Bitmapset *pull_exec_paramids(Expr *expr);
|
||||||
|
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context);
|
||||||
|
static bool analyze_partkey_exprs(PartitionPruneInfo *pinfo, List *steps,
|
||||||
|
int partnatts);
|
||||||
static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
|
static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
|
||||||
PartitionPruneStepOp *opstep);
|
PartitionPruneStepOp *opstep);
|
||||||
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
|
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
|
||||||
|
@ -180,12 +184,12 @@ static bool partkey_datum_from_expr(PartitionPruneContext *context,
|
||||||
* pruning to take place.
|
* pruning to take place.
|
||||||
*
|
*
|
||||||
* Here we generate partition pruning steps for 'prunequal' and also build a
|
* Here we generate partition pruning steps for 'prunequal' and also build a
|
||||||
* data stucture which allows mapping of partition indexes into 'subpaths'
|
* data structure which allows mapping of partition indexes into 'subpaths'
|
||||||
* indexes.
|
* indexes.
|
||||||
*
|
*
|
||||||
* If no Params were found to match the partition key in any of the
|
* If no non-Const expressions are being compared to the partition key in any
|
||||||
* 'partitioned_rels', then we return NIL. In such a case run-time partition
|
* of the 'partitioned_rels', then we return NIL. In such a case run-time
|
||||||
* pruning would be useless.
|
* partition pruning would be useless, since the planner did it already.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
|
@ -197,7 +201,7 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
int *relid_subnode_map;
|
int *relid_subnode_map;
|
||||||
int *relid_subpart_map;
|
int *relid_subpart_map;
|
||||||
int i;
|
int i;
|
||||||
bool gotparam = false;
|
bool doruntimeprune = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate two arrays to store the 1-based indexes of the 'subpaths' and
|
* Allocate two arrays to store the 1-based indexes of the 'subpaths' and
|
||||||
|
@ -229,7 +233,7 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
relid_subpart_map[rti] = i++;
|
relid_subpart_map[rti] = i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We now build a PartitionPruneInfo for each partition_rels */
|
/* We now build a PartitionPruneInfo for each rel in partition_rels */
|
||||||
foreach(lc, partition_rels)
|
foreach(lc, partition_rels)
|
||||||
{
|
{
|
||||||
Index rti = lfirst_int(lc);
|
Index rti = lfirst_int(lc);
|
||||||
|
@ -238,6 +242,7 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
Bitmapset *present_parts;
|
Bitmapset *present_parts;
|
||||||
int nparts = subpart->nparts;
|
int nparts = subpart->nparts;
|
||||||
|
int partnatts = subpart->part_scheme->partnatts;
|
||||||
int *subnode_map;
|
int *subnode_map;
|
||||||
int *subpart_map;
|
int *subpart_map;
|
||||||
List *partprunequal;
|
List *partprunequal;
|
||||||
|
@ -320,17 +325,11 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
pinfo->pruning_steps = pruning_steps;
|
pinfo->pruning_steps = pruning_steps;
|
||||||
pinfo->present_parts = present_parts;
|
pinfo->present_parts = present_parts;
|
||||||
pinfo->nparts = nparts;
|
pinfo->nparts = nparts;
|
||||||
pinfo->extparams = NULL;
|
|
||||||
pinfo->execparams = NULL;
|
|
||||||
pinfo->subnode_map = subnode_map;
|
pinfo->subnode_map = subnode_map;
|
||||||
pinfo->subpart_map = subpart_map;
|
pinfo->subpart_map = subpart_map;
|
||||||
|
|
||||||
/*
|
/* Determine which pruning types should be enabled at this level */
|
||||||
* Extract Params matching partition key and record if we got any.
|
doruntimeprune |= analyze_partkey_exprs(pinfo, pruning_steps, partnatts);
|
||||||
* We'll not bother enabling run-time pruning if no params matched the
|
|
||||||
* partition key at any level of partitioning.
|
|
||||||
*/
|
|
||||||
gotparam |= pull_partkey_params(pinfo, pruning_steps);
|
|
||||||
|
|
||||||
pinfolist = lappend(pinfolist, pinfo);
|
pinfolist = lappend(pinfolist, pinfo);
|
||||||
}
|
}
|
||||||
|
@ -338,14 +337,10 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
|
||||||
pfree(relid_subnode_map);
|
pfree(relid_subnode_map);
|
||||||
pfree(relid_subpart_map);
|
pfree(relid_subpart_map);
|
||||||
|
|
||||||
if (gotparam)
|
if (doruntimeprune)
|
||||||
return pinfolist;
|
return pinfolist;
|
||||||
|
|
||||||
/*
|
/* No run-time pruning required. */
|
||||||
* If no Params were found to match the partition key on any of the
|
|
||||||
* partitioned relations then there's no point doing any run-time
|
|
||||||
* partition pruning.
|
|
||||||
*/
|
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,10 +438,11 @@ prune_append_rel_partitions(RelOptInfo *rel)
|
||||||
context.nparts = rel->nparts;
|
context.nparts = rel->nparts;
|
||||||
context.boundinfo = rel->boundinfo;
|
context.boundinfo = rel->boundinfo;
|
||||||
|
|
||||||
/* Not valid when being called from the planner */
|
/* These are not valid when being called from the planner */
|
||||||
context.planstate = NULL;
|
context.planstate = NULL;
|
||||||
context.safeparams = NULL;
|
|
||||||
context.exprstates = NULL;
|
context.exprstates = NULL;
|
||||||
|
context.exprhasexecparam = NULL;
|
||||||
|
context.evalexecparams = false;
|
||||||
|
|
||||||
/* Actual pruning happens here. */
|
/* Actual pruning happens here. */
|
||||||
partindexes = get_matching_partitions(&context, pruning_steps);
|
partindexes = get_matching_partitions(&context, pruning_steps);
|
||||||
|
@ -1478,6 +1474,10 @@ match_clause_to_partition_key(RelOptInfo *rel,
|
||||||
if (contain_volatile_functions((Node *) expr))
|
if (contain_volatile_functions((Node *) expr))
|
||||||
return PARTCLAUSE_UNSUPPORTED;
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
|
|
||||||
|
/* We can't prune using an expression with Vars. */
|
||||||
|
if (contain_var_clause((Node *) expr))
|
||||||
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the input types of the operator we're considering.
|
* Determine the input types of the operator we're considering.
|
||||||
*
|
*
|
||||||
|
@ -1624,10 +1624,14 @@ match_clause_to_partition_key(RelOptInfo *rel,
|
||||||
if (!op_strict(saop_op))
|
if (!op_strict(saop_op))
|
||||||
return PARTCLAUSE_UNSUPPORTED;
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
|
|
||||||
/* Useless if the array has any volatile functions. */
|
/* We can't use any volatile expressions to prune partitions. */
|
||||||
if (contain_volatile_functions((Node *) rightop))
|
if (contain_volatile_functions((Node *) rightop))
|
||||||
return PARTCLAUSE_UNSUPPORTED;
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
|
|
||||||
|
/* We can't prune using an expression with Vars. */
|
||||||
|
if (contain_var_clause((Node *) rightop))
|
||||||
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of NOT IN (..), we get a '<>', which we handle if list
|
* In case of NOT IN (..), we get a '<>', which we handle if list
|
||||||
* partitioning is in use and we're able to confirm that it's negator
|
* partitioning is in use and we're able to confirm that it's negator
|
||||||
|
@ -1655,7 +1659,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
|
||||||
return PARTCLAUSE_UNSUPPORTED;
|
return PARTCLAUSE_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return PARTCLAUSE_UNSUPPORTED; /* no useful negator */
|
return PARTCLAUSE_UNSUPPORTED; /* no useful negator */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2683,54 +2687,102 @@ get_matching_range_bounds(PartitionPruneContext *context,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pull_partkey_params
|
* pull_exec_paramids
|
||||||
* Loop through each pruning step and record each external and exec
|
* Returns a Bitmapset containing the paramids of all Params with
|
||||||
* Params being compared to the partition keys.
|
* paramkind = PARAM_EXEC in 'expr'.
|
||||||
|
*/
|
||||||
|
static Bitmapset *
|
||||||
|
pull_exec_paramids(Expr *expr)
|
||||||
|
{
|
||||||
|
Bitmapset *result = NULL;
|
||||||
|
|
||||||
|
(void) pull_exec_paramids_walker((Node *) expr, &result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
pull_exec_paramids_walker(Node *node, Bitmapset **context)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return false;
|
||||||
|
if (IsA(node, Param))
|
||||||
|
{
|
||||||
|
Param *param = (Param *) node;
|
||||||
|
|
||||||
|
if (param->paramkind == PARAM_EXEC)
|
||||||
|
*context = bms_add_member(*context, param->paramid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return expression_tree_walker(node, pull_exec_paramids_walker,
|
||||||
|
(void *) context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* analyze_partkey_exprs
|
||||||
|
* Loop through all pruning steps and identify which ones require
|
||||||
|
* executor startup-time or executor run-time pruning.
|
||||||
|
*
|
||||||
|
* Returns true if any executor partition pruning should be attempted at this
|
||||||
|
* level. Also fills fields of *pinfo to record how to process each step.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
pull_partkey_params(PartitionPruneInfo *pinfo, List *steps)
|
analyze_partkey_exprs(PartitionPruneInfo *pinfo, List *steps, int partnatts)
|
||||||
{
|
{
|
||||||
|
bool doruntimeprune = false;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
bool gotone = false;
|
|
||||||
|
/*
|
||||||
|
* Steps require run-time pruning if they contain EXEC_PARAM Params.
|
||||||
|
* Otherwise, if their expressions aren't simple Consts, they require
|
||||||
|
* startup-time pruning.
|
||||||
|
*/
|
||||||
|
pinfo->nexprs = list_length(steps) * partnatts;
|
||||||
|
pinfo->hasexecparam = (bool *) palloc0(sizeof(bool) * pinfo->nexprs);
|
||||||
|
pinfo->do_initial_prune = false;
|
||||||
|
pinfo->do_exec_prune = false;
|
||||||
|
pinfo->execparamids = NULL;
|
||||||
|
|
||||||
foreach(lc, steps)
|
foreach(lc, steps)
|
||||||
{
|
{
|
||||||
PartitionPruneStepOp *stepop = lfirst(lc);
|
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
|
||||||
ListCell *lc2;
|
ListCell *lc2;
|
||||||
|
int keyno;
|
||||||
|
|
||||||
if (!IsA(stepop, PartitionPruneStepOp))
|
if (!IsA(step, PartitionPruneStepOp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
foreach(lc2, stepop->exprs)
|
keyno = 0;
|
||||||
|
foreach(lc2, step->exprs)
|
||||||
{
|
{
|
||||||
Expr *expr = lfirst(lc2);
|
Expr *expr = lfirst(lc2);
|
||||||
|
|
||||||
if (IsA(expr, Param))
|
if (!IsA(expr, Const))
|
||||||
{
|
{
|
||||||
Param *param = (Param *) expr;
|
Bitmapset *execparamids = pull_exec_paramids(expr);
|
||||||
|
bool hasexecparams;
|
||||||
|
int stateidx = PruneCxtStateIdx(partnatts,
|
||||||
|
step->step.step_id,
|
||||||
|
keyno);
|
||||||
|
|
||||||
switch (param->paramkind)
|
Assert(stateidx < pinfo->nexprs);
|
||||||
{
|
hasexecparams = !bms_is_empty(execparamids);
|
||||||
case PARAM_EXTERN:
|
pinfo->hasexecparam[stateidx] = hasexecparams;
|
||||||
pinfo->extparams = bms_add_member(pinfo->extparams,
|
pinfo->execparamids = bms_join(pinfo->execparamids,
|
||||||
param->paramid);
|
execparamids);
|
||||||
break;
|
|
||||||
case PARAM_EXEC:
|
|
||||||
pinfo->execparams = bms_add_member(pinfo->execparams,
|
|
||||||
param->paramid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
if (hasexecparams)
|
||||||
elog(ERROR, "unrecognized paramkind: %d",
|
pinfo->do_exec_prune = true;
|
||||||
(int) param->paramkind);
|
else
|
||||||
break;
|
pinfo->do_initial_prune = true;
|
||||||
}
|
|
||||||
gotone = true;
|
doruntimeprune = true;
|
||||||
}
|
}
|
||||||
|
keyno++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gotone;
|
return doruntimeprune;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3026,42 +3078,47 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
|
||||||
* Evaluate 'expr', whose ExprState is stateidx of the context exprstate
|
* Evaluate 'expr', whose ExprState is stateidx of the context exprstate
|
||||||
* array; set *value to the resulting Datum. Return true if evaluation was
|
* array; set *value to the resulting Datum. Return true if evaluation was
|
||||||
* possible, otherwise false.
|
* possible, otherwise false.
|
||||||
|
*
|
||||||
|
* Note that the evaluated result may be in the per-tuple memory context of
|
||||||
|
* context->planstate->ps_ExprContext, and we may have leaked other memory
|
||||||
|
* there too. This memory must be recovered by resetting that ExprContext
|
||||||
|
* after we're done with the pruning operation (see execPartition.c).
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
partkey_datum_from_expr(PartitionPruneContext *context,
|
partkey_datum_from_expr(PartitionPruneContext *context,
|
||||||
Expr *expr, int stateidx, Datum *value)
|
Expr *expr, int stateidx, Datum *value)
|
||||||
{
|
{
|
||||||
switch (nodeTag(expr))
|
if (IsA(expr, Const))
|
||||||
{
|
{
|
||||||
case T_Const:
|
*value = ((Const *) expr)->constvalue;
|
||||||
*value = ((Const *) expr)->constvalue;
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* When called from the executor we'll have a valid planstate so we
|
||||||
|
* may be able to evaluate an expression which could not be folded to
|
||||||
|
* a Const during planning. Since run-time pruning can occur both
|
||||||
|
* during initialization of the executor or while it's running, we
|
||||||
|
* must be careful here to evaluate expressions containing PARAM_EXEC
|
||||||
|
* Params only when told it's OK.
|
||||||
|
*/
|
||||||
|
if (context->planstate &&
|
||||||
|
(context->evalexecparams ||
|
||||||
|
!context->exprhasexecparam[stateidx]))
|
||||||
|
{
|
||||||
|
ExprState *exprstate;
|
||||||
|
ExprContext *ectx;
|
||||||
|
bool isNull;
|
||||||
|
|
||||||
|
exprstate = context->exprstates[stateidx];
|
||||||
|
ectx = context->planstate->ps_ExprContext;
|
||||||
|
*value = ExecEvalExprSwitchContext(exprstate, ectx, &isNull);
|
||||||
|
if (isNull)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
case T_Param:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When being called from the executor we may be able to evaluate
|
|
||||||
* the Param's value.
|
|
||||||
*/
|
|
||||||
if (context->planstate &&
|
|
||||||
bms_is_member(((Param *) expr)->paramid, context->safeparams))
|
|
||||||
{
|
|
||||||
ExprState *exprstate;
|
|
||||||
ExprContext *ectx;
|
|
||||||
bool isNull;
|
|
||||||
|
|
||||||
exprstate = context->exprstates[stateidx];
|
|
||||||
ectx = context->planstate->ps_ExprContext;
|
|
||||||
*value = ExecEvalExprSwitchContext(exprstate, ectx, &isNull);
|
|
||||||
if (isNull)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -127,15 +127,16 @@ typedef struct PartitionTupleRouting
|
||||||
* subpart_map An array containing the offset into the
|
* subpart_map An array containing the offset into the
|
||||||
* 'partprunedata' array in PartitionPruning, or
|
* 'partprunedata' array in PartitionPruning, or
|
||||||
* -1 if there is no such element in that array.
|
* -1 if there is no such element in that array.
|
||||||
* present_parts A Bitmapset of the partition index that we have
|
* present_parts A Bitmapset of the partition indexes that we
|
||||||
* subnodes mapped for.
|
* have subnodes mapped for.
|
||||||
* context Contains the context details required to call
|
* context Contains the context details required to call
|
||||||
* the partition pruning code.
|
* the partition pruning code.
|
||||||
* pruning_steps Contains a list of PartitionPruneStep used to
|
* pruning_steps List of PartitionPruneSteps used to
|
||||||
* perform the actual pruning.
|
* perform the actual pruning.
|
||||||
* extparams Contains paramids of external params found
|
* do_initial_prune true if pruning should be performed during
|
||||||
* matching partition keys in 'pruning_steps'.
|
* executor startup.
|
||||||
* allparams As 'extparams' but also including exec params.
|
* do_exec_prune true if pruning should be performed during
|
||||||
|
* executor run.
|
||||||
*-----------------------
|
*-----------------------
|
||||||
*/
|
*/
|
||||||
typedef struct PartitionPruningData
|
typedef struct PartitionPruningData
|
||||||
|
@ -145,15 +146,14 @@ typedef struct PartitionPruningData
|
||||||
Bitmapset *present_parts;
|
Bitmapset *present_parts;
|
||||||
PartitionPruneContext context;
|
PartitionPruneContext context;
|
||||||
List *pruning_steps;
|
List *pruning_steps;
|
||||||
Bitmapset *extparams;
|
bool do_initial_prune;
|
||||||
Bitmapset *allparams;
|
bool do_exec_prune;
|
||||||
} PartitionPruningData;
|
} PartitionPruningData;
|
||||||
|
|
||||||
/*-----------------------
|
/*-----------------------
|
||||||
* PartitionPruneState - State object required for executor nodes to perform
|
* PartitionPruneState - State object required for executor nodes to perform
|
||||||
* partition pruning elimination of their subnodes. This encapsulates a
|
* partition pruning elimination of their subnodes. This encapsulates a
|
||||||
* flattened hierarchy of PartitionPruningData structs and also stores all
|
* flattened hierarchy of PartitionPruningData structs.
|
||||||
* paramids which were found to match the partition keys of each partition.
|
|
||||||
* This struct can be attached to node types which support arbitrary Lists of
|
* This struct can be attached to node types which support arbitrary Lists of
|
||||||
* subnodes containing partitions to allow subnodes to be eliminated due to
|
* subnodes containing partitions to allow subnodes to be eliminated due to
|
||||||
* the clauses being unable to match to any tuple that the subnode could
|
* the clauses being unable to match to any tuple that the subnode could
|
||||||
|
@ -163,24 +163,24 @@ typedef struct PartitionPruningData
|
||||||
* partitioned relation. First element contains the
|
* partitioned relation. First element contains the
|
||||||
* details for the target partitioned table.
|
* details for the target partitioned table.
|
||||||
* num_partprunedata Number of items in 'partprunedata' array.
|
* num_partprunedata Number of items in 'partprunedata' array.
|
||||||
|
* do_initial_prune true if pruning should be performed during executor
|
||||||
|
* startup (at any hierarchy level).
|
||||||
|
* do_exec_prune true if pruning should be performed during
|
||||||
|
* executor run (at any hierarchy level).
|
||||||
* prune_context A memory context which can be used to call the query
|
* prune_context A memory context which can be used to call the query
|
||||||
* planner's partition prune functions.
|
* planner's partition prune functions.
|
||||||
* extparams All PARAM_EXTERN paramids which were found to match a
|
* execparamids Contains paramids of PARAM_EXEC Params found within
|
||||||
* partition key in each of the contained
|
* any of the partprunedata structs.
|
||||||
* PartitionPruningData structs.
|
|
||||||
* execparams As above but for PARAM_EXEC.
|
|
||||||
* allparams Union of 'extparams' and 'execparams', saved to avoid
|
|
||||||
* recalculation.
|
|
||||||
*-----------------------
|
*-----------------------
|
||||||
*/
|
*/
|
||||||
typedef struct PartitionPruneState
|
typedef struct PartitionPruneState
|
||||||
{
|
{
|
||||||
PartitionPruningData *partprunedata;
|
PartitionPruningData *partprunedata;
|
||||||
int num_partprunedata;
|
int num_partprunedata;
|
||||||
|
bool do_initial_prune;
|
||||||
|
bool do_exec_prune;
|
||||||
MemoryContext prune_context;
|
MemoryContext prune_context;
|
||||||
Bitmapset *extparams;
|
Bitmapset *execparamids;
|
||||||
Bitmapset *execparams;
|
|
||||||
Bitmapset *allparams;
|
|
||||||
} PartitionPruneState;
|
} PartitionPruneState;
|
||||||
|
|
||||||
extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
|
extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
|
||||||
|
|
|
@ -1597,11 +1597,17 @@ typedef struct PartitionPruneInfo
|
||||||
List *pruning_steps; /* List of PartitionPruneStep */
|
List *pruning_steps; /* List of PartitionPruneStep */
|
||||||
Bitmapset *present_parts; /* Indexes of all partitions which subnodes
|
Bitmapset *present_parts; /* Indexes of all partitions which subnodes
|
||||||
* are present for. */
|
* are present for. */
|
||||||
int nparts; /* The length of the following two arrays */
|
int nparts; /* Length of subnode_map[] and subpart_map[] */
|
||||||
|
int nexprs; /* Length of hasexecparam[] */
|
||||||
int *subnode_map; /* subnode index by partition id, or -1 */
|
int *subnode_map; /* subnode index by partition id, or -1 */
|
||||||
int *subpart_map; /* subpart index by partition id, or -1 */
|
int *subpart_map; /* subpart index by partition id, or -1 */
|
||||||
Bitmapset *extparams; /* All external paramids seen in prunesteps */
|
bool *hasexecparam; /* true if corresponding pruning_step contains
|
||||||
Bitmapset *execparams; /* All exec paramids seen in prunesteps */
|
* any PARAM_EXEC Params. */
|
||||||
|
bool do_initial_prune; /* true if pruning should be performed
|
||||||
|
* during executor startup. */
|
||||||
|
bool do_exec_prune; /* true if pruning should be performed during
|
||||||
|
* executor run. */
|
||||||
|
Bitmapset *execparamids; /* All PARAM_EXEC Param IDs in pruning_steps */
|
||||||
} PartitionPruneInfo;
|
} PartitionPruneInfo;
|
||||||
|
|
||||||
#endif /* PRIMNODES_H */
|
#endif /* PRIMNODES_H */
|
||||||
|
|
|
@ -40,23 +40,27 @@ typedef struct PartitionPruneContext
|
||||||
PartitionBoundInfo boundinfo;
|
PartitionBoundInfo boundinfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can be set when the context is used from the executor to allow params
|
* This will be set when the context is used from the executor, to allow
|
||||||
* found matching the partition key to be evaluated.
|
* Params to be evaluated.
|
||||||
*/
|
*/
|
||||||
PlanState *planstate;
|
PlanState *planstate;
|
||||||
|
|
||||||
/*
|
|
||||||
* Parameters that are safe to be used for partition pruning. execparams
|
|
||||||
* are not safe to use until the executor is running.
|
|
||||||
*/
|
|
||||||
Bitmapset *safeparams;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Array of ExprStates, indexed as per PruneCtxStateIdx; one for each
|
* Array of ExprStates, indexed as per PruneCtxStateIdx; one for each
|
||||||
* partkey in each pruning step. Allocated if planstate is non-NULL,
|
* partkey in each pruning step. Allocated if planstate is non-NULL,
|
||||||
* otherwise NULL.
|
* otherwise NULL.
|
||||||
*/
|
*/
|
||||||
ExprState **exprstates;
|
ExprState **exprstates;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Similar array of flags, each true if corresponding 'exprstate'
|
||||||
|
* expression contains any PARAM_EXEC Params. (Can be NULL if planstate
|
||||||
|
* is NULL.)
|
||||||
|
*/
|
||||||
|
bool *exprhasexecparam;
|
||||||
|
|
||||||
|
/* true if it's safe to evaluate PARAM_EXEC Params */
|
||||||
|
bool evalexecparams;
|
||||||
} PartitionPruneContext;
|
} PartitionPruneContext;
|
||||||
|
|
||||||
#define PruneCxtStateIdx(partnatts, step_id, keyno) \
|
#define PruneCxtStateIdx(partnatts, step_id, keyno) \
|
||||||
|
|
|
@ -1726,8 +1726,8 @@ explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4);
|
||||||
Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
|
Filter: ((a >= $1) AND (a <= $2) AND (b < 3))
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
-- Ensure a mix of external and exec params work together at different
|
-- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at
|
||||||
-- levels of partitioning.
|
-- different levels of partitioning.
|
||||||
prepare ab_q2 (int, int) as
|
prepare ab_q2 (int, int) as
|
||||||
select a from ab where a between $1 and $2 and b < (select 3);
|
select a from ab where a between $1 and $2 and b < (select 3);
|
||||||
execute ab_q2 (1, 8);
|
execute ab_q2 (1, 8);
|
||||||
|
@ -1770,7 +1770,7 @@ explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2);
|
||||||
Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
|
Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
-- As above, but with swap the exec param to the first partition level
|
-- As above, but swap the PARAM_EXEC Param to the first partition level
|
||||||
prepare ab_q3 (int, int) as
|
prepare ab_q3 (int, int) as
|
||||||
select a from ab where b between $1 and $2 and a < (select 3);
|
select a from ab where b between $1 and $2 and a < (select 3);
|
||||||
execute ab_q3 (1, 8);
|
execute ab_q3 (1, 8);
|
||||||
|
@ -1835,6 +1835,54 @@ fetch backward all from cur;
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
begin;
|
||||||
|
-- Test run-time pruning using stable functions
|
||||||
|
create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
|
||||||
|
-- Ensure pruning works using a stable function containing no Vars
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1);
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------
|
||||||
|
Append (actual rows=1 loops=1)
|
||||||
|
Subplans Removed: 3
|
||||||
|
-> Seq Scan on list_part1 (actual rows=1 loops=1)
|
||||||
|
Filter: (a = list_part_fn(1))
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
-- Ensure pruning does not take place when the function has a Var parameter
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a);
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------
|
||||||
|
Append (actual rows=4 loops=1)
|
||||||
|
-> Seq Scan on list_part1 (actual rows=1 loops=1)
|
||||||
|
Filter: (a = list_part_fn(a))
|
||||||
|
-> Seq Scan on list_part2 (actual rows=1 loops=1)
|
||||||
|
Filter: (a = list_part_fn(a))
|
||||||
|
-> Seq Scan on list_part3 (actual rows=1 loops=1)
|
||||||
|
Filter: (a = list_part_fn(a))
|
||||||
|
-> Seq Scan on list_part4 (actual rows=1 loops=1)
|
||||||
|
Filter: (a = list_part_fn(a))
|
||||||
|
(9 rows)
|
||||||
|
|
||||||
|
-- Ensure pruning does not take place when the expression contains a Var.
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------
|
||||||
|
Append (actual rows=0 loops=1)
|
||||||
|
-> Seq Scan on list_part1 (actual rows=0 loops=1)
|
||||||
|
Filter: (a = (list_part_fn(1) + a))
|
||||||
|
Rows Removed by Filter: 1
|
||||||
|
-> Seq Scan on list_part2 (actual rows=0 loops=1)
|
||||||
|
Filter: (a = (list_part_fn(1) + a))
|
||||||
|
Rows Removed by Filter: 1
|
||||||
|
-> Seq Scan on list_part3 (actual rows=0 loops=1)
|
||||||
|
Filter: (a = (list_part_fn(1) + a))
|
||||||
|
Rows Removed by Filter: 1
|
||||||
|
-> Seq Scan on list_part4 (actual rows=0 loops=1)
|
||||||
|
Filter: (a = (list_part_fn(1) + a))
|
||||||
|
Rows Removed by Filter: 1
|
||||||
|
(13 rows)
|
||||||
|
|
||||||
|
rollback;
|
||||||
drop table list_part;
|
drop table list_part;
|
||||||
-- Parallel append
|
-- Parallel append
|
||||||
-- Suppress the number of loops each parallel node runs for. This is because
|
-- Suppress the number of loops each parallel node runs for. This is because
|
||||||
|
@ -2007,7 +2055,7 @@ select explain_parallel_append('execute ab_q5 (33, 44, 55)');
|
||||||
Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
|
Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
|
||||||
(9 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Test Parallel Append with exec params
|
-- Test Parallel Append with PARAM_EXEC Params
|
||||||
select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
|
select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
|
||||||
explain_parallel_append
|
explain_parallel_append
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
|
@ -2079,6 +2127,40 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on
|
||||||
Index Cond: (a = a.a)
|
Index Cond: (a = a.a)
|
||||||
(27 rows)
|
(27 rows)
|
||||||
|
|
||||||
|
-- Ensure the same partitions are pruned when we make the nested loop
|
||||||
|
-- parameter an Expr rather than a plain Param.
|
||||||
|
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)');
|
||||||
|
explain_parallel_append
|
||||||
|
---------------------------------------------------------------------------------------------------
|
||||||
|
Finalize Aggregate (actual rows=1 loops=1)
|
||||||
|
-> Gather (actual rows=2 loops=1)
|
||||||
|
Workers Planned: 1
|
||||||
|
Workers Launched: 1
|
||||||
|
-> Partial Aggregate (actual rows=1 loops=2)
|
||||||
|
-> Nested Loop (actual rows=0 loops=2)
|
||||||
|
-> Parallel Seq Scan on lprt_a a (actual rows=51 loops=N)
|
||||||
|
Filter: (a = ANY ('{0,0,1}'::integer[]))
|
||||||
|
-> Append (actual rows=0 loops=102)
|
||||||
|
-> Index Scan using ab_a1_b1_a_idx on ab_a1_b1 (actual rows=0 loops=2)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a1_b2_a_idx on ab_a1_b2 (actual rows=0 loops=2)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a1_b3_a_idx on ab_a1_b3 (actual rows=0 loops=2)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a2_b1_a_idx on ab_a2_b1 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a2_b2_a_idx on ab_a2_b2 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a2_b3_a_idx on ab_a2_b3 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a3_b1_a_idx on ab_a3_b1 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a3_b2_a_idx on ab_a3_b2 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
-> Index Scan using ab_a3_b3_a_idx on ab_a3_b3 (never executed)
|
||||||
|
Index Cond: (a = (a.a + 0))
|
||||||
|
(27 rows)
|
||||||
|
|
||||||
insert into lprt_a values(3),(3);
|
insert into lprt_a values(3),(3);
|
||||||
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
|
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
|
||||||
explain_parallel_append
|
explain_parallel_append
|
||||||
|
|
|
@ -348,8 +348,8 @@ execute ab_q1 (1, 8);
|
||||||
explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2);
|
explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 2);
|
||||||
explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4);
|
explain (analyze, costs off, summary off, timing off) execute ab_q1 (2, 4);
|
||||||
|
|
||||||
-- Ensure a mix of external and exec params work together at different
|
-- Ensure a mix of PARAM_EXTERN and PARAM_EXEC Params work together at
|
||||||
-- levels of partitioning.
|
-- different levels of partitioning.
|
||||||
prepare ab_q2 (int, int) as
|
prepare ab_q2 (int, int) as
|
||||||
select a from ab where a between $1 and $2 and b < (select 3);
|
select a from ab where a between $1 and $2 and b < (select 3);
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@ execute ab_q2 (1, 8);
|
||||||
|
|
||||||
explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2);
|
explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2);
|
||||||
|
|
||||||
-- As above, but with swap the exec param to the first partition level
|
-- As above, but swap the PARAM_EXEC Param to the first partition level
|
||||||
prepare ab_q3 (int, int) as
|
prepare ab_q3 (int, int) as
|
||||||
select a from ab where b between $1 and $2 and a < (select 3);
|
select a from ab where b between $1 and $2 and a < (select 3);
|
||||||
|
|
||||||
|
@ -396,6 +396,22 @@ fetch backward all from cur;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
-- Test run-time pruning using stable functions
|
||||||
|
create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable;
|
||||||
|
|
||||||
|
-- Ensure pruning works using a stable function containing no Vars
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1);
|
||||||
|
|
||||||
|
-- Ensure pruning does not take place when the function has a Var parameter
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a);
|
||||||
|
|
||||||
|
-- Ensure pruning does not take place when the expression contains a Var.
|
||||||
|
explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a;
|
||||||
|
|
||||||
|
rollback;
|
||||||
|
|
||||||
drop table list_part;
|
drop table list_part;
|
||||||
|
|
||||||
-- Parallel append
|
-- Parallel append
|
||||||
|
@ -458,7 +474,7 @@ select explain_parallel_append('execute ab_q5 (2, 3, 3)');
|
||||||
-- We'll still get a single subplan in this case, but it should not be scanned.
|
-- We'll still get a single subplan in this case, but it should not be scanned.
|
||||||
select explain_parallel_append('execute ab_q5 (33, 44, 55)');
|
select explain_parallel_append('execute ab_q5 (33, 44, 55)');
|
||||||
|
|
||||||
-- Test Parallel Append with exec params
|
-- Test Parallel Append with PARAM_EXEC Params
|
||||||
select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
|
select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
|
||||||
|
|
||||||
-- Test pruning during parallel nested loop query
|
-- Test pruning during parallel nested loop query
|
||||||
|
@ -486,6 +502,10 @@ set enable_mergejoin = 0;
|
||||||
|
|
||||||
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)');
|
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(0, 0, 1)');
|
||||||
|
|
||||||
|
-- Ensure the same partitions are pruned when we make the nested loop
|
||||||
|
-- parameter an Expr rather than a plain Param.
|
||||||
|
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a + 0 where a.a in(0, 0, 1)');
|
||||||
|
|
||||||
insert into lprt_a values(3),(3);
|
insert into lprt_a values(3),(3);
|
||||||
|
|
||||||
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
|
select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on ab.a = a.a where a.a in(1, 0, 3)');
|
||||||
|
|
Loading…
Reference in New Issue