diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2019-05-17 19:44:19 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2019-05-17 19:44:19 -0400 |
commit | 592d5d75be9720e575e76ba35c3ff04659ec0603 (patch) | |
tree | 3135d26a4c7abbf06028712b8aef370523a630f7 /src/backend/executor | |
parent | 51948c4e1fdef88ba9b953bd7b58d19a348732be (diff) |
Restructure creation of run-time pruning steps.
Previously, gen_partprune_steps() always built executor pruning steps
using all suitable clauses, including those containing PARAM_EXEC
Params. This meant that the pruning steps were only completely safe
for executor run-time (scan start) pruning. To prune at executor
startup, we had to ignore the steps involving exec Params. But this
doesn't really work in general, since there may be logic changes
needed as well --- for example, pruning according to the last operator's
btree strategy is the wrong thing if we're not applying that operator.
The rules embodied in gen_partprune_steps() and its minions are
sufficiently complicated that tracking their incremental effects in
other logic seems quite impractical.
Short of a complete redesign, the only safe fix seems to be to run
gen_partprune_steps() twice, once to create executor startup pruning
steps and then again for run-time pruning steps. We can save a few
cycles however by noting during the first scan whether we rejected
any clauses because they involved exec Params --- if not, we don't
need to do the second scan.
In support of this, refactor the internal APIs in partprune.c to make
more use of passing information in the GeneratePruningStepsContext
struct, rather than as separate arguments.
This is, I hope, the last piece of our response to a bug report from
Alan Jackson. Back-patch to v11 where this code came in.
Discussion: https://postgr.es/m/FAD28A83-AC73-489E-A058-2681FA31D648@tvsquared.com
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execPartition.c | 206 |
1 files changed, 121 insertions, 85 deletions
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 0595d6bf1d6..306f695de16 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -78,6 +78,10 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel, bool *isnull, int maxfieldlen); static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map); +static void ExecInitPruningContext(PartitionPruneContext *context, + Oid reloid, + List *pruning_steps, + PlanState *planstate); static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, @@ -1525,18 +1529,13 @@ ExecCreatePartitionPruneState(PlanState *planstate, { PartitionedRelPruneInfo *pinfo = lfirst_node(PartitionedRelPruneInfo, lc2); PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j]; - PartitionPruneContext *context = &pprune->context; - PartitionDesc partdesc; - PartitionKey partkey; - int partnatts; - int n_steps; - ListCell *lc3; /* * We must copy the subplan_map rather than pointing directly to * the plan's version, as we may end up making modifications to it * later. */ + pprune->nparts = pinfo->nparts; pprune->subplan_map = palloc(sizeof(int) * pinfo->nparts); memcpy(pprune->subplan_map, pinfo->subplan_map, sizeof(int) * pinfo->nparts); @@ -1548,76 +1547,28 @@ ExecCreatePartitionPruneState(PlanState *planstate, pprune->present_parts = bms_copy(pinfo->present_parts); /* - * We need to hold a pin on the partitioned table's relcache entry - * so that we can rely on its copies of the table's partition key - * and partition descriptor. We need not get a lock though; one - * should have been acquired already by InitPlan or - * ExecLockNonLeafAppendTables. + * Initialize pruning contexts as needed. */ - context->partrel = relation_open(pinfo->reloid, NoLock); - - partkey = RelationGetPartitionKey(context->partrel); - partdesc = RelationGetPartitionDesc(context->partrel); - n_steps = list_length(pinfo->pruning_steps); - - context->strategy = partkey->strategy; - context->partnatts = partnatts = partkey->partnatts; - context->nparts = pinfo->nparts; - context->boundinfo = partdesc->boundinfo; - context->partcollation = partkey->partcollation; - context->partsupfunc = partkey->partsupfunc; - - /* We'll look up type-specific support functions as needed */ - context->stepcmpfuncs = (FmgrInfo *) - palloc0(sizeof(FmgrInfo) * n_steps * partnatts); - - context->ppccontext = CurrentMemoryContext; - context->planstate = planstate; - - /* Initialize expression state for each expression we need */ - context->exprstates = (ExprState **) - palloc0(sizeof(ExprState *) * n_steps * partnatts); - foreach(lc3, pinfo->pruning_steps) + pprune->initial_pruning_steps = pinfo->initial_pruning_steps; + if (pinfo->initial_pruning_steps) { - PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc3); - ListCell *lc4; - int keyno; - - /* not needed for other step kinds */ - if (!IsA(step, PartitionPruneStepOp)) - continue; - - Assert(list_length(step->exprs) <= partnatts); - - keyno = 0; - foreach(lc4, step->exprs) - { - Expr *expr = (Expr *) lfirst(lc4); - - /* not needed for Consts */ - if (!IsA(expr, Const)) - { - int stateidx = PruneCxtStateIdx(partnatts, - step->step.step_id, - keyno); - - context->exprstates[stateidx] = - ExecInitExpr(expr, context->planstate); - } - keyno++; - } + ExecInitPruningContext(&pprune->initial_context, + pinfo->reloid, + pinfo->initial_pruning_steps, + planstate); + /* Record whether initial pruning is needed at any level */ + prunestate->do_initial_prune = true; + } + pprune->exec_pruning_steps = pinfo->exec_pruning_steps; + if (pinfo->exec_pruning_steps) + { + ExecInitPruningContext(&pprune->exec_context, + pinfo->reloid, + pinfo->exec_pruning_steps, + planstate); + /* Record whether exec pruning is needed at any level */ + prunestate->do_exec_prune = true; } - - /* Array is not modified at runtime, so just point to plan's copy */ - context->exprhasexecparam = pinfo->hasexecparam; - - pprune->pruning_steps = pinfo->pruning_steps; - 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 IDs of all PARAM_EXEC Params affecting the @@ -1654,7 +1605,90 @@ ExecDestroyPartitionPruneState(PartitionPruneState *prunestate) int j; for (j = 0; j < prunedata->num_partrelprunedata; j++) - relation_close(pprune[j].context.partrel, NoLock); + { + if (pprune[j].initial_pruning_steps) + relation_close(pprune[j].initial_context.partrel, NoLock); + if (pprune[j].exec_pruning_steps) + relation_close(pprune[j].exec_context.partrel, NoLock); + } + } +} + +/* + * Initialize a PartitionPruneContext for the given list of pruning steps. + */ +static void +ExecInitPruningContext(PartitionPruneContext *context, + Oid reloid, + List *pruning_steps, + PlanState *planstate) +{ + PartitionKey partkey; + PartitionDesc partdesc; + int n_steps; + int partnatts; + ListCell *lc; + + /* + * We need to hold a pin on the partitioned table's relcache entry + * so that we can rely on its copies of the table's partition key + * and partition descriptor. We need not get a lock though; one + * should have been acquired already by InitPlan or + * ExecLockNonLeafAppendTables. + */ + context->partrel = relation_open(reloid, NoLock); + + partkey = RelationGetPartitionKey(context->partrel); + partdesc = RelationGetPartitionDesc(context->partrel); + + n_steps = list_length(pruning_steps); + + context->strategy = partkey->strategy; + context->partnatts = partnatts = partkey->partnatts; + context->nparts = partdesc->nparts; + context->boundinfo = partdesc->boundinfo; + context->partcollation = partkey->partcollation; + context->partsupfunc = partkey->partsupfunc; + + /* We'll look up type-specific support functions as needed */ + context->stepcmpfuncs = (FmgrInfo *) + palloc0(sizeof(FmgrInfo) * n_steps * partnatts); + + context->ppccontext = CurrentMemoryContext; + context->planstate = planstate; + + /* Initialize expression state for each expression we need */ + context->exprstates = (ExprState **) + palloc0(sizeof(ExprState *) * n_steps * partnatts); + foreach(lc, pruning_steps) + { + PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc); + ListCell *lc2; + int keyno; + + /* not needed for other step kinds */ + if (!IsA(step, PartitionPruneStepOp)) + continue; + + Assert(list_length(step->exprs) <= partnatts); + + keyno = 0; + foreach(lc2, step->exprs) + { + Expr *expr = (Expr *) lfirst(lc2); + + /* not needed for Consts */ + if (!IsA(expr, Const)) + { + int stateidx = PruneCxtStateIdx(partnatts, + step->step.step_id, + keyno); + + context->exprstates[stateidx] = + ExecInitExpr(expr, context->planstate); + } + keyno++; + } } } @@ -1702,7 +1736,8 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans) find_matching_subplans_recurse(prunedata, pprune, true, &result); /* Expression eval may have used space in node's ps_ExprContext too */ - ResetExprContext(pprune->context.planstate->ps_ExprContext); + if (pprune->initial_pruning_steps) + ResetExprContext(pprune->initial_context.planstate->ps_ExprContext); } MemoryContextSwitchTo(oldcontext); @@ -1769,7 +1804,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans) for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--) { PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j]; - int nparts = pprune->context.nparts; + int nparts = pprune->nparts; int k; /* We just rebuild present_parts from scratch */ @@ -1854,7 +1889,8 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate) find_matching_subplans_recurse(prunedata, pprune, false, &result); /* Expression eval may have used space in node's ps_ExprContext too */ - ResetExprContext(pprune->context.planstate->ps_ExprContext); + if (pprune->exec_pruning_steps) + ResetExprContext(pprune->exec_context.planstate->ps_ExprContext); } MemoryContextSwitchTo(oldcontext); @@ -1890,15 +1926,15 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata, check_stack_depth(); /* Only prune if pruning would be useful at this level. */ - if (initial_prune ? pprune->do_initial_prune : pprune->do_exec_prune) + if (initial_prune && pprune->initial_pruning_steps) { - 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); + partset = get_matching_partitions(&pprune->initial_context, + pprune->initial_pruning_steps); + } + else if (!initial_prune && pprune->exec_pruning_steps) + { + partset = get_matching_partitions(&pprune->exec_context, + pprune->exec_pruning_steps); } else { |