1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

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:
Tom Lane
2018-06-10 15:22:25 -04:00
parent c83e202990
commit 73b7f48f78
11 changed files with 461 additions and 271 deletions

View File

@@ -53,6 +53,7 @@
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "partitioning/partprune.h"
#include "partitioning/partbounds.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,
StrategyNumber opstrategy, Datum *values, int nvalues,
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,
PartitionPruneStepOp *opstep);
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
@@ -180,12 +184,12 @@ static bool partkey_datum_from_expr(PartitionPruneContext *context,
* pruning to take place.
*
* 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.
*
* If no Params were found to match the partition key in any of the
* 'partitioned_rels', then we return NIL. In such a case run-time partition
* pruning would be useless.
* If no non-Const expressions are being compared to the partition key in any
* of the 'partitioned_rels', then we return NIL. In such a case run-time
* partition pruning would be useless, since the planner did it already.
*/
List *
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_subpart_map;
int i;
bool gotparam = false;
bool doruntimeprune = false;
/*
* 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++;
}
/* We now build a PartitionPruneInfo for each partition_rels */
/* We now build a PartitionPruneInfo for each rel in partition_rels */
foreach(lc, partition_rels)
{
Index rti = lfirst_int(lc);
@@ -238,6 +242,7 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
RangeTblEntry *rte;
Bitmapset *present_parts;
int nparts = subpart->nparts;
int partnatts = subpart->part_scheme->partnatts;
int *subnode_map;
int *subpart_map;
List *partprunequal;
@@ -320,17 +325,11 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
pinfo->pruning_steps = pruning_steps;
pinfo->present_parts = present_parts;
pinfo->nparts = nparts;
pinfo->extparams = NULL;
pinfo->execparams = NULL;
pinfo->subnode_map = subnode_map;
pinfo->subpart_map = subpart_map;
/*
* Extract Params matching partition key and record if we got any.
* 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);
/* Determine which pruning types should be enabled at this level */
doruntimeprune |= analyze_partkey_exprs(pinfo, pruning_steps, partnatts);
pinfolist = lappend(pinfolist, pinfo);
}
@@ -338,14 +337,10 @@ make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
pfree(relid_subnode_map);
pfree(relid_subpart_map);
if (gotparam)
if (doruntimeprune)
return pinfolist;
/*
* 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.
*/
/* No run-time pruning required. */
return NIL;
}
@@ -443,10 +438,11 @@ prune_append_rel_partitions(RelOptInfo *rel)
context.nparts = rel->nparts;
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.safeparams = NULL;
context.exprstates = NULL;
context.exprhasexecparam = NULL;
context.evalexecparams = false;
/* Actual pruning happens here. */
partindexes = get_matching_partitions(&context, pruning_steps);
@@ -1478,6 +1474,10 @@ match_clause_to_partition_key(RelOptInfo *rel,
if (contain_volatile_functions((Node *) expr))
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.
*
@@ -1624,10 +1624,14 @@ match_clause_to_partition_key(RelOptInfo *rel,
if (!op_strict(saop_op))
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))
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
* 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;
}
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
* Loop through each pruning step and record each external and exec
* Params being compared to the partition keys.
* pull_exec_paramids
* Returns a Bitmapset containing the paramids of all Params with
* 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
pull_partkey_params(PartitionPruneInfo *pinfo, List *steps)
analyze_partkey_exprs(PartitionPruneInfo *pinfo, List *steps, int partnatts)
{
bool doruntimeprune = false;
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)
{
PartitionPruneStepOp *stepop = lfirst(lc);
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
ListCell *lc2;
int keyno;
if (!IsA(stepop, PartitionPruneStepOp))
if (!IsA(step, PartitionPruneStepOp))
continue;
foreach(lc2, stepop->exprs)
keyno = 0;
foreach(lc2, step->exprs)
{
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)
{
case PARAM_EXTERN:
pinfo->extparams = bms_add_member(pinfo->extparams,
param->paramid);
break;
case PARAM_EXEC:
pinfo->execparams = bms_add_member(pinfo->execparams,
param->paramid);
break;
Assert(stateidx < pinfo->nexprs);
hasexecparams = !bms_is_empty(execparamids);
pinfo->hasexecparam[stateidx] = hasexecparams;
pinfo->execparamids = bms_join(pinfo->execparamids,
execparamids);
default:
elog(ERROR, "unrecognized paramkind: %d",
(int) param->paramkind);
break;
}
gotone = true;
if (hasexecparams)
pinfo->do_exec_prune = true;
else
pinfo->do_initial_prune = 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
* array; set *value to the resulting Datum. Return true if evaluation was
* 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
partkey_datum_from_expr(PartitionPruneContext *context,
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;
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;