From f3884e16e2e1db6cd91961ed37113d89244fb906 Mon Sep 17 00:00:00 2001 From: Beena Emerson Date: Thu, 7 Dec 2017 10:54:29 +0530 Subject: [PATCH] Implement Runtime Partition Pruning Patch by: Beena Emerson, Dilip Kumar Discussion: https://postgr.es/m/CAOG9ApE16ac-_VVZVvv0gePSgkg_BwYEV1NBqZFqDR2bBE0X0A@mail.gmail.com --- src/backend/catalog/partition.c | 128 ++++++++++++++++++--- src/backend/commands/explain.c | 15 +++ src/backend/executor/execPartition.c | 4 +- src/backend/executor/nodeAppend.c | 192 ++++++++++++++++++++++++++++++-- src/backend/nodes/copyfuncs.c | 5 + src/backend/optimizer/path/allpaths.c | 146 ++++++++++++++++++++++-- src/backend/optimizer/path/joinrels.c | 2 +- src/backend/optimizer/plan/createplan.c | 24 ++++ src/backend/optimizer/plan/planner.c | 2 +- src/backend/optimizer/prep/prepunion.c | 4 +- src/backend/optimizer/util/clauses.c | 19 ++++ src/backend/optimizer/util/pathnode.c | 52 ++++++++- src/backend/optimizer/util/relnode.c | 21 +++- src/backend/utils/cache/plancache.c | 2 +- src/include/catalog/partition.h | 5 +- src/include/executor/execPartition.h | 3 + src/include/nodes/execnodes.h | 10 ++ src/include/nodes/plannodes.h | 8 ++ src/include/nodes/relation.h | 18 +++ src/include/optimizer/clauses.h | 1 + src/include/optimizer/pathnode.h | 4 +- 21 files changed, 614 insertions(+), 51 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index f07ac15..b4a4965 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -34,6 +34,7 @@ #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" @@ -272,11 +273,14 @@ 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, List *clauses, + ParamListInfo base_prmlist, + ExprContext *exontext); static int classify_partition_bounding_keys(Relation relation, List *clauses, int rt_index, PartScanKeyInfo *keys, bool *constfalse, - List **or_clauses); + List **or_clauses, ParamListInfo base_prmlist, + ExprContext *econtext); static void remove_redundant_clauses(PartitionKey partkey, int partattoff, List *all_clauses, List **result, bool *constfalse); @@ -286,7 +290,8 @@ static bool partition_cmp_args(PartitionKey key, int partattoff, 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, Datum *value, ParamListInfo base_prmlist, + ExprContext *econtext); static Bitmapset *get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys); @@ -1659,6 +1664,37 @@ get_partition_qual_relid(Oid relid) return result; } +/* get_leaf_part_recurse + * Get the leaf oids for the given rel. + */ +void +get_leaf_part_recurse(Relation rel, List **leaf_part_oids) +{ + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int i; + + check_stack_depth(); + + for (i = 0; i < partdesc->nparts; i++) + { + Oid partrelid = partdesc->oids[i]; + + if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE) + *leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid); + else + { + /* + * We assume all tables in the partition tree were already locked + * by the caller. + */ + Relation partrel = heap_open(partrelid, NoLock); + + get_leaf_part_recurse(partrel, leaf_part_oids); + heap_close(partrel, NoLock); + } + } +} + /* * get_partitions_from_clauses * Determine the set of partitions of relation that will satisfy all @@ -1669,7 +1705,8 @@ get_partition_qual_relid(Oid relid) */ Bitmapset * get_partitions_from_clauses(Relation relation, int rt_index, - List *partclauses) + List *partclauses, ParamListInfo base_prmlist, + ExprContext *econtext) { Bitmapset *result; List *partconstr = RelationGetPartitionQual(relation); @@ -1703,7 +1740,8 @@ get_partitions_from_clauses(Relation relation, int rt_index, } result = get_partitions_from_clauses_recurse(relation, rt_index, - partclauses); + partclauses, base_prmlist, + econtext); return result; } @@ -1719,7 +1757,8 @@ get_partitions_from_clauses(Relation relation, int rt_index, */ static Bitmapset * get_partitions_from_clauses_recurse(Relation relation, int rt_index, - List *clauses) + List *clauses, ParamListInfo base_prmlist, + ExprContext *econtext) { PartitionDesc partdesc = RelationGetPartitionDesc(relation); Bitmapset *result = NULL; @@ -1735,7 +1774,8 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index, */ nkeys = classify_partition_bounding_keys(relation, clauses, rt_index, &keys, &constfalse, - &or_clauses); + &or_clauses, base_prmlist, + econtext); /* * The analysis of the matched clauses done by @@ -1797,7 +1837,9 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index, arg_partset = get_partitions_from_clauses_recurse(relation, rt_index, - arg_clauses); + arg_clauses, + base_prmlist, + econtext); /* * Partition sets obtained from mutually-disjunctive clauses are @@ -1853,7 +1895,8 @@ static int classify_partition_bounding_keys(Relation relation, List *clauses, int rt_index, PartScanKeyInfo *keys, bool *constfalse, - List **or_clauses) + List **or_clauses, ParamListInfo base_prmlist, + ExprContext *econtext) { PartitionKey partkey = RelationGetPartitionKey(relation); int i; @@ -1893,6 +1936,9 @@ classify_partition_bounding_keys(Relation relation, List *clauses, continue; } } + /* when called from ExecReScanAppend */ + else if (IsA(lfirst(lc), ExprState)) + clause = ((ExprState *) lfirst(lc))->expr; else clause = (Expr *) lfirst(lc); @@ -2305,7 +2351,8 @@ classify_partition_bounding_keys(Relation relation, List *clauses, if (op_strategy < 0 && need_next_max && partkey_datum_from_expr(partkey, i, constarg, - &keys->maxkeys[i])) + &keys->maxkeys[i], base_prmlist, + econtext)) { keys->n_maxkeys++; keys->max_incl = incl; @@ -2317,12 +2364,14 @@ classify_partition_bounding_keys(Relation relation, List *clauses, Assert(incl); if (need_next_eq && partkey_datum_from_expr(partkey, i, constarg, - &keys->eqkeys[i])) + &keys->eqkeys[i], base_prmlist, + econtext)) keys->n_eqkeys++; if (need_next_max && partkey_datum_from_expr(partkey, i, constarg, - &keys->maxkeys[i])) + &keys->maxkeys[i], base_prmlist, + econtext)) { keys->n_maxkeys++; keys->max_incl = true; @@ -2330,7 +2379,8 @@ classify_partition_bounding_keys(Relation relation, List *clauses, if (need_next_min && partkey_datum_from_expr(partkey, i, constarg, - &keys->minkeys[i])) + &keys->minkeys[i], base_prmlist, + econtext)) { keys->n_minkeys++; keys->min_incl = true; @@ -2338,7 +2388,8 @@ classify_partition_bounding_keys(Relation relation, List *clauses, } else if (need_next_min && partkey_datum_from_expr(partkey, i, constarg, - &keys->minkeys[i])) + &keys->minkeys[i], base_prmlist, + econtext)) { keys->n_minkeys++; keys->min_incl = incl; @@ -2426,7 +2477,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, Datum *value, ParamListInfo base_prmlist, + ExprContext *econtext) { Oid exprtype = exprType((Node *) expr); @@ -2467,6 +2519,46 @@ partkey_datum_from_expr(PartitionKey key, int partattoff, *value = ((Const *) expr)->constvalue; return true; + case T_Param: + switch (((Param *) expr)->paramkind) + { + case PARAM_EXTERN: + if (base_prmlist) + { + Node *n; + + n = eval_const_expressions_from_list(base_prmlist, + (Node *) expr); + + if (IsA(n, Const)) + { + *value = ((Const *) n)->constvalue; + return true; + } + } + return false; + + 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; + } + return false; + default: return false; } @@ -2728,10 +2820,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, &leftarg_const, + NULL, NULL)) return false; if (!partkey_datum_from_expr(key, partattoff, - rightarg->constarg, &rightarg_const)) + rightarg->constarg, &rightarg_const, + NULL, NULL)) return false; /* diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 447f69d..06c62cb 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1347,6 +1347,21 @@ ExplainNode(PlanState *planstate, List *ancestors, if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfoChar(es->str, '\n'); + /* run-time pruning information for Append node */ + if (es->analyze && IsA(plan, Append)) + { + Append *append = (Append *) plan; + + if (append->base_params) + show_scan_qual(append->base_params, "Runtime Partition Pruning", + planstate, ancestors, es); + + if (append->join_clauses) + show_scan_qual(append->join_clauses, + "Runtime Partition Pruning Join Filter", + planstate, ancestors, es); + } + /* target list */ if (es->verbose) show_plan_tlist(planstate, ancestors, es); diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index d275cef..187d30f 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -23,8 +23,6 @@ #include "utils/rls.h" #include "utils/ruleutils.h" -static PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel, - int *num_parted, List **leaf_part_oids); static void get_partition_dispatch_recurse(Relation rel, Relation parent, List **pds, List **leaf_part_oids); static void FormPartitionKeyDatum(PartitionDispatch pd, @@ -275,7 +273,7 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, * All the relations in the partition tree (including 'rel') must have been * locked (using at least the AccessShareLock) by the caller. */ -static PartitionDispatch * +PartitionDispatch * RelationGetPartitionDispatchInfo(Relation rel, int *num_parted, List **leaf_part_oids) { diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 1d2fb35..073d14f 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -57,7 +57,9 @@ #include "postgres.h" +#include "catalog/pg_inherits_fn.h" #include "executor/execdebug.h" +#include "executor/execPartition.h" #include "executor/nodeAppend.h" #include "miscadmin.h" @@ -108,6 +110,111 @@ exec_append_initialize_next(AppendState *appendstate) } /* ---------------------------------------------------------------- + * initClauses + * + * Runs ExecInitExpr for each cell in given list. + * + * Returns NULL if list is NULL + * ---------------------------------------------------------------- + */ +static List * +initClauses(PlanState *parent, List *list) +{ + List *new_list = NIL; + ListCell *lc; + + if (list == NULL) + return NULL; + + foreach(lc, list) + { + Expr *val = (Expr *) lfirst(lc); + + new_list = lappend(new_list, ExecInitExpr(val, parent)); + } + return new_list; +} + +/* ---------------------------------------------------------------- + * set_append_subplan_indexes + * + * Determine the subplans to scan based on the clauses. + * ---------------------------------------------------------------- + */ +static void +set_append_subplan_indexes(AppendState *node, List *clauses) +{ + Bitmapset *partset = NULL, + *subplans = NULL; + Relation rel; + PartitionDispatch *pd, + parent, + *p1; + List *parents = NIL; + List *leaf_part_oids = NIL; + int cur_index, + num_parted, + i; + + /* + * Get the information about the partition tree after locking all the + * partitions. + */ + rel = relation_open(node->parentoid, NoLock); + (void) find_all_inheritors(RelationGetRelid(rel), AccessShareLock, NULL); + pd = RelationGetPartitionDispatchInfo(rel, &num_parted, &leaf_part_oids); + relation_close(rel, NoLock); + + parents = lappend(parents, &pd[0]); + node->subplan_indexes = NIL; + do + { + p1 = linitial(parents); + parent = *p1; + + partset = get_partitions_from_clauses(parent->reldesc, + 1, + list_copy(clauses), + node->es_param_list_info, + node->ps.ps_ExprContext); + + if (!bms_is_empty(partset)) + { + while ((cur_index = bms_first_member(partset)) >= 0) + { + if (parent->indexes[cur_index] >= 0) + subplans = + bms_add_member(subplans, parent->indexes[cur_index]); + else + parents = + lappend(parents, &pd[-parent->indexes[cur_index]]); + } + } + parents = list_delete_first(parents); + } while (parents); + + for (i = 1; i < num_parted; i++) + { + PartitionDispatch partdispatch = pd[i]; + + heap_close(partdispatch->reldesc, NoLock); + ExecDropSingleTupleTableSlot(partdispatch->tupslot); + } + + if (!bms_is_empty(subplans)) + { + while ((i = bms_first_member(subplans)) >= 0) + { + int index = node->append_paths_array[i]; + + if (index >= 0) + node->subplan_indexes = lappend_int(node->subplan_indexes, + index); + } + } +} + +/* ---------------------------------------------------------------- * ExecInitAppend * * Begin all of the subscans of the append node. @@ -151,17 +258,22 @@ ExecInitAppend(Append *node, EState *estate, int eflags) appendstate->ps.ExecProcNode = ExecAppend; appendstate->appendplans = appendplanstates; appendstate->as_nplans = nplans; + appendstate->subplan_indexes = NIL; + appendstate->as_whichpartition = -1; + appendstate->append_paths_size = node->append_paths_size; + appendstate->append_paths_array = node->append_paths_array; + appendstate->parentoid = node->parentoid; /* * Miscellaneous initialization - * - * Append plans don't have expression contexts because they never call - * ExecQual or ExecProject. */ + /* create expression context for node */ + ExecAssignExprContext(estate, &appendstate->ps); + /* - * append nodes still have Result slots, which hold pointers to tuples, so - * we have to initialize them. + * append nodes have Result slots, which hold pointers to tuples, so we + * have to initialize them. */ ExecInitResultTupleSlot(estate, &appendstate->ps); @@ -178,9 +290,16 @@ ExecInitAppend(Append *node, EState *estate, int eflags) i++; } - /* - * initialize output tuple type - */ + if (node->base_params) + { + appendstate->base_params = node->base_params; + appendstate->es_param_list_info = estate->es_param_list_info; + } + + if (node->join_clauses) + appendstate->join_clauses = initClauses((PlanState *) appendstate, + node->join_clauses); + ExecAssignResultTypeFromTL(&appendstate->ps); appendstate->ps.ps_ProjInfo = NULL; @@ -204,6 +323,19 @@ ExecAppend(PlanState *pstate) { AppendState *node = castNode(AppendState, pstate); + /* Determine the subplans to scan depending on the base_params */ + if (node->base_params && node->as_whichpartition == -1) + { + set_append_subplan_indexes(node, node->base_params); + if (node->subplan_indexes) + { + node->as_whichplan = linitial_int(node->subplan_indexes); + node->as_whichpartition = 0; + } + else + node->as_whichplan = 0; + } + for (;;) { PlanState *subnode; @@ -212,8 +344,19 @@ ExecAppend(PlanState *pstate) CHECK_FOR_INTERRUPTS(); /* + * end scan if no subplan is selected for the current join_clauses + * and/or base_params + */ + if ((node->join_clauses || node->base_params) + && node->as_whichpartition == -1) + return ExecClearTuple(node->ps.ps_ResultTupleSlot); + + /* * figure out which subplan we are currently processing */ + if (node->as_whichpartition != -1) + node->as_whichplan = list_nth_int(node->subplan_indexes, + node->as_whichpartition); subnode = node->appendplans[node->as_whichplan]; /* @@ -237,7 +380,22 @@ ExecAppend(PlanState *pstate) * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + /* + * For runtime partition pruning, goto the next valid partition + * index + */ + if (node->subplan_indexes) + { + if (++node->as_whichpartition < list_length(node->subplan_indexes)) + node->as_whichplan = list_nth_int(node->subplan_indexes, + node->as_whichpartition); + else + return ExecClearTuple(node->ps.ps_ResultTupleSlot); + } + else + node->as_whichplan++; + } else node->as_whichplan--; if (!exec_append_initialize_next(node)) @@ -280,6 +438,10 @@ ExecReScanAppend(AppendState *node) { int i; + /* Determine subplans to scan based on the new Params */ + if (node->ps.chgParam != NULL && node->join_clauses) + set_append_subplan_indexes(node, node->join_clauses); + for (i = 0; i < node->as_nplans; i++) { PlanState *subnode = node->appendplans[i]; @@ -298,6 +460,16 @@ ExecReScanAppend(AppendState *node) if (subnode->chgParam == NULL) ExecReScan(subnode); } - node->as_whichplan = 0; + + if (node->subplan_indexes) + { + node->as_whichplan = linitial_int(node->subplan_indexes); + node->as_whichpartition = 0; + } + else + { + node->as_whichplan = 0; + node->as_whichpartition = -1; + } exec_append_initialize_next(node); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d9ff8a7..4af11e8 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -242,6 +242,11 @@ _copyAppend(const Append *from) */ COPY_NODE_FIELD(partitioned_rels); COPY_NODE_FIELD(appendplans); + COPY_NODE_FIELD(base_params); + COPY_NODE_FIELD(join_clauses); + COPY_SCALAR_FIELD(parentoid); + COPY_POINTER_FIELD(append_paths_array, from->append_paths_size * sizeof(int)); + COPY_SCALAR_FIELD(append_paths_size); return newnode; } diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 6b087ec..6d75b41 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -26,6 +26,7 @@ #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" +#include "catalog/pg_inherits_fn.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -144,9 +145,10 @@ static List *get_append_rel_partitions(PlannerInfo *root, static List *match_clauses_to_partkey(PlannerInfo *root, RelOptInfo *rel, List *clauses, + bool *contains_param, bool *contains_const, bool *constfalse); - +static int list_member_oid_index(List *list, Oid datum); /* * make_one_rel @@ -289,6 +291,27 @@ set_base_rel_sizes(PlannerInfo *root) if (root->glob->parallelModeOK) set_rel_consider_parallel(root, rel, rte); + if (rte->relkind == RELKIND_PARTITIONED_TABLE) + { + List *leaf_parts = NIL; + Relation parent = relation_open(rte->relid, NoLock); + int i; + + /* fetch the leaf oids of the parent rel */ + (void) find_all_inheritors(RelationGetRelid(parent), AccessShareLock, NULL); + get_leaf_part_recurse(parent, &leaf_parts); + relation_close(parent, NoLock); + + /* initialize necessary root variables */ + root->append_paths_size = list_length(leaf_parts); + root->leaf_node_oids = leaf_parts; + root->append_paths_array = palloc0(root->append_paths_size * sizeof(int)); + root->baserestrictinfo_param_indexes = NIL; + root->append_paths_count = 0; + + for (i = 0; i < root->append_paths_size; i++) + root->append_paths_array[i] = -1; + } set_rel_size(root, rel, rti, rte); } } @@ -350,6 +373,40 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, { /* It's an "append relation", process accordingly */ set_append_rel_size(root, rel, rti, rte); + + /* + * If this rel is part of a join then collect the necessary join + * clauses required for runtime partition pruning. + */ + if (rel->joininfo && rel->part_scheme) + { + List *partclauses; + bool contains_param, + contains_const, + constfalse; + + /* + * Get the clauses that match the partition key + */ + partclauses = match_clauses_to_partkey(root, rel, + list_copy(rel->joininfo), + &contains_param, + &contains_const, + &constfalse); + + if (partclauses != NIL) + { + ListCell *lc; + + foreach(lc, partclauses) + { + Node *n = lfirst(lc); + + if (!list_member(root->join_clauses, n)) + root->join_clauses = lappend(root->join_clauses, n); + } + } + } } else { @@ -871,7 +928,8 @@ get_append_rel_partitions(PlannerInfo *root, { List *partclauses; bool contains_const, - constfalse; + constfalse, + contains_param; /* * Get the clauses that match the partition key, including information @@ -880,6 +938,7 @@ get_append_rel_partitions(PlannerInfo *root, */ partclauses = match_clauses_to_partkey(root, rel, list_copy(rel->baserestrictinfo), + &contains_param, &contains_const, &constfalse); @@ -897,7 +956,7 @@ get_append_rel_partitions(PlannerInfo *root, */ if (partclauses != NIL && contains_const) partindexes = get_partitions_from_clauses(parent, rel->relid, - partclauses); + partclauses, NULL, NULL); /* * Else there are no clauses that are useful to prune any paritions, @@ -964,17 +1022,21 @@ get_append_rel_partitions(PlannerInfo *root, * * If the list contains a pseudo-constant RestrictInfo with constant false * value, *constfalse is set. + * + * If the list has a param, *contains_param is set */ static List * match_clauses_to_partkey(PlannerInfo *root, RelOptInfo *rel, List *clauses, + bool *contains_param, bool *contains_const, bool *constfalse) { PartitionScheme partscheme = rel->part_scheme; List *result = NIL; ListCell *lc; + int clause_index = -1; *contains_const = false; *constfalse = false; @@ -987,6 +1049,8 @@ match_clauses_to_partkey(PlannerInfo *root, Expr *clause; int i; + clause_index++; + if (IsA(member, RestrictInfo)) { RestrictInfo *rinfo = (RestrictInfo *) member; @@ -1024,14 +1088,24 @@ match_clauses_to_partkey(PlannerInfo *root, bool arg_matches_key = false, matched_arg_contains_const = false, all_args_constfalse = true; + List *base_param_indexes; + + /* + * Make a copy of root's baserestrictinfo_param_indexes before + * the recursive call + */ + base_param_indexes = + list_copy(root->baserestrictinfo_param_indexes); foreach (lc1, orclause->args) { Node *arg = lfirst(lc1); - bool contains_const1, - constfalse1; + bool contains_const1; + bool constfalse1; + bool contains_param1; if (match_clauses_to_partkey(root, rel, list_make1(arg), + &contains_param1, &contains_const1, &constfalse1) != NIL) { @@ -1042,8 +1116,22 @@ match_clauses_to_partkey(PlannerInfo *root, /* We got at least one arg that is not constant false. */ if (!constfalse1) all_args_constfalse = false; + + /* + * We got at least one arg which is Param, so add the + * current clause_index to the base_param_indexes list. + */ + if (contains_param1) + { + *contains_param = true; + base_param_indexes = + list_append_unique_int(base_param_indexes, + clause_index); + } } + root->baserestrictinfo_param_indexes = base_param_indexes; + if (arg_matches_key) { result = lappend(result, clause); @@ -1150,6 +1239,15 @@ match_clauses_to_partkey(PlannerInfo *root, if (contain_volatile_functions((Node *) constexpr)) continue; + if (IsA(constexpr, Param)) + { + *contains_param = true; + + root->baserestrictinfo_param_indexes = + list_append_unique_int(root->baserestrictinfo_param_indexes, + clause_index); + } + /* * Everything seems to be fine, so add it to the list of * clauses we will use for pruning. @@ -1249,6 +1347,26 @@ match_clauses_to_partkey(PlannerInfo *root, return result; } +/* list_member_oid_index + * Returns the index of the given datum in the oid list. + * + * It returns -1 if the datum is not found. + */ +static int +list_member_oid_index(List *list, Oid datum) +{ + int i = 0; + const ListCell *cell; + + foreach(cell, list) + { + if (lfirst_oid(cell) == datum) + return i; + i++; + } + return -1; +} + /* * set_append_rel_size * Set size estimates for a simple "append relation" @@ -1641,7 +1759,8 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, foreach(l, rel_appinfos) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); - int childRTindex; + int childRTindex, + index; RangeTblEntry *childRTE; RelOptInfo *childrel; @@ -1674,6 +1793,11 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, if (IS_DUMMY_REL(childrel)) continue; + /* only consider non dummy children */ + index = list_member_oid_index(root->leaf_node_oids, childRTE->relid); + if (index >= 0) + root->append_paths_array[index] = root->append_paths_count++; + /* * Child is live, so add it to the live_childrels list for use below. */ @@ -1855,7 +1979,7 @@ 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, NULL, 0, + add_path(rel, (Path *) create_append_path(root, rel, subpaths, NULL, 0, partitioned_rels)); /* @@ -1882,7 +2006,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, Assert(parallel_workers > 0); /* Generate a partial append path. */ - appendpath = create_append_path(rel, partial_subpaths, NULL, + appendpath = create_append_path(root, rel, partial_subpaths, NULL, parallel_workers, partitioned_rels); add_partial_path(rel, (Path *) appendpath); } @@ -1936,7 +2060,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, if (subpaths_valid) add_path(rel, (Path *) - create_append_path(rel, subpaths, required_outer, 0, + create_append_path(root, rel, subpaths, required_outer, 0, partitioned_rels)); } } @@ -2173,7 +2297,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel) rel->pathlist = NIL; rel->partial_pathlist = NIL; - add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL)); + add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL)); /* * 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 b491fb9..61fe647 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1232,7 +1232,7 @@ mark_dummy_rel(RelOptInfo *rel) rel->partial_pathlist = NIL; /* Set up the dummy path */ - add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL)); + add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL)); /* 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 d445477..a47373f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1001,6 +1001,24 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) return plan; } +static List * +replace_partition_nestloop_params(PlannerInfo *root, List *old_list) +{ + List *new_list = NIL; + ListCell *lc; + + if (old_list == NULL) + return NULL; + + foreach(lc, old_list) + { + Node *n = lfirst(lc); + + new_list = lappend(new_list, replace_nestloop_params(root, n)); + } + return new_list; +} + /* * create_append_plan * Create an Append plan for 'best_path' and (recursively) plans @@ -1063,6 +1081,12 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->base_params = best_path->base_params; + plan->join_clauses = replace_partition_nestloop_params(root, best_path->join_clauses); + plan->parentoid = best_path->parentoid; + plan->append_paths_size = best_path->append_paths_size; + plan->append_paths_array = best_path->append_paths_array; + return (Plan *) plan; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 28093ac..ffefcd0 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3678,7 +3678,7 @@ create_grouping_paths(PlannerInfo *root, paths = lappend(paths, path); } path = (Path *) - create_append_path(grouped_rel, + create_append_path(root, grouped_rel, paths, NULL, 0, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index f620243..12d0f85 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -590,7 +590,7 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root, /* * Append the child results together. */ - path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL); + path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL); /* We have to manually jam the right tlist into the path; ick */ path->pathtarget = create_pathtarget(root, tlist); @@ -702,7 +702,7 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, /* * Append the child results together. */ - path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL); + path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL); /* 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 72f1fa3..fbbcae8 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 68dee0f..9f9738f 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1208,7 +1208,7 @@ 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, List *subpaths, Relids required_outer, +create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, Relids required_outer, int parallel_workers, List *partitioned_rels) { AppendPath *pathnode = makeNode(AppendPath); @@ -1253,6 +1253,56 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer, Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer)); } + /* Do necessary evaluation needed for runtime partition pruning. */ + if (root && (required_outer || root->baserestrictinfo_param_indexes)) + { + RangeTblEntry *rte = planner_rt_fetch(rel->relid, root); + + pathnode->append_paths_array = root->append_paths_array; + pathnode->append_paths_size = root->append_paths_size; + + if (rte && rte->rtekind == RTE_RELATION) + { + Oid poid = rte->relid; + Relation prel = relation_open(poid, NoLock); + + if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + if (root->join_clauses) + pathnode->join_clauses = root->join_clauses; + + if (root->baserestrictinfo_param_indexes) + { + ListCell *lc; + List *quals = list_copy(rel->baserestrictinfo); + + pathnode->base_params = NIL; + + /* + * collect the clauses from baserestrictinfo for partition + * pruning + */ + foreach(lc, root->baserestrictinfo_param_indexes) + { + int index = lfirst_int(lc); + Node *member = list_nth(quals, index); + Expr *clause; + + if (IsA(member, RestrictInfo)) + clause = ((RestrictInfo *) member)->clause; + else + clause = (Expr *) member; + + pathnode->base_params = lappend(pathnode->base_params, + clause); + } + } + pathnode->parentoid = poid; + } + relation_close(prel, NoLock); + } + } + return pathnode; } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index a968fa4..545a34b 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -1555,6 +1555,9 @@ ParamPathInfo * get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) { ParamPathInfo *ppi; + Relids joinrelids; + List *pclauses; + ListCell *lc; /* Unparameterized paths have no ParamPathInfo */ if (bms_is_empty(required_outer)) @@ -1566,11 +1569,27 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) if ((ppi = find_param_path_info(appendrel, required_outer))) return ppi; + /* + * To determine whether the appendrel is applicable for runtime pruning or + * not, we fetch the clause from the join clause. + */ + joinrelids = bms_union(appendrel->relids, required_outer); + pclauses = NIL; + foreach(lc, appendrel->joininfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + if (join_clause_is_movable_into(rinfo, + appendrel->relids, + joinrelids)) + pclauses = lappend(pclauses, rinfo); + } + /* Else build the ParamPathInfo */ ppi = makeNode(ParamPathInfo); ppi->ppi_req_outer = required_outer; ppi->ppi_rows = 0; - ppi->ppi_clauses = NIL; + ppi->ppi_clauses = pclauses; appendrel->ppilist = lappend(appendrel->ppilist, ppi); return ppi; diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 2041de5..5a8acd9 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,7 @@ extern int get_partition_for_tuple(Relation relation, Datum *values, /* For partition-pruning */ extern Bitmapset *get_partitions_from_clauses(Relation relation, int rt_index, - List *partclauses); + List *partclauses, ParamListInfo base_prmlist, + ExprContext *econtext); +extern void get_leaf_part_recurse(Relation rel, List **leaf_part_oids); #endif /* PARTITION_H */ diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 64e5aab..6e7a841 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -49,6 +49,9 @@ typedef struct PartitionDispatchData typedef struct PartitionDispatchData *PartitionDispatch; +extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel, + int *num_parted, List **leaf_part_oids); + extern void ExecSetupPartitionTupleRouting(Relation rel, Index resultRTindex, EState *estate, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e05bc04..ad9cdac 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1006,6 +1006,16 @@ typedef struct AppendState PlanState **appendplans; /* array of PlanStates for my inputs */ int as_nplans; int as_whichplan; + + /* for runtime partition pruning */ + Oid parentoid; /* oid of the parent */ + int *append_paths_array; + int append_paths_size; /* size of append_paths_array */ + List *base_params; /* base restrictinfo on partition keys */ + List *join_clauses; /* join clauses on partiton keys */ + ParamListInfo es_param_list_info; + List *subplan_indexes; /* List of subplan indexes to scan */ + int as_whichpartition; /* current index to scan in index */ } AppendState; /* ---------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 9b38d44..698322d 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" #include "nodes/primnodes.h" +#include "nodes/relation.h" /* ---------------------------------------------------------------- @@ -248,6 +249,13 @@ typedef struct Append /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *appendplans; + + /* for runtime partition pruning */ + Oid parentoid; + int *append_paths_array; + int append_paths_size; + List *base_params; + List *join_clauses; } Append; /* ---------------- diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 94c2e8d..611fb74 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -317,6 +317,17 @@ typedef struct PlannerInfo /* optional private data for join_search_hook, e.g., GEQO */ void *join_search_private; + + /* These fields accumulate data required for runtime partition pruning. */ + int *append_paths_array; /* array for append subplans info */ + int append_paths_size; /* size of append_paths_array */ + int append_paths_count; /* current count of append_paths_array */ + List *leaf_node_oids; /* leaf oids of current rel */ + List *baserestrictinfo_param_indexes; /* index from baserestrictinfo + * list with partition related + * clauses */ + List *join_clauses; /* join clause which are required for + * partition pruning */ } PlannerInfo; @@ -1289,6 +1300,13 @@ typedef struct AppendPath /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *subpaths; /* list of component Paths */ + + /* for runtime partition pruning */ + Oid parentoid; + int *append_paths_array; + int append_paths_size; + List *base_params; + List *join_clauses; } AppendPath; #define IS_DUMMY_PATH(p) \ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 1ef13a4..f8e7660 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -79,6 +79,7 @@ extern void CommuteOpExpr(OpExpr *clause); 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); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index c1f2fc9..7c1fbfd 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -63,8 +63,8 @@ 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, List *subpaths, - Relids required_outer, int parallel_workers, +extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel, + List *subpaths, Relids required_outer, int parallel_workers, List *partitioned_rels); extern MergeAppendPath *create_merge_append_path(PlannerInfo *root, RelOptInfo *rel, -- 1.8.3.1