runtime_prune_drowley_v3.patch
application/octet-stream
Filename: runtime_prune_drowley_v3.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 212 | 23 |
| src/backend/executor/nodeAppend.c | 155 | 26 |
| src/backend/nodes/bitmapset.c | 95 | 0 |
| src/backend/nodes/copyfuncs.c | 30 | 0 |
| src/backend/optimizer/path/allpaths.c | 48 | 23 |
| src/backend/optimizer/path/joinrels.c | 2 | 2 |
| src/backend/optimizer/plan/createplan.c | 65 | 5 |
| src/backend/optimizer/plan/planner.c | 4 | 2 |
| src/backend/optimizer/prep/prepunion.c | 4 | 4 |
| src/backend/optimizer/util/clauses.c | 19 | 0 |
| src/backend/optimizer/util/pathnode.c | 21 | 4 |
| src/backend/optimizer/util/relnode.c | 3 | 0 |
| src/backend/utils/cache/plancache.c | 8 | 1 |
| src/include/catalog/partition.h | 5 | 0 |
| src/include/nodes/bitmapset.h | 1 | 0 |
| src/include/nodes/execnodes.h | 5 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/plannodes.h | 8 | 0 |
| src/include/nodes/primnodes.h | 20 | 0 |
| src/include/nodes/relation.h | 7 | 0 |
| src/include/optimizer/clauses.h | 3 | 0 |
| src/include/optimizer/pathnode.h | 3 | 2 |
| src/include/optimizer/paths.h | 6 | 0 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index b2a2ab6..ceae604 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -34,11 +34,13 @@
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
#include "executor/executor.h"
+#include "executor/nodeSubplan.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
+#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h"
@@ -279,25 +281,32 @@ static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
PG_FUNCTION_INFO_V1(satisfies_hash_partition);
static Bitmapset *get_partitions_from_clauses_recurse(Relation relation,
- int rt_index, List *clauses);
+ int rt_index, ParamListInfo prmlist,
+ ExprContext *econtext, List *clauses);
static Bitmapset *get_partitions_from_ne_clauses(Relation relation,
- List *ne_clauses);
+ ParamListInfo prmlist,
+ ExprContext *econtext, List *ne_clauses);
static Bitmapset *get_partitions_from_or_clause_args(Relation relation,
- int rt_index, List *or_clause_args);
+ int rt_index, ParamListInfo prmlist,
+ ExprContext *econtext,List *or_clause_args);
static int classify_partition_bounding_keys(Relation relation, List *clauses,
int rt_index,
- PartScanKeyInfo *keys, bool *constfalse,
+ PartScanKeyInfo *keys, ParamListInfo prmlist,
+ ExprContext *econtext, bool *constfalse,
List **or_clauses, List **ne_clauses);
static void remove_redundant_clauses(PartitionKey partkey,
int partattoff, List *all_clauses,
- List **result, bool *constfalse);
+ List **result, ParamListInfo prmlist,
+ ExprContext *econtext, bool *constfalse);
static bool partition_cmp_args(PartitionKey key, int partattoff,
PartClause *op, PartClause *leftarg, PartClause *rightarg,
+ ParamListInfo prmlist, ExprContext *econtext,
bool *result);
static int32 partition_op_strategy(PartitionKey key, PartClause *op,
bool *incl);
static bool partkey_datum_from_expr(PartitionKey key, int partattoff,
- Expr *expr, Datum *value);
+ Expr *expr, ParamListInfo prmlist,
+ ExprContext *econtext, Datum *value);
static Bitmapset *get_partitions_for_keys(Relation rel,
PartScanKeyInfo *keys);
static Bitmapset *get_partitions_for_keys_hash(Relation rel,
@@ -1684,7 +1693,8 @@ get_partition_qual_relid(Oid relid)
*/
Bitmapset *
get_partitions_from_clauses(Relation relation, int rt_index,
- List *partclauses)
+ ParamListInfo prmlist, ExprContext *econtext,
+ List *partclauses)
{
Bitmapset *result;
List *partconstr = RelationGetPartitionQual(relation);
@@ -1717,12 +1727,128 @@ get_partitions_from_clauses(Relation relation, int rt_index,
}
}
- result = get_partitions_from_clauses_recurse(relation, rt_index,
- partclauses);
+ result = get_partitions_from_clauses_recurse(relation, rt_index, prmlist,
+ econtext, partclauses);
return result;
}
+/*
+ * make_partition_pruneinfo
+ * Build PartitionPruneInfo tree to allow the output of
+ * get_partitions_from_clauses to be translated into
+ * 'subpaths' indexes
+ */
+PartitionPruneInfo *
+make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *rel,
+ List *partition_rels, List *subpaths)
+{
+ PartitionPruneInfo *pinfo;
+ AppendRelInfo *appinfo;
+ RangeTblEntry *rte;
+ ListCell *lc;
+ int i;
+ int partidx;
+ int nparts = rel->nparts;
+
+ rte = root->simple_rte_array[rel->relid];
+
+ pinfo = makeNode(PartitionPruneInfo);
+ pinfo->parentoid = rte->relid;
+ pinfo->subnodeindex = (int *) palloc(sizeof(int) * nparts);
+ pinfo->subpartindex = (PartitionPruneInfo **)
+ palloc0(sizeof(PartitionPruneInfo *) * nparts);
+ pinfo->nparts = nparts;
+
+ /*
+ * -1 represents a partition that's already been pruned. Set them all to
+ * this initially. We'll determine the subpath index for the non-pruned
+ * ones below.
+ */
+ for (i = 0; i < nparts; i++)
+ pinfo->subnodeindex[i] = -1;
+
+ i = -1;
+ foreach(lc, subpaths)
+ {
+ Path *path = (Path *) lfirst(lc);
+
+ i++; /* track subnode index */
+
+ /* Find the AppendRelInfo for the Append child */
+ appinfo = find_childrel_appendrelinfo(root, path->parent);
+
+ /*
+ * Skip subpaths who's rel is not directly parented by rel. We'll
+ * process any we skip here later when looping through partition_rels
+ */
+ if (appinfo->parent_relid != rel->relid)
+ continue;
+
+ /*
+ * Determine the element in part_appinfo which belongs to this
+ * subnode.
+ */
+ for (partidx = 0; partidx < nparts; partidx++)
+ {
+
+ if (rel->part_appinfos[partidx]->child_relid != appinfo->child_relid)
+ continue;
+
+ /* found it! Save the subnode index */
+ pinfo->subnodeindex[partidx] = i;
+ break;
+ }
+ }
+
+ /*
+ * Some of the tables returned by get_partitions_from_clauses may be other
+ * partitioned tables. Unlike the case above, these won't be subnodes of
+ * the Append. To handle these we must make use of another
+ * PartitionPruneInfo which we must use to recursively search until we
+ * reach the leaf partitions.
+ */
+ foreach(lc, partition_rels)
+ {
+ Index rti = lfirst_int(lc);
+ RelOptInfo *subpart = find_base_rel(root, rti);
+
+ /*
+ * partition_rels contains the rti of the relation being queried. We
+ * only care about sub-partition parents here.
+ */
+ if (subpart->reloptkind == RELOPT_BASEREL)
+ continue;
+
+ appinfo = find_childrel_appendrelinfo(root, subpart);
+
+ /*
+ * We only want to deal with sub-partition parents that are directly
+ * below rel. We'll search until we get down to the leaf partitions by
+ * recursively calling ourself.
+ */
+ if (appinfo->parent_relid != rel->relid)
+ continue;
+
+ /*
+ * Handle sub-partition parents by building a sub-PartitionPruneInfo.
+ */
+ for (partidx = 0; partidx < nparts; partidx++)
+ {
+ if (rel->part_appinfos[partidx]->child_relid != appinfo->child_relid)
+ continue;
+
+ pinfo->subpartindex[partidx] = make_partition_pruneinfo(root,
+ subpart,
+ partition_rels,
+ subpaths);
+ break;
+ }
+ }
+
+ return pinfo;
+}
+
/* Module-local functions */
/*
@@ -1734,6 +1860,8 @@ get_partitions_from_clauses(Relation relation, int rt_index,
*/
static Bitmapset *
get_partitions_from_clauses_recurse(Relation relation, int rt_index,
+ ParamListInfo prmlist,
+ ExprContext *econtext,
List *clauses)
{
PartitionDesc partdesc = RelationGetPartitionDesc(relation);
@@ -1750,8 +1878,9 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index,
* can work with.
*/
nkeys = classify_partition_bounding_keys(relation, clauses, rt_index,
- &keys, &constfalse,
- &or_clauses, &ne_clauses);
+ &keys, prmlist, econtext,
+ &constfalse, &or_clauses,
+ &ne_clauses);
/*
* classify_partition_bounding_keys() may have found clauses marked
@@ -1784,6 +1913,8 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index,
if (ne_clauses)
result = bms_int_members(result,
get_partitions_from_ne_clauses(relation,
+ prmlist,
+ econtext,
ne_clauses));
/*
@@ -1800,6 +1931,8 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index,
result = bms_int_members(result,
get_partitions_from_or_clause_args(relation,
rt_index,
+ prmlist,
+ econtext,
or->args));
}
@@ -1863,7 +1996,8 @@ count_partition_datums(Relation rel, int index)
* ne_clauses. Only ever called if relation is a list partitioned table.
*/
static Bitmapset *
-get_partitions_from_ne_clauses(Relation relation, List *ne_clauses)
+get_partitions_from_ne_clauses(Relation relation, ParamListInfo prmlist,
+ ExprContext *econtext, List *ne_clauses)
{
ListCell *lc;
Bitmapset *result,
@@ -1898,7 +2032,8 @@ get_partitions_from_ne_clauses(Relation relation, List *ne_clauses)
PartClause *pc = lfirst(lc);
Datum datum;
- if (partkey_datum_from_expr(partkey, 0, pc->constarg, &datum) &&
+ if (partkey_datum_from_expr(partkey, 0, pc->constarg, prmlist,
+ econtext, &datum) &&
!datum_in_array(partkey, datum, exclude_datums, n_exclude_datums))
exclude_datums[n_exclude_datums++] = datum;
}
@@ -1965,6 +2100,8 @@ get_partitions_from_ne_clauses(Relation relation, List *ne_clauses)
*/
static Bitmapset *
get_partitions_from_or_clause_args(Relation relation, int rt_index,
+ ParamListInfo prmlist,
+ ExprContext *econtext,
List *or_clause_args)
{
ListCell *lc;
@@ -1997,6 +2134,8 @@ get_partitions_from_or_clause_args(Relation relation, int rt_index,
}
arg_partset = get_partitions_from_clauses_recurse(relation, rt_index,
+ prmlist,
+ econtext,
arg_clauses);
/*
@@ -2051,7 +2190,10 @@ get_partitions_from_or_clause_args(Relation relation, int rt_index,
static int
classify_partition_bounding_keys(Relation relation, List *clauses,
int rt_index,
- PartScanKeyInfo *keys, bool *constfalse,
+ PartScanKeyInfo *keys,
+ ParamListInfo prmlist,
+ ExprContext *econtext,
+ bool *constfalse,
List **or_clauses,
List **ne_clauses)
{
@@ -2480,7 +2622,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
remove_redundant_clauses(partkey, i,
keyclauses_all[i], &keyclauses[i],
- constfalse);
+ prmlist, econtext, constfalse);
if (*constfalse)
return 0;
}
@@ -2521,6 +2663,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
if (op_strategy < 0 &&
need_next_max &&
partkey_datum_from_expr(partkey, i, constarg,
+ prmlist, econtext,
&keys->maxkeys[i]))
{
keys->n_maxkeys++;
@@ -2533,11 +2676,13 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
Assert(incl);
if (need_next_eq &&
partkey_datum_from_expr(partkey, i, constarg,
+ prmlist, econtext,
&keys->eqkeys[i]))
keys->n_eqkeys++;
if (need_next_max &&
partkey_datum_from_expr(partkey, i, constarg,
+ prmlist, econtext,
&keys->maxkeys[i]))
{
keys->n_maxkeys++;
@@ -2546,6 +2691,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
if (need_next_min &&
partkey_datum_from_expr(partkey, i, constarg,
+ prmlist, econtext,
&keys->minkeys[i]))
{
keys->n_minkeys++;
@@ -2554,6 +2700,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
}
else if (need_next_min &&
partkey_datum_from_expr(partkey, i, constarg,
+ prmlist, econtext,
&keys->minkeys[i]))
{
keys->n_minkeys++;
@@ -2642,7 +2789,8 @@ partition_op_strategy(PartitionKey key, PartClause *op, bool *incl)
*/
static bool
partkey_datum_from_expr(PartitionKey key, int partattoff,
- Expr *expr, Datum *value)
+ Expr *expr, ParamListInfo prmlist, ExprContext *econtext,
+ Datum *value)
{
Oid exprtype = exprType((Node *) expr);
@@ -2684,6 +2832,43 @@ partkey_datum_from_expr(PartitionKey key, int partattoff,
case T_Const:
*value = ((Const *) expr)->constvalue;
return true;
+ case T_Param:
+
+ switch (((Param *) expr)->paramkind)
+ {
+ case PARAM_EXTERN:
+ if (prmlist)
+ {
+ Node *node;
+ Param *param = (Param *) expr;
+ node = eval_const_expressions_from_list(prmlist,
+ (Node *) param);
+ if (IsA(node, Const))
+ {
+ *value = ((Const *) node)->constvalue;
+ return true;
+ }
+ }
+
+ case PARAM_EXEC:
+ if (econtext)
+ {
+ Param *param = (Param *) expr;
+ ParamExecData *prm;
+
+ prm = &(econtext->ecxt_param_exec_vals[param->paramid]);
+ if (unlikely(prm->execPlan != NULL))
+ {
+ ExecSetParamPlan(prm->execPlan, econtext);
+ Assert(prm->execPlan == NULL);
+ }
+ *value = prm->value;
+ return true;
+ }
+
+ default:
+ return false;
+ }
default:
return false;
@@ -2702,6 +2887,7 @@ partkey_datum_from_expr(PartitionKey key, int partattoff,
static void
remove_redundant_clauses(PartitionKey partkey, int partattoff,
List *all_clauses, List **result,
+ ParamListInfo prmlist, ExprContext *econtext,
bool *constfalse)
{
PartClause *hash_clause,
@@ -2745,7 +2931,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
/* check if another clause would contradict the one we have */
else if (partition_cmp_args(partkey, partattoff,
cur, cur, hash_clause,
- &test_result))
+ prmlist, econtext, &test_result))
{
if (!test_result)
{
@@ -2800,7 +2986,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
*/
if (partition_cmp_args(partkey, partattoff,
cur, cur, btree_clauses[s],
- &test_result))
+ prmlist, econtext, &test_result))
{
/* cur is more restrictive, replace old key. */
if (test_result)
@@ -2856,7 +3042,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
*/
if (partition_cmp_args(partkey, partattoff,
chk, eq, chk,
- &test_result))
+ prmlist, econtext, &test_result))
{
if (!test_result)
{
@@ -2886,7 +3072,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
if (partition_cmp_args(partkey, partattoff,
le, lt, le,
- &test_result))
+ prmlist, econtext, &test_result))
{
if (test_result)
btree_clauses[BTLessEqualStrategyNumber - 1] = NULL;
@@ -2904,7 +3090,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
if (partition_cmp_args(partkey, partattoff,
ge, gt, ge,
- &test_result))
+ prmlist, econtext, &test_result))
{
if (test_result)
btree_clauses[BTGreaterEqualStrategyNumber - 1] = NULL;
@@ -2938,6 +3124,7 @@ remove_redundant_clauses(PartitionKey partkey, int partattoff,
static bool
partition_cmp_args(PartitionKey key, int partattoff,
PartClause *op, PartClause *leftarg, PartClause *rightarg,
+ ParamListInfo prmlist, ExprContext *econtext,
bool *result)
{
Oid partopfamily = key->partopfamily[partattoff];
@@ -2947,10 +3134,12 @@ partition_cmp_args(PartitionKey key, int partattoff,
Assert(op->valid_cache && leftarg->valid_cache && rightarg->valid_cache);
/* Get the constant values from the operands */
if (!partkey_datum_from_expr(key, partattoff,
- leftarg->constarg, &leftarg_const))
+ leftarg->constarg, prmlist, econtext,
+ &leftarg_const))
return false;
if (!partkey_datum_from_expr(key, partattoff,
- rightarg->constarg, &rightarg_const))
+ rightarg->constarg, prmlist, econtext,
+ &rightarg_const))
return false;
/*
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 0e93713..c8dee20 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "parser/parsetree.h"
#include "executor/execdebug.h"
#include "executor/nodeAppend.h"
#include "miscadmin.h"
@@ -82,6 +83,9 @@ static TupleTableSlot *ExecAppend(PlanState *pstate);
static bool choose_next_subplan_locally(AppendState *node);
static bool choose_next_subplan_for_leader(AppendState *node);
static bool choose_next_subplan_for_worker(AppendState *node);
+static void set_valid_runtime_subplans(AppendState *node,
+ PartitionPruneInfo *pinfo);
+
/* ----------------------------------------------------------------
* ExecInitAppend
@@ -127,6 +131,27 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
appendstate->ps.ExecProcNode = ExecAppend;
appendstate->appendplans = appendplanstates;
appendstate->as_nplans = nplans;
+ appendstate->prune_qual = node->plan.qual;
+ appendstate->part_prune_params = node->part_prune_params;
+ appendstate->part_prune_info = node->part_prune_info;
+
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
+ */
+ ExecAssignExprContext(estate, &appendstate->ps);
+
+ /*
+ * The presence of a part_prune_info means that run-time pruning is
+ * enabled, so here we'll determine which subplans need to be scanned.
+ */
+ if (node->part_prune_info)
+ set_valid_runtime_subplans(appendstate, appendstate->part_prune_info);
+
+ /* else, mark all subplans as requiring a scan */
+ else
+ appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
/*
* Miscellaneous initialization
@@ -163,10 +188,20 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
/*
* Parallel-aware append plans must choose the first subplan to execute by
* looking at shared memory, but non-parallel-aware append plans can
- * always start with the first subplan.
+ * always start with the first valid subplan.
*/
- appendstate->as_whichplan =
- appendstate->ps.plan->parallel_aware ? INVALID_SUBPLAN_INDEX : 0;
+ if (appendstate->ps.plan->parallel_aware)
+ appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
+ else
+ {
+ /* Set the plan to the first valid subplan */
+ appendstate->as_whichplan = bms_next_member(
+ appendstate->as_valid_subplans, -1);
+
+ /* There mightn't be a single valid subplan. */
+ if (appendstate->as_whichplan < 0)
+ appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
+ }
/* If parallel-aware, this will be overridden later. */
appendstate->choose_next_subplan = choose_next_subplan_locally;
@@ -257,6 +292,22 @@ ExecReScanAppend(AppendState *node)
{
int i;
+ /*
+ * The presence of a part_prune_info means that run-time pruning is
+ * enabled. If any of the parameters being used for partition pruning
+ * have changed, then we'd better redetermine which subplans we need to
+ * scan.
+ */
+ if (node->part_prune_info &&
+ bms_overlap(node->ps.chgParam, node->part_prune_params))
+ {
+ /* Free previous valid subplan bits, if any */
+ bms_free(node->as_valid_subplans);
+ node->as_valid_subplans = NULL;
+
+ set_valid_runtime_subplans(node, node->part_prune_info);
+ }
+
for (i = 0; i < node->as_nplans; i++)
{
PlanState *subnode = node->appendplans[i];
@@ -276,8 +327,17 @@ ExecReScanAppend(AppendState *node)
ExecReScan(subnode);
}
- node->as_whichplan =
- node->ps.plan->parallel_aware ? INVALID_SUBPLAN_INDEX : 0;
+ if (node->ps.plan->parallel_aware)
+ node->as_whichplan = INVALID_SUBPLAN_INDEX;
+ else
+ {
+ /* Set the plan to the first valid subplan */
+ node->as_whichplan = bms_next_member(node->as_valid_subplans, -1);
+
+ /* There mightn't be a single valid subplan. */
+ if (node->as_whichplan < 0)
+ node->as_whichplan = INVALID_SUBPLAN_INDEX;
+ }
}
/* ----------------------------------------------------------------
@@ -366,22 +426,19 @@ static bool
choose_next_subplan_locally(AppendState *node)
{
int whichplan = node->as_whichplan;
+ int nextplan;
/* We should never see INVALID_SUBPLAN_INDEX in this case. */
Assert(whichplan >= 0 && whichplan <= node->as_nplans);
if (ScanDirectionIsForward(node->ps.state->es_direction))
- {
- if (whichplan >= node->as_nplans - 1)
- return false;
- node->as_whichplan++;
- }
+ nextplan = bms_next_member(node->as_valid_subplans, whichplan);
else
- {
- if (whichplan <= 0)
- return false;
- node->as_whichplan--;
- }
+ nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
+
+ if (nextplan < 0)
+ return false;
+ node->as_whichplan = nextplan;
return true;
}
@@ -399,6 +456,7 @@ choose_next_subplan_for_leader(AppendState *node)
{
ParallelAppendState *pstate = node->as_pstate;
Append *append = (Append *) node->ps.plan;
+ Bitmapset *validplans = node->as_valid_subplans;
/* Backward scan is not supported by parallel-aware plans */
Assert(ScanDirectionIsForward(node->ps.state->es_direction));
@@ -412,21 +470,31 @@ choose_next_subplan_for_leader(AppendState *node)
}
else
{
- /* Start with last subplan. */
- node->as_whichplan = node->as_nplans - 1;
+ /* Start with last valid subplan. */
+ node->as_whichplan = bms_prev_member(validplans, -1);
+
+ /* Bail if there are no valid plans */
+ if (node->as_whichplan < 0)
+ {
+ pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
+ node->as_whichplan = INVALID_SUBPLAN_INDEX;
+ LWLockRelease(&pstate->pa_lock);
+ return false;
+ }
}
/* Loop until we find a subplan to execute. */
while (pstate->pa_finished[node->as_whichplan])
{
- if (node->as_whichplan == 0)
+ node->as_whichplan = bms_prev_member(validplans, node->as_whichplan);
+
+ if (node->as_whichplan < 0)
{
pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
node->as_whichplan = INVALID_SUBPLAN_INDEX;
LWLockRelease(&pstate->pa_lock);
return false;
}
- node->as_whichplan--;
}
/* If non-partial, immediately mark as finished. */
@@ -457,6 +525,7 @@ choose_next_subplan_for_worker(AppendState *node)
{
ParallelAppendState *pstate = node->as_pstate;
Append *append = (Append *) node->ps.plan;
+ Bitmapset *validplans = node->as_valid_subplans;
/* Backward scan is not supported by parallel-aware plans */
Assert(ScanDirectionIsForward(node->ps.state->es_direction));
@@ -477,15 +546,26 @@ choose_next_subplan_for_worker(AppendState *node)
/* Loop until we find a subplan to execute. */
while (pstate->pa_finished[pstate->pa_next_plan])
{
- if (pstate->pa_next_plan < node->as_nplans - 1)
+ int nextplan;
+
+ nextplan = bms_next_member(validplans, pstate->pa_next_plan);
+ if (nextplan >= 0)
{
- /* Advance to next plan. */
- pstate->pa_next_plan++;
+ /* Advance to next valid plan. */
+ pstate->pa_next_plan = nextplan;
}
else if (append->first_partial_plan < node->as_nplans)
{
- /* Loop back to first partial plan. */
- pstate->pa_next_plan = append->first_partial_plan;
+ /* Loop back to first valid partial plan. */
+ pstate->pa_next_plan = bms_next_member(validplans,
+ append->first_partial_plan - 1);
+
+ /*
+ * Ensure we there is a valid first partial plan, if not then
+ * we'll the next plan so that we finish the loop below.
+ */
+ if (pstate->pa_next_plan < 0)
+ pstate->pa_next_plan = node->as_whichplan;
}
else
{
@@ -503,8 +583,9 @@ choose_next_subplan_for_worker(AppendState *node)
}
/* Pick the plan we found, and advance pa_next_plan one more time. */
- node->as_whichplan = pstate->pa_next_plan++;
- if (pstate->pa_next_plan >= node->as_nplans)
+ node->as_whichplan = pstate->pa_next_plan;
+ pstate->pa_next_plan = bms_next_member(validplans, pstate->pa_next_plan);
+ if (pstate->pa_next_plan < 0)
{
if (append->first_partial_plan < node->as_nplans)
pstate->pa_next_plan = append->first_partial_plan;
@@ -526,3 +607,51 @@ choose_next_subplan_for_worker(AppendState *node)
return true;
}
+
+/*
+ * get_valid_runtime_subplans
+ * Determine which subset of subplan nodes we need to scan based on
+ * 'prune_qual'. All subplans which provably cannot possibly have
+ * matching records are eliminated and the remainder are set in the
+ * AppendState's 'as_valid_subplans' variable.
+ */
+static void
+set_valid_runtime_subplans(AppendState *node, PartitionPruneInfo *pinfo)
+{
+ Bitmapset *partset;
+ Relation rel;
+ int i;
+ List *clauses = node->prune_qual;
+
+ rel = relation_open(pinfo->parentoid, NoLock);
+
+ /* Determine which partition indexes we need to scan */
+ partset = get_partitions_from_clauses(rel, 1,
+ node->ps.state->es_param_list_info,
+ node->ps.ps_ExprContext, clauses);
+
+ /* Translate partset into subnode indexes */
+ i = -1;
+ while ((i = bms_next_member(partset, i)) >= 0)
+ {
+ if (pinfo->subnodeindex[i] >= 0)
+ node->as_valid_subplans = bms_add_member(node->as_valid_subplans,
+ pinfo->subnodeindex[i]);
+ else if (pinfo->subpartindex[i] != NULL)
+ set_valid_runtime_subplans(node, pinfo->subpartindex[i]);
+ else
+ {
+ /*
+ * If this happens then we're somehow missing an Append subnode.
+ * This shouldn't happen and could only happen if a more
+ * restrictive clause list was used for partition elimination
+ * during planning than was used here.
+ */
+ elog(ERROR, "partition missing from Append subnodes");
+ }
+ }
+
+ bms_free(partset);
+
+ relation_close(rel, NoLock);
+}
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index ae30072..fb104bc 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -58,6 +58,9 @@
* rightmost_one_pos[x] gives the bit number (0-7) of the rightmost one bit
* in a nonzero byte value x. The entry for x=0 is never used.
*
+ * leftmost_ons_pos[x] gives the bit number (0-7) of the leftmost one bit in a
+ * nonzero byte value x. The entry for x=0 is never used.
+ *
* number_of_ones[x] gives the number of one-bits (0-8) in a byte value x.
*
* We could make these tables larger and reduce the number of iterations
@@ -84,6 +87,25 @@ static const uint8 rightmost_one_pos[256] = {
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
+static const uint8 leftmost_one_pos[256] = {
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+
static const uint8 number_of_ones[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
@@ -1045,6 +1067,79 @@ bms_next_member(const Bitmapset *a, int prevbit)
}
/*
+ * bms_prev_member - find prev member of a set
+ *
+ * Returns largest member less than "prevbit", or -2 if there is none.
+ * "prevbit" must NOT be more than one above the highest possible bit that can
+ * be set at the Bitmapset at its current size.
+ *
+ * To ease finding the highest set bit for the initial loop, the special
+ * prevbit value of -1 can be passed to have the function find the highest
+ * 1-bit in the set.
+ *
+ * This is intended as support for iterating through the members of a set in
+ * reverse. The typical pattern is
+ *
+ * x = -1;
+ * while ((x = bms_prev_member(inputset, x)) >= 0)
+ * process member x;
+ *
+ * Notice that when there are no more members, we return -2, not -1 as you
+ * might expect. The rationale for that is to allow distinguishing the
+ * loop-not-started state (x == -1) from the loop-completed state (x == -2).
+ * It makes no difference in simple loop usage, but complex iteration logic
+ * might need such an ability.
+ */
+
+int
+bms_prev_member(const Bitmapset *a, int prevbit)
+{
+ int wordnum;
+ int ushiftbits;
+ bitmapword mask;
+
+ /*
+ * If set is NULL or if there are no more bits to the right then we've
+ * nothing to do.
+ */
+ if (a == NULL || prevbit == 0)
+ return -2;
+
+ /* transform -1 to the highest possible bit we could have set */
+ if (prevbit == -1)
+ prevbit = a->nwords * BITS_PER_BITMAPWORD - 1;
+ else
+ prevbit--;
+
+ ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1);
+ mask = (~(bitmapword) 0) >> ushiftbits;
+ for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
+ {
+ bitmapword w = a->words[wordnum];
+
+ /* mask out bits left of prevbit */
+ w &= mask;
+
+ if (w != 0)
+ {
+ int result;
+ int shift = 24;
+ result = wordnum * BITS_PER_BITMAPWORD;
+
+ while ((w >> shift) == 0)
+ shift -= 8;
+
+ result += shift + leftmost_one_pos[(w >> shift) & 255];
+ return result;
+ }
+
+ /* in subsequent words, consider all bits */
+ mask = (~(bitmapword) 0);
+ }
+ return -2;
+}
+
+/*
* bms_hash_value - compute a hash key for a Bitmapset
*
* Note: we must ensure that any two bitmapsets that are bms_equal() will
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 84d7171..f533cbe 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -243,6 +243,8 @@ _copyAppend(const Append *from)
COPY_NODE_FIELD(partitioned_rels);
COPY_NODE_FIELD(appendplans);
COPY_SCALAR_FIELD(first_partial_plan);
+ COPY_BITMAPSET_FIELD(part_prune_params);
+ COPY_NODE_FIELD(part_prune_info);
return newnode;
}
@@ -2126,6 +2128,31 @@ _copyOnConflictExpr(const OnConflictExpr *from)
return newnode;
}
+static PartitionPruneInfo *
+_copyPartitionPruneInfo(const PartitionPruneInfo *from)
+{
+ PartitionPruneInfo *newnode = makeNode(PartitionPruneInfo);
+ int i;
+
+ COPY_SCALAR_FIELD(parentoid);
+ COPY_SCALAR_FIELD(nparts);
+ COPY_POINTER_FIELD(subnodeindex, from->nparts * sizeof(int));
+ COPY_POINTER_FIELD(subpartindex, from->nparts * sizeof(PartitionPruneInfo));
+
+ /*
+ * The above copied the entire array, but we still need to create copies
+ * of each PartitionPruneInfo contained in that array.
+ */
+ for (i = 0; i < from->nparts; i++)
+ {
+ if (newnode->subpartindex[i] != NULL)
+ newnode->subpartindex[i] =
+ _copyPartitionPruneInfo(newnode->subpartindex[i]);
+ }
+
+ return newnode;
+}
+
/* ****************************************************************
* relation.h copy functions
*
@@ -5007,6 +5034,9 @@ copyObjectImpl(const void *from)
case T_OnConflictExpr:
retval = _copyOnConflictExpr(from);
break;
+ case T_PartitionPruneInfo:
+ retval = _copyPartitionPruneInfo(from);
+ break;
/*
* RELATION NODES
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index eeaf8fd..ee53c72 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -141,12 +141,6 @@ static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
static List *get_append_rel_partitions(PlannerInfo *root,
RelOptInfo *rel,
RangeTblEntry *rte);
-static List *match_clauses_to_partkey(PlannerInfo *root,
- RelOptInfo *rel,
- List *clauses,
- bool *contains_const,
- bool *constfalse);
-
/*
* make_one_rel
@@ -877,6 +871,7 @@ get_append_rel_partitions(PlannerInfo *root,
Relation parent;
PartitionDesc partdesc;
Bitmapset *partindexes;
+ Bitmapset *paramids = NULL;
/*
* Get the clauses that match the partition key, including information
@@ -885,6 +880,7 @@ get_append_rel_partitions(PlannerInfo *root,
*/
partclauses = match_clauses_to_partkey(root, rel,
list_copy(rel->baserestrictinfo),
+ ¶mids,
&contains_const,
&constfalse);
@@ -892,6 +888,13 @@ get_append_rel_partitions(PlannerInfo *root,
if (constfalse)
return NIL;
+ /*
+ * Record any params found that we could use to further eliminate
+ * partitions during execution.
+ */
+ rel->runtime_prune_params = bms_add_members(rel->runtime_prune_params,
+ paramids);
+
parent = heap_open(rte->relid, NoLock);
partdesc = RelationGetPartitionDesc(parent);
@@ -900,8 +903,8 @@ get_append_rel_partitions(PlannerInfo *root,
* then use these to prune partitions.
*/
if (partclauses != NIL && contains_const)
- partindexes = get_partitions_from_clauses(parent, rel->relid,
- partclauses);
+ partindexes = get_partitions_from_clauses(parent, rel->relid, NULL,
+ NULL, partclauses);
else
{
/*
@@ -963,10 +966,11 @@ get_append_rel_partitions(PlannerInfo *root,
* If the list contains a pseudo-constant RestrictInfo with constant false
* value, *constfalse is set.
*/
-static List *
+List *
match_clauses_to_partkey(PlannerInfo *root,
RelOptInfo *rel,
List *clauses,
+ Bitmapset **paramids,
bool *contains_const,
bool *constfalse)
{
@@ -1030,6 +1034,7 @@ match_clauses_to_partkey(PlannerInfo *root,
constfalse1;
if (match_clauses_to_partkey(root, rel, list_make1(arg),
+ paramids,
&contains_const1,
&constfalse1) != NIL)
{
@@ -1154,8 +1159,11 @@ match_clauses_to_partkey(PlannerInfo *root,
*/
result = lappend(result, clause);
- if (!*contains_const)
- *contains_const = IsA(constexpr, Const);
+ if (IsA(constexpr, Const))
+ *contains_const = true;
+ else if (IsA(constexpr, Param))
+ *paramids = bms_add_member(*paramids,
+ ((Param *) constexpr)->paramid);
}
else if (IsA(clause, ScalarArrayOpExpr))
{
@@ -1711,6 +1719,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
List *partitioned_rels = NIL;
RangeTblEntry *rte;
double partial_rows = -1;
+ bool trypartitionprune = false;
/*
* AppendPath generated for partitioned tables must record the RT indexes
@@ -1735,7 +1744,19 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
rte = planner_rt_fetch(rel->relid, root);
if (rte->rtekind == RTE_RELATION &&
rte->relkind == RELKIND_PARTITIONED_TABLE)
- partitioned_rels = rel->live_partitioned_rels;
+ {
+ partitioned_rels = rel->live_partitioned_rels;
+
+ /*
+ * For base partitioned tables we'll try to see if we can perform
+ * any run-time partition pruning. We need to do a bit more work
+ * later in planning to ensure we can enable it, so this just
+ * allows a way to save doing that work in cases when we certainly
+ * can't enable it.
+ */
+ if (rel->reloptkind == RELOPT_BASEREL)
+ trypartitionprune = true;
+ }
}
else if (rel->reloptkind == RELOPT_JOINREL && rel->part_scheme)
{
@@ -1907,9 +1928,10 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
* if we have zero or one live subpath due to constraint exclusion.)
*/
if (subpaths_valid)
- add_path(rel, (Path *) create_append_path(rel, subpaths, NIL,
+ add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL,
NULL, 0, false,
- partitioned_rels, -1));
+ partitioned_rels, -1,
+ trypartitionprune));
/*
* Consider an append of unordered, unparameterized partial paths. Make
@@ -1949,10 +1971,11 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
Assert(parallel_workers > 0);
/* Generate a partial append path. */
- appendpath = create_append_path(rel, NIL, partial_subpaths, NULL,
- parallel_workers,
+ appendpath = create_append_path(root, rel, NIL, partial_subpaths,
+ NULL, parallel_workers,
enable_parallel_append,
- partitioned_rels, -1);
+ partitioned_rels, -1,
+ trypartitionprune);
/*
* Make sure any subsequent partial paths use the same row count
@@ -1998,10 +2021,11 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
max_parallel_workers_per_gather);
Assert(parallel_workers > 0);
- appendpath = create_append_path(rel, pa_nonpartial_subpaths,
+ appendpath = create_append_path(root, rel, pa_nonpartial_subpaths,
pa_partial_subpaths,
NULL, parallel_workers, true,
- partitioned_rels, partial_rows);
+ partitioned_rels, partial_rows,
+ trypartitionprune);
add_partial_path(rel, (Path *) appendpath);
}
@@ -2054,9 +2078,10 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
if (subpaths_valid)
add_path(rel, (Path *)
- create_append_path(rel, subpaths, NIL,
+ create_append_path(root, rel, subpaths, NIL,
required_outer, 0, false,
- partitioned_rels, -1));
+ partitioned_rels, -1,
+ trypartitionprune));
}
}
@@ -2319,8 +2344,8 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
- add_path(rel, (Path *) create_append_path(rel, NIL, NIL, NULL,
- 0, false, NIL, -1));
+ add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NULL,
+ 0, false, NIL, -1, false));
/*
* We set the cheapest path immediately, to ensure that IS_DUMMY_REL()
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 5bd3031..15d1426 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1232,8 +1232,8 @@ mark_dummy_rel(RelOptInfo *rel)
rel->partial_pathlist = NIL;
/* Set up the dummy path */
- add_path(rel, (Path *) create_append_path(rel, NIL, NIL, NULL,
- 0, false, NIL, -1));
+ add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NULL,
+ 0, false, NIL, -1, false));
/* Set or update cheapest_total_path and related fields */
set_cheapest(rel);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1a0d3a8..57961ec 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -204,7 +204,10 @@ static NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
static Append *make_append(List *appendplans, int first_partial_plan,
- List *tlist, List *partitioned_rels);
+ List *tlist, List *partitioned_rels,
+ PartitionPruneInfo *partpruneinfo,
+ Bitmapset *partpruneparams,
+ List *qual);
static RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree,
Plan *righttree,
@@ -1016,6 +1019,9 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
List *tlist = build_path_tlist(root, &best_path->path);
List *subplans = NIL;
ListCell *subpaths;
+ List *qual = NIL;
+ RelOptInfo *rel = best_path->path.parent;
+ PartitionPruneInfo *pinfo = NULL;
/*
* The subpaths list could be empty, if every child was proven empty by
@@ -1053,6 +1059,53 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
subplans = lappend(subplans, subplan);
}
+
+ if (best_path->trypartitionprune)
+ {
+ /* Not for join rels */
+ Assert(bms_membership(rel->relids) == BMS_SINGLETON);
+
+ qual = extract_actual_clauses(best_path->path.parent->baserestrictinfo, false);
+
+ if (best_path->path.param_info)
+ {
+ RangeTblEntry *rte;
+ List *prmquals = best_path->path.param_info->ppi_clauses;
+ bool contains_const;
+ bool constfalse;
+
+ prmquals = extract_actual_clauses(prmquals, false);
+ prmquals = (List *) replace_nestloop_params(root,
+ (Node *) prmquals);
+
+ rte = planner_rt_fetch(rel->relid, root);
+
+ qual = list_concat(qual, prmquals);
+
+ /*
+ * So far, we only know about the pruning params for the base quals
+ * in rel, there may well be params matching partition keys in the
+ * parameterized path clause too, so we'll gather these now. We'll
+ * borrow match_clauses_to_partkey for this, although we only care
+ * about the parameter IDs and not any of the other outputs.
+ */
+ (void) match_clauses_to_partkey(root, rel, prmquals,
+ &rel->runtime_prune_params,
+ &contains_const, &constfalse);
+ }
+
+ /*
+ * If there are parameters matching the partition key then we'll now
+ * enable run-time partition pruning. There's no fancy big switch
+ * to enable it, we'll just make a PartitionPruneInfo and pass that
+ * along to the executor. It'll just make use of it when available.
+ */
+ if (rel->runtime_prune_params)
+ pinfo = make_partition_pruneinfo(root, best_path->path.parent,
+ best_path->partitioned_rels,
+ best_path->subpaths);
+ }
+
/*
* XXX ideally, if there's just one child, we'd not bother to generate an
* Append node but just return the single child. At the moment this does
@@ -1061,7 +1114,10 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
*/
plan = make_append(subplans, best_path->first_partial_path,
- tlist, best_path->partitioned_rels);
+ tlist, best_path->partitioned_rels,
+ pinfo,
+ rel->runtime_prune_params,
+ qual);
copy_generic_path_info(&plan->plan, (Path *) best_path);
@@ -5308,19 +5364,23 @@ make_foreignscan(List *qptlist,
static Append *
make_append(List *appendplans, int first_partial_plan,
- List *tlist, List *partitioned_rels)
+ List *tlist, List *partitioned_rels,
+ PartitionPruneInfo *partpruneinfo,
+ Bitmapset *partpruneparams,
+ List *qual)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
plan->targetlist = tlist;
- plan->qual = NIL;
+ plan->qual = qual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->partitioned_rels = partitioned_rels;
node->appendplans = appendplans;
node->first_partial_plan = first_partial_plan;
-
+ node->part_prune_info = partpruneinfo;
+ node->part_prune_params = partpruneparams;
return node;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index ffdf9c5..6139a13 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3678,14 +3678,16 @@ create_grouping_paths(PlannerInfo *root,
paths = lappend(paths, path);
}
path = (Path *)
- create_append_path(grouped_rel,
+ create_append_path(root,
+ grouped_rel,
paths,
NIL,
NULL,
0,
false,
NIL,
- -1);
+ -1,
+ false);
path->pathtarget = target;
}
else
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index a24e8ac..cc08769 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -590,8 +590,8 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NIL,
- NULL, 0, false, NIL, -1);
+ path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
+ NULL, 0, false, NIL, -1, false);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
@@ -702,8 +702,8 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NIL,
- NULL, 0, false, NIL, -1);
+ path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
+ NULL, 0, false, NIL, -1, false);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ce83fbc..60f31e3 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2470,6 +2470,25 @@ eval_const_expressions(PlannerInfo *root, Node *node)
}
/*--------------------
+ * eval_const_expressions_from_list
+ *
+ * This is similar to eval_const_expression except that it takes ParamListInfo
+ * argument instead of PlannerInfo to create the context.
+ */
+Node *
+eval_const_expressions_from_list(ParamListInfo prmlist, Node *node)
+{
+ eval_const_expressions_context context;
+
+ context.boundParams = prmlist; /* bound Params */
+ context.root = NULL;
+ context.active_fns = NIL; /* nothing being recursively simplified */
+ context.case_val = NULL; /* no CASE being examined */
+ context.estimate = false; /* safe transformations only */
+ return eval_const_expressions_mutator(node, &context);
+}
+
+/*--------------------
* estimate_expression_value
*
* This function attempts to estimate the value of an expression for
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 2aee156..7f3dcbb 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -17,6 +17,7 @@
#include <math.h>
#include "miscadmin.h"
+#include "catalog/partition.h"
#include "nodes/nodeFuncs.h"
#include "nodes/extensible.h"
#include "optimizer/clauses.h"
@@ -1210,11 +1211,13 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
* Note that we must handle subpaths = NIL, representing a dummy access path.
*/
AppendPath *
-create_append_path(RelOptInfo *rel,
+create_append_path(PlannerInfo *root,
+ RelOptInfo *rel,
List *subpaths, List *partial_subpaths,
Relids required_outer,
int parallel_workers, bool parallel_aware,
- List *partitioned_rels, double rows)
+ List *partitioned_rels, double rows,
+ bool trypartitionprune)
{
AppendPath *pathnode = makeNode(AppendPath);
ListCell *l;
@@ -1224,8 +1227,20 @@ create_append_path(RelOptInfo *rel,
pathnode->path.pathtype = T_Append;
pathnode->path.parent = rel;
pathnode->path.pathtarget = rel->reltarget;
- pathnode->path.param_info = get_appendrel_parampathinfo(rel,
- required_outer);
+
+ /*
+ * When using run-time partition pruning we need the clause list details
+ * in the param_info. get_appendrel_parampathinfo does not do this, so
+ * when we're trying to enable run-time partition pruning we'll just call
+ * get_baserel_parampathinfo instead as it does what we need.
+ */
+ if (trypartitionprune)
+ pathnode->path.param_info = get_baserel_parampathinfo(root,
+ rel,
+ required_outer);
+ else
+ pathnode->path.param_info = get_appendrel_parampathinfo(rel,
+ required_outer);
pathnode->path.parallel_aware = parallel_aware;
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = parallel_workers;
@@ -1248,6 +1263,8 @@ create_append_path(RelOptInfo *rel,
append_startup_cost_compare);
}
pathnode->first_partial_path = list_length(subpaths);
+ pathnode->part_prune_params = rel->runtime_prune_params;
+ pathnode->trypartitionprune = trypartitionprune;
pathnode->subpaths = list_concat(subpaths, partial_subpaths);
foreach(l, subpaths)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index dcfda1c..e673ea8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -156,6 +156,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->boundinfo = NULL;
rel->part_appinfos = NULL;
rel->part_rels = NULL;
+ rel->runtime_prune_params = NULL;
rel->partexprs = NULL;
rel->nullable_partexprs = NULL;
rel->live_part_appinfos = NIL;
@@ -577,6 +578,7 @@ build_join_rel(PlannerInfo *root,
joinrel->boundinfo = NULL;
joinrel->part_appinfos = NULL;
joinrel->part_rels = NULL;
+ joinrel->runtime_prune_params = NULL;
joinrel->partexprs = NULL;
joinrel->nullable_partexprs = NULL;
@@ -745,6 +747,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
joinrel->part_scheme = NULL;
joinrel->part_appinfos = NULL;
joinrel->part_rels = NULL;
+ joinrel->runtime_prune_params = NULL;
joinrel->partexprs = NULL;
joinrel->nullable_partexprs = NULL;
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 853c1f6..e83720f 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -1053,7 +1053,14 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
* Note that if generic_cost is -1 (indicating we've not yet determined
* the generic plan cost), we'll always prefer generic at this point.
*/
- if (plansource->generic_cost < avg_custom_cost)
+
+ /*
+ * XXX fix this. Currently we tend to favor custom plans when plan
+ * time partition pruning is performed since the generic plans will
+ * tend to cost more since they'll not have eliminated the same
+ * partitions. For testing only, we'll just use a generic plan here.
+ */
+ /* if (plansource->generic_cost < avg_custom_cost) */
return false;
return true;
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 7a5ab45..9ff6685 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -16,6 +16,7 @@
#include "fmgr.h"
#include "executor/tuptable.h"
#include "nodes/execnodes.h"
+#include "nodes/relation.h"
#include "parser/parse_node.h"
#include "utils/rel.h"
@@ -73,5 +74,9 @@ extern int get_partition_for_tuple(Relation relation, Datum *values,
/* For partition-pruning */
extern Bitmapset *get_partitions_from_clauses(Relation relation, int rt_index,
+ ParamListInfo prmlist, ExprContext *econtext,
List *partclauses);
+extern PartitionPruneInfo *make_partition_pruneinfo(PlannerInfo *root,
+ RelOptInfo *rel,
+ List *partition_rels, List *subpaths);
#endif /* PARTITION_H */
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 3b62a97..b4ded9a 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -98,6 +98,7 @@ extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
/* support for iterating through the integer elements of a set: */
extern int bms_first_member(Bitmapset *a);
extern int bms_next_member(const Bitmapset *a, int prevbit);
+extern int bms_prev_member(const Bitmapset *a, int prevbit);
/* support for hashtables using Bitmapsets as keys: */
extern uint32 bms_hash_value(const Bitmapset *a);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 44d8c47..f3581dc 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1002,6 +1002,7 @@ typedef struct ModifyTableState
*
* nplans how many plans are in the array
* whichplan which plan is being executed (0 .. n-1)
+ * valid_subplans for runtime pruning, valid appendplans indexes to scan
* ----------------
*/
@@ -1016,8 +1017,12 @@ struct AppendState
PlanState **appendplans; /* array of PlanStates for my inputs */
int as_nplans;
int as_whichplan;
+ Bitmapset *as_valid_subplans;
ParallelAppendState *as_pstate; /* parallel coordination info */
Size pstate_len; /* size of parallel coordination info */
+ List *prune_qual; /* quals used for partition pruning */
+ Bitmapset *part_prune_params; /* ParamIds useful for partition pruning */
+ PartitionPruneInfo *part_prune_info; /* details for partition pruning */
bool (*choose_next_subplan) (AppendState *);
};
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c5b5115..b21ecfb 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -191,6 +191,7 @@ typedef enum NodeTag
T_FromExpr,
T_OnConflictExpr,
T_IntoClause,
+ T_PartitionPruneInfo,
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d763da6..cc57b1d 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -249,6 +249,14 @@ typedef struct Append
List *partitioned_rels;
List *appendplans;
int first_partial_plan;
+ Bitmapset *part_prune_params; /* ParamIds used for partition pruning */
+
+ /*
+ * Mapping details for run-time subplan pruning. This allows translation
+ * from partition numbers into subplan indexes. This is set to NULL when
+ * run-time subplan pruning is disabled.
+ */
+ PartitionPruneInfo *part_prune_info;
} Append;
/* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 074ae0a..19695bd 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1506,4 +1506,24 @@ typedef struct OnConflictExpr
List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
} OnConflictExpr;
+/*----------
+ * PartitionPruneInfo - Allows pruning of Append subplans
+ *
+ * Here we store mapping details to allow translation of a partitioned table's
+ * id number into an Append node's subplan index. This structure is used
+ * recursively to search for all subplan nodes when there are sub-partitioned
+ * tables in the Append plan.
+ *----------
+ */
+typedef struct PartitionPruneInfo
+{
+ NodeTag type;
+ Oid parentoid; /* Oid of parent partition rel */
+ int nparts; /* length of the following arrays */
+ int *subnodeindex; /* subnode index indexed by partition id */
+
+ /* sub-PartitionPruneInfo indexed by partition id */
+ struct PartitionPruneInfo **subpartindex;
+} PartitionPruneInfo;
+
#endif /* PRIMNODES_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 9f0b657..7d83375 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -669,6 +669,9 @@ typedef struct RelOptInfo
struct RelOptInfo **part_rels; /* Array of RelOptInfos of *all*
* partitions, stored in the same order as
* of bounds */
+ Bitmapset *runtime_prune_params; /* Only valid for base partition rels.
+ * Stores ParamIds used for run-time
+ * pruning of partitions. */
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
@@ -1295,6 +1298,10 @@ typedef struct AppendPath
/* Index of first partial path in subpaths */
int first_partial_path;
+
+ /* ParamIds useful for subpath elimination during execution, or NULL */
+ Bitmapset *part_prune_params;
+ bool trypartitionprune; /* Attempt to enable partition pruning? */
} AppendPath;
#define IS_DUMMY_PATH(p) \
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 1ef13a4..4fb48b1 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -80,6 +80,9 @@ extern void CommuteRowCompareExpr(RowCompareExpr *clause);
extern Node *eval_const_expressions(PlannerInfo *root, Node *node);
+extern Node *eval_const_expressions_from_list(ParamListInfo prmlist,
+ Node *node);
+
extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern Query *inline_set_returning_function(PlannerInfo *root,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index f183aac..d153aa3 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -64,11 +64,12 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
List *bitmapquals);
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
List *tidquals, Relids required_outer);
-extern AppendPath *create_append_path(RelOptInfo *rel,
+extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel,
List *subpaths, List *partial_subpaths,
Relids required_outer,
int parallel_workers, bool parallel_aware,
- List *partitioned_rels, double rows);
+ List *partitioned_rels, double rows,
+ bool trypartitionprune);
extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
RelOptInfo *rel,
List *subpaths,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ea886b6..b98d0b9 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -50,6 +50,12 @@ extern PGDLLIMPORT join_search_hook_type join_search_hook;
extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
extern void set_dummy_rel_pathlist(RelOptInfo *rel);
+extern List *match_clauses_to_partkey(PlannerInfo *root,
+ RelOptInfo *rel,
+ List *clauses,
+ Bitmapset **paramids,
+ bool *contains_const,
+ bool *constfalse);
extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
List *initial_rels);