aboutsummaryrefslogtreecommitdiff
path: root/src/backend/partitioning/partprune.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/partitioning/partprune.c')
-rw-r--r--src/backend/partitioning/partprune.c267
1 files changed, 264 insertions, 3 deletions
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index e70770e7b33..417e1fee815 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -1,17 +1,22 @@
/*-------------------------------------------------------------------------
*
* partprune.c
- * Support for partition pruning during query planning
+ * Support for partition pruning during query planning and execution
*
* This module implements partition pruning using the information contained in
- * table's partition descriptor and query clauses.
+ * table's partition descriptor, query clauses, and run-time parameters.
*
* During planning, clauses that can be matched to the table's partition key
* are turned into a set of "pruning steps", which are then executed to
* produce a set of partitions (as indexes of the RelOptInfo->part_rels array)
- * that satisfy the constraints in the step Partitions not in the set are said
+ * that satisfy the constraints in the step. Partitions not in the set are said
* to have been pruned.
*
+ * A base pruning step may also consist of expressions whose values are only
+ * known during execution, such as Params, in which case pruning cannot occur
+ * entirely during planning. In that case, such steps are included alongside
+ * the plan, so that they can be used by the executor for further pruning.
+ *
* There are two kinds of pruning steps: a "base" pruning step, which contains
* information extracted from one or more clauses that are matched to the
* (possibly multi-column) partition key, such as the expressions whose values
@@ -39,10 +44,12 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
+#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/pathnode.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/prep.h"
@@ -153,6 +160,7 @@ 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 PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
PartitionPruneStepOp *opstep);
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
@@ -163,6 +171,181 @@ static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause,
static bool partkey_datum_from_expr(PartitionPruneContext *context,
Expr *expr, Datum *value);
+/*
+ * make_partition_pruneinfo
+ * Build List of PartitionPruneInfos, one for each 'partitioned_rels'.
+ * These can be used in the executor to allow additional partition
+ * 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'
+ * 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.
+ */
+List *
+make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
+ List *subpaths, List *prunequal)
+{
+ RelOptInfo *targetpart = NULL;
+ ListCell *lc;
+ List *pinfolist = NIL;
+ int *relid_subnode_map;
+ int *relid_subpart_map;
+ int i;
+ bool gotparam = false;
+
+ /*
+ * Allocate two arrays to store the 1-based indexes of the 'subpaths' and
+ * 'partitioned_rels' by relid.
+ */
+ relid_subnode_map = palloc0(sizeof(int) * root->simple_rel_array_size);
+ relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
+
+ i = 1;
+ foreach(lc, subpaths)
+ {
+ Path *path = (Path *) lfirst(lc);
+ RelOptInfo *pathrel = path->parent;
+
+ Assert(IS_SIMPLE_REL(pathrel));
+ Assert(pathrel->relid < root->simple_rel_array_size);
+
+ relid_subnode_map[pathrel->relid] = i++;
+ }
+
+ /* Likewise for the partition_rels */
+ i = 1;
+ foreach(lc, partition_rels)
+ {
+ Index rti = lfirst_int(lc);
+
+ Assert(rti < root->simple_rel_array_size);
+
+ relid_subpart_map[rti] = i++;
+ }
+
+ /* We now build a PartitionPruneInfo for each partition_rels */
+ foreach(lc, partition_rels)
+ {
+ Index rti = lfirst_int(lc);
+ RelOptInfo *subpart = find_base_rel(root, rti);
+ PartitionPruneInfo *pinfo;
+ RangeTblEntry *rte;
+ Bitmapset *present_parts;
+ int nparts = subpart->nparts;
+ int *subnode_map;
+ int *subpart_map;
+ List *partprunequal;
+ List *pruning_steps;
+ bool contradictory;
+
+ /*
+ * The first item in the list is the target partitioned relation. The
+ * quals belong to this relation, so require no translation.
+ */
+ if (!targetpart)
+ {
+ targetpart = subpart;
+ partprunequal = prunequal;
+ }
+ else
+ {
+ /*
+ * For sub-partitioned tables the columns may not be in the same
+ * order as the parent, so we must translate the prunequal to make
+ * it compatible with this relation.
+ */
+ partprunequal = (List *)
+ adjust_appendrel_attrs_multilevel(root,
+ (Node *) prunequal,
+ subpart->relids,
+ targetpart->relids);
+ }
+
+ pruning_steps = gen_partprune_steps(subpart, partprunequal,
+ &contradictory);
+
+ if (contradictory)
+ {
+ /*
+ * This shouldn't happen as the planner should have detected this
+ * earlier. However, we do use additional quals from parameterized
+ * paths here. These do only compare Params to the partition key,
+ * so this shouldn't cause the discovery of any new qual
+ * contradictions that were not previously discovered as the Param
+ * values are unknown during planning. Anyway, we'd better do
+ * something sane here, so let's just disable run-time pruning.
+ */
+ return NIL;
+ }
+
+ subnode_map = (int *) palloc(nparts * sizeof(int));
+ subpart_map = (int *) palloc(nparts * sizeof(int));
+ present_parts = NULL;
+
+ /*
+ * Loop over each partition of the partitioned rel and record the
+ * subpath index for each. Any partitions which are not present in
+ * the subpaths List will be set to -1, and any sub-partitioned table
+ * which is not present will also be set to -1.
+ */
+ for (i = 0; i < nparts; i++)
+ {
+ RelOptInfo *partrel = subpart->part_rels[i];
+ int subnodeidx = relid_subnode_map[partrel->relid] - 1;
+ int subpartidx = relid_subpart_map[partrel->relid] - 1;
+
+ subnode_map[i] = subnodeidx;
+ subpart_map[i] = subpartidx;
+
+ /*
+ * Record the indexes of all the partition indexes that we have
+ * subnodes or subparts for. This allows an optimization to skip
+ * attempting any run-time pruning when no Params are found
+ * matching the partition key at this level.
+ */
+ if (subnodeidx >= 0 || subpartidx >= 0)
+ present_parts = bms_add_member(present_parts, i);
+ }
+
+ rte = root->simple_rte_array[subpart->relid];
+
+ pinfo = makeNode(PartitionPruneInfo);
+ pinfo->reloid = rte->relid;
+ 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);
+
+ pinfolist = lappend(pinfolist, pinfo);
+ }
+
+ pfree(relid_subnode_map);
+ pfree(relid_subpart_map);
+
+ if (gotparam)
+ 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.
+ */
+ return NIL;
+}
/*
* gen_partprune_steps
@@ -258,6 +441,10 @@ prune_append_rel_partitions(RelOptInfo *rel)
context.nparts = rel->nparts;
context.boundinfo = rel->boundinfo;
+ /* Not valid when being called from the planner */
+ context.planstate = NULL;
+ context.safeparams = NULL;
+
/* Actual pruning happens here. */
partindexes = get_matching_partitions(&context, pruning_steps);
@@ -2493,6 +2680,57 @@ 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.
+ */
+static bool
+pull_partkey_params(PartitionPruneInfo *pinfo, List *steps)
+{
+ ListCell *lc;
+ bool gotone = false;
+
+ foreach(lc, steps)
+ {
+ PartitionPruneStepOp *stepop = lfirst(lc);
+ ListCell *lc2;
+
+ if (!IsA(stepop, PartitionPruneStepOp))
+ continue;
+
+ foreach(lc2, stepop->exprs)
+ {
+ Expr *expr = lfirst(lc2);
+
+ if (IsA(expr, Param))
+ {
+ Param *param = (Param *) expr;
+
+ 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;
+
+ default:
+ elog(ERROR, "unrecognized paramkind: %d",
+ (int) param->paramkind);
+ break;
+ }
+ gotone = true;
+ }
+ }
+ }
+
+ return gotone;
+}
+
+/*
* perform_pruning_base_step
* Determines the indexes of datums that satisfy conditions specified in
* 'opstep'.
@@ -2793,6 +3031,29 @@ partkey_datum_from_expr(PartitionPruneContext *context,
*value = ((Const *) expr)->constvalue;
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;
+ bool isNull;
+
+ exprstate = ExecInitExpr(expr, context->planstate);
+
+ *value = ExecEvalExprSwitchContext(exprstate,
+ context->planstate->ps_ExprContext,
+ &isNull);
+ if (isNull)
+ return false;
+
+ return true;
+ }
+
default:
break;
}