From ae31ae294cef9660fcc547abfc7543b5b3c85a1f Mon Sep 17 00:00:00 2001 From: Beena Emerson Date: Tue, 26 Sep 2017 09:26:28 +0530 Subject: [PATCH] Implement runtime partiton pruning Patch by: Beena Emerson, Dilip Kumar --- src/backend/executor/nodeAppend.c | 179 +++++++++++++++++++++++++++++++- src/backend/nodes/copyfuncs.c | 22 ++++ src/backend/nodes/outfuncs.c | 14 +++ src/backend/nodes/readfuncs.c | 18 ++++ src/backend/optimizer/path/allpaths.c | 171 ++++++++++++++++++++++++++++-- src/backend/optimizer/path/joinrels.c | 2 +- src/backend/optimizer/plan/createplan.c | 17 +++ src/backend/optimizer/plan/planner.c | 2 +- src/backend/optimizer/prep/prepunion.c | 4 +- src/backend/optimizer/util/clauses.c | 17 +++ src/backend/optimizer/util/pathnode.c | 38 ++++++- src/backend/optimizer/util/relnode.c | 24 ++++- src/backend/utils/cache/plancache.c | 2 +- src/include/nodes/execnodes.h | 13 +++ src/include/nodes/nodes.h | 1 + src/include/nodes/plannodes.h | 13 +++ src/include/nodes/relation.h | 13 +++ src/include/optimizer/clauses.h | 1 + src/include/optimizer/pathnode.h | 2 +- src/include/optimizer/paths.h | 3 + 20 files changed, 535 insertions(+), 21 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index bed9bb8..19587d4 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -57,9 +57,11 @@ #include "postgres.h" +#include "nodes/relation.h" #include "executor/execdebug.h" #include "executor/nodeAppend.h" #include "miscadmin.h" +#include "optimizer/clauses.h" static TupleTableSlot *ExecAppend(PlanState *pstate); static bool exec_append_initialize_next(AppendState *appendstate); @@ -165,6 +167,86 @@ ExecInitAppend(Append *node, EState *estate, int eflags) */ ExecInitResultTupleSlot(estate, &appendstate->ps); + if (node->et_rtp_list) + { + List *temp_appendplans = NIL; + Append *temp_node; + + Datum *minkeys, + *maxkeys; + Relation rel; + NullTestType keynullness = IS_NOT_NULL; + int min_datum_index, + max_datum_index; + bool null_partition_chosen = false; + bool default_partition_chosen = false; + int i = 0; + int n_minkeys = 0, + n_maxkeys = 0; + + minkeys = (Datum *) palloc(list_length(node->et_rtp_list) * sizeof(Datum)); + maxkeys = (Datum *) palloc(list_length(node->et_rtp_list) * sizeof(Datum)); + + foreach(lc, node->et_rtp_list) + { + RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc); + RuntimePruningExec *rtpe = palloc0(sizeof(RuntimePruningExec)); + + if (rtp->maxop) + { + Node *n = eval_const_expressions2(estate->es_param_list_info, (Node *) rtp->maxop); + + maxkeys[i] = ((Const *) n)->constvalue; + n_maxkeys++; + } + if (rtp->minop) + { + Node *n = eval_const_expressions2(estate->es_param_list_info, (Node *) rtp->minop); + + minkeys[i] = ((Const *) n)->constvalue; + n_minkeys++; + } + rtpe->minincl = rtp->minincl; + rtpe->maxincl = rtp->maxincl; + + i++; + } + + rel = relation_open(node->parentoid, NoLock); + appendstate->index = get_partitions_for_keys(rel, + &keynullness, + minkeys, n_minkeys, false, + maxkeys, n_maxkeys, false, + &min_datum_index, &max_datum_index, + &null_partition_chosen, + &default_partition_chosen); + relation_close(rel, NoLock); + + i = 0; + foreach(lc, appendstate->index) + { + int index = lfirst_int(lc); + + Plan *initNode = (Plan *) list_nth(node->appendplans, index); + + temp_appendplans = lappend(temp_appendplans, initNode); + appendplanstates[i++] = ExecInitNode(initNode, estate, eflags); + } + + /* create new AppendState for our append node */ + temp_node = copyObject(node); + temp_node->appendplans = temp_appendplans; + ((Plan *) temp_node)->plan_rows = i; + appendstate->as_nplans = i; + appendstate->ps.plan = (Plan *) temp_node; + appendstate->ps.state = estate; + appendstate->ps.ExecProcNode = ExecAppend; + appendstate->appendplans = (PlanState **) palloc0(i * sizeof(PlanState *)); + appendstate->appendplans = appendplanstates; + + return appendstate; + } + /* * call ExecInitNode on each of the plans to be executed and save the * results into the array "appendplans". @@ -190,6 +272,27 @@ ExecInitAppend(Append *node, EState *estate, int eflags) appendstate->as_whichplan = 0; exec_append_initialize_next(appendstate); + if (node->rtp_list) + { + ListCell *lc; + + foreach(lc, node->rtp_list) + { + RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc); + RuntimePruningExec *rtpe = palloc0(sizeof(RuntimePruningExec)); + + rtpe->maxop = ExecInitExpr(rtp->maxop, appendplanstates); + rtpe->minop = ExecInitExpr(rtp->minop, appendplanstates); + rtpe->minincl = rtp->minincl; + rtpe->maxincl = rtp->maxincl; + + appendstate->rtp_list = lappend(appendstate->rtp_list, rtpe); + } + appendstate->parentoid = node->parentoid; + ExecAssignExprContext(estate, &appendstate->ps); + } + + return appendstate; } @@ -237,9 +340,21 @@ 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->index) + { + if (++node->as_whichpartition < list_length(node->index)) + node->as_whichplan = list_nth_int(node->index, node->as_whichpartition); + + return ExecClearTuple(node->ps.ps_ResultTupleSlot); + } + else + node->as_whichplan++; + } else node->as_whichplan--; + if (!exec_append_initialize_next(node)) return ExecClearTuple(node->ps.ps_ResultTupleSlot); @@ -279,7 +394,60 @@ void ExecReScanAppend(AppendState *node) { int i; + ListCell *lc; + int n_minkeys = 0; + int n_maxkeys = 0; + if (node->ps.chgParam != NULL && node->rtp_list) + { + Datum *minkeys, + *maxkeys; + bool maxisnull, + minisnull; + Relation rel; + NullTestType keynullness = IS_NOT_NULL; + int min_datum_index, + max_datum_index; + bool null_partition_chosen = false; + bool default_partition_chosen = false; + int i; + + i = 0; + minkeys = (Datum *) palloc(list_length(node->rtp_list) * sizeof(Datum)); + maxkeys = (Datum *) palloc(list_length(node->rtp_list) * sizeof(Datum)); + + foreach(lc, node->rtp_list) + { + RuntimePruningExec *rtpe = (RuntimePruningExec *) lfirst(lc); + + if (rtpe->minop) + { + minkeys[i] = ExecEvalExpr(rtpe->minop, node->ps.ps_ExprContext, &minisnull); + n_minkeys++; + } + if (rtpe->maxop) + { + maxkeys[i] = ExecEvalExpr(rtpe->maxop, node->ps.ps_ExprContext, &maxisnull); + n_maxkeys++; + } + i++; + + if (node->parentoid) + { + rel = relation_open(node->parentoid, NoLock); + + /* FIXME: min_incl and max_incl is currently taken as false */ + node->index = get_partitions_for_keys(rel, + &keynullness, + minkeys, n_minkeys, false, + maxkeys, n_maxkeys, false, + &min_datum_index, &max_datum_index, + &null_partition_chosen, + &default_partition_chosen); + relation_close(rel, NoLock); + } + } + } for (i = 0; i < node->as_nplans; i++) { PlanState *subnode = node->appendplans[i]; @@ -298,6 +466,13 @@ ExecReScanAppend(AppendState *node) if (subnode->chgParam == NULL) ExecReScan(subnode); } - node->as_whichplan = 0; + + if (node->index) + { + node->as_whichplan = linitial_int(node->index); + node->as_whichpartition = 0; + } + else + node->as_whichplan = 0; exec_append_initialize_next(node); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f1bed14..a1562f3 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -242,6 +242,9 @@ _copyAppend(const Append *from) */ COPY_NODE_FIELD(partitioned_rels); COPY_NODE_FIELD(appendplans); + COPY_NODE_FIELD(rtp_list); + COPY_NODE_FIELD(et_rtp_list); + COPY_SCALAR_FIELD(parentoid); return newnode; } @@ -1145,6 +1148,22 @@ _copyNestLoopParam(const NestLoopParam *from) } /* + * _copyRuntimePruningInfo, + */ +static RuntimePruningInfo * +_copyRuntimePruningInfo(const RuntimePruningInfo *from) +{ + RuntimePruningInfo *newnode = makeNode(RuntimePruningInfo); + + COPY_NODE_FIELD(minop); + COPY_NODE_FIELD(maxop); + COPY_SCALAR_FIELD(minincl); + COPY_SCALAR_FIELD(maxincl); + + return newnode; +} + +/* * _copyPlanRowMark */ static PlanRowMark * @@ -4816,6 +4835,9 @@ copyObjectImpl(const void *from) case T_NestLoopParam: retval = _copyNestLoopParam(from); break; + case T_RuntimePruningInfo: + retval = _copyRuntimePruningInfo(from); + break; case T_PlanRowMark: retval = _copyPlanRowMark(from); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b83d919..5764c7a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -978,6 +978,17 @@ _outNestLoopParam(StringInfo str, const NestLoopParam *node) } static void +_outRuntimePruningInfo(StringInfo str, const RuntimePruningInfo *node) +{ + WRITE_NODE_TYPE("RUNTIMEPRUNINGINFO"); + + WRITE_NODE_FIELD(minop); + WRITE_NODE_FIELD(maxop); + WRITE_BOOL_FIELD(maxincl); + WRITE_BOOL_FIELD(minincl); +} + +static void _outPlanRowMark(StringInfo str, const PlanRowMark *node) { WRITE_NODE_TYPE("PLANROWMARK"); @@ -3746,6 +3757,9 @@ outNode(StringInfo str, const void *obj) case T_NestLoopParam: _outNestLoopParam(str, obj); break; + case T_RuntimePruningInfo: + _outRuntimePruningInfo(str, obj); + break; case T_PlanRowMark: _outPlanRowMark(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index fbf8330..b090837 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2277,6 +2277,22 @@ _readNestLoopParam(void) } /* + * _readRuntimePruningInfo + */ +static RuntimePruningInfo * +_readRuntimePruningInfo(void) +{ + READ_LOCALS(RuntimePruningInfo); + + READ_NODE_FIELD(minop); + READ_NODE_FIELD(maxop); + READ_BOOL_FIELD(minincl); + READ_BOOL_FIELD(maxincl); + + READ_DONE(); +} + +/* * _readPlanRowMark */ static PlanRowMark * @@ -2646,6 +2662,8 @@ parseNodeString(void) return_value = _readLimit(); else if (MATCH("NESTLOOPPARAM", 13)) return_value = _readNestLoopParam(); + else if (MATCH("RUNTIMEPRUNINGINFO", 18)) + return_value = _readRuntimePruningInfo(); else if (MATCH("PLANROWMARK", 11)) return_value = _readPlanRowMark(); else if (MATCH("PLANINVALITEM", 13)) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 2bb3641..c34826d 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -848,7 +848,7 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) static void get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses, - NullTestType *keynullness) + NullTestType *keynullness, bool is_extern) { ListCell *lc; int keyPos; @@ -883,6 +883,8 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses, if (equal(leftop, partkey)) { + if (is_extern && !IsA(rightop, Param)) + continue; matchedclauses[keyPos] = lappend(matchedclauses[keyPos], clause); /* A strict operator implies NOT NULL argument. */ @@ -892,6 +894,9 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses, { Oid commutator = get_commutator(expr_op); + if (is_extern && !IsA(leftop, Param)) + continue; + if (OidIsValid(commutator)) { OpExpr *commutated_expr; @@ -929,6 +934,143 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses, } /* + * FIXME, this function can reuse most of the code from get_rel_partitions. we + * can make get_rel_partitions function generic so that it can accept the + * clause instead of processing all the baserelrestriction info, this way this + * function will be more generic and can be used for appendrel as well as for + * baserel. + */ +List * +get_rel_partition_info(RelOptInfo *rel, RangeTblEntry *rte, List *clauses, + RuntimePruningInfo * *prtp, bool is_extern) +{ + Relation parent = heap_open(rte->relid, NoLock); + ListCell *lc1; + List *matchedclauses[PARTITION_MAX_KEYS]; + NullTestType keynullness[PARTITION_MAX_KEYS]; + bool need_next_min, + need_next_max, + minkey_set[PARTITION_MAX_KEYS], + maxkey_set[PARTITION_MAX_KEYS], + min_incl, + max_incl; + int n_minkeys = 0, + n_maxkeys = 0, + i; + + List *rtp_list = NIL; + + get_matching_clause(rel, clauses, matchedclauses, keynullness, is_extern); + + /* + * Determine the min keys and the max keys using btree semantics-based + * interpretation of the clauses' operators. + */ + + /* + * XXX - There should be a step similar to _bt_preprocess_keys() here, to + * eliminate any redundant scan keys for a given partition column. For + * example, among a <= 4 and a <= 5, we can only keep a <= 4 for being + * more restrictive and discard a <= 5. While doing that, we can also + * check to see if there exists a contradictory combination of scan keys + * that makes the query trivially false for all records in the table. + */ + memset(minkey_set, false, sizeof(minkey_set)); + memset(maxkey_set, false, sizeof(maxkey_set)); + need_next_min = true; + need_next_max = true; + for (i = 0; i < rel->part_scheme->partnatts; i++) + { + /* + * If no scan key existed for the previous column, we are done. + */ + if (i > n_minkeys) + need_next_min = false; + + if (i > n_maxkeys) + need_next_max = false; + + foreach(lc1, matchedclauses[i]) + { + Expr *clause = lfirst(lc1); + Expr *leftop = (Expr *) get_leftop(clause); + Expr *rightop = (Expr *) get_rightop(clause); + Oid opno = ((OpExpr *) clause)->opno, + opfamily = rel->part_scheme->partopfamily[i]; + StrategyNumber strategy; + RuntimePruningInfo *rtp_temp = makeNode(RuntimePruningInfo); + + strategy = get_op_opfamily_strategy(opno, opfamily); + switch (strategy) + { + case BTLessStrategyNumber: + case BTLessEqualStrategyNumber: + if (need_next_max) + { + rtp_temp->maxop = rightop; + if (!maxkey_set[i]) + n_maxkeys++; + maxkey_set[i] = true; + max_incl = (strategy == BTLessEqualStrategyNumber); + } + if (strategy == BTLessStrategyNumber) + need_next_max = false; + break; + + case BTGreaterStrategyNumber: + case BTGreaterEqualStrategyNumber: + if (need_next_min) + { + rtp_temp->minop = rightop; + if (!minkey_set[i]) + n_minkeys++; + minkey_set[i] = true; + min_incl = (strategy == BTGreaterEqualStrategyNumber); + } + if (strategy == BTGreaterStrategyNumber) + need_next_min = false; + break; + + case BTEqualStrategyNumber: + if (need_next_min) + { + rtp_temp->minop = rightop; + if (!minkey_set[i]) + n_minkeys++; + } + minkey_set[i] = true; + min_incl = true; + + if (need_next_max) + { + rtp_temp->maxop = rightop; + if (!maxkey_set[i]) + n_maxkeys++; + } + maxkey_set[i] = true; + max_incl = true; + break; + + /* + * This might mean '<>', but we don't have anything for + * that case yet. Perhaps, handle that as key < const OR + * key > const, once we have props needed for handling OR + * clauses. + */ + default: + min_incl = max_incl = false; + break; + } + + rtp_list = lappend(rtp_list, rtp_temp); + } + } + + heap_close(parent, NoLock); + return rtp_list; +} + +/* * get_rel_partitions * Return the list of partitions of rel that pass the query clauses * @@ -938,6 +1080,7 @@ static List * get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relation parent = heap_open(rte->relid, NoLock); + PartitionDesc partdesc = RelationGetPartitionDesc(parent); List *indexes; List *result = NIL; ListCell *lc1; @@ -956,7 +1099,7 @@ get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) i; get_matching_clause(rel, rel->baserestrictinfo, matchedclauses, - keynullness); + keynullness, false); /* * Determine the min keys and the max keys using btree semantics-based @@ -990,11 +1133,17 @@ get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) foreach(lc1, matchedclauses[i]) { - Expr *clause = lfirst(lc1); - Const *rightop = (Const *) get_rightop(clause); - Oid opno = ((OpExpr *) clause)->opno, - opfamily = rel->part_scheme->partopfamily[i]; - StrategyNumber strategy; + Expr *clause = lfirst(lc1); + Expr *rightop_expr = (Expr *) get_rightop(clause); + Oid opno = ((OpExpr *) clause)->opno, + opfamily = rel->part_scheme->partopfamily[i]; + StrategyNumber strategy; + Const *rightop; + + if (IsA(rightop_expr, Param)) + continue; + + rightop = (Const *) rightop_expr; strategy = get_op_opfamily_strategy(opno, opfamily); switch (strategy) @@ -1720,7 +1869,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)); /* @@ -1747,7 +1896,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); } @@ -1801,7 +1950,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)); } } @@ -2038,7 +2187,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 6ee2350..4b58eb4 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1217,7 +1217,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 2821662..991cb64 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -991,6 +991,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) return plan; } + /* * create_append_plan * Create an Append plan for 'best_path' and (recursively) plans @@ -1053,6 +1054,22 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) copy_generic_path_info(&plan->plan, (Path *) best_path); + if (best_path->rtp_list) + { + ListCell *lc; + + foreach(lc, best_path->rtp_list) + { + RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc); + + rtp->minop = (Expr *) replace_nestloop_params(root, (Node *) rtp->minop); + rtp->maxop = (Expr *) replace_nestloop_params(root, (Node *) rtp->maxop); + } + plan->rtp_list = best_path->rtp_list; + } + + plan->et_rtp_list = best_path->et_rtp_list; + plan->parentoid = best_path->parentoid; return (Plan *) plan; } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 7f146d6..0e2d05f 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3643,7 +3643,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 3e0c3de..88b0033 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 93add27..15620a0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -189,6 +189,22 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset, return (Expr *) expr; } +Node * +eval_const_expressions2(ParamListInfo prmList, Node *node) +{ + eval_const_expressions_context context; + + if (prmList) + context.boundParams = prmList; /* bound Params */ + else + context.boundParams = NULL; + 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); +} + /* * get_leftop * @@ -2507,6 +2523,7 @@ eval_const_expressions_mutator(Node *node, pval = prm->value; else pval = datumCopy(prm->value, typByVal, typLen); + return (Node *) makeConst(param->paramtype, param->paramtypmod, param->paramcollid, diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 26567cb..632105e 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -16,6 +16,7 @@ #include +#include "catalog/pg_class.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -27,6 +28,7 @@ #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/selfuncs.h" @@ -1200,7 +1202,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); @@ -1245,6 +1247,40 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer, Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer)); } + if (required_outer || rel->baserestrictinfo) + { + RangeTblEntry *rte = planner_rt_fetch(rel->relid, root); + List *query_quals = rel->baserestrictinfo; + + if (rte && rte->rtekind == RTE_RELATION) + { + Oid poid = rte->relid; + Relation prel = RelationIdGetRelation(poid); + + if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + RuntimePruningInfo *rtp; + ParamPathInfo *ppi = pathnode->path.param_info; + + if (ppi) + { + List *ppi_clauses = ppi->ppi_clauses; + + pathnode->rtp_list = get_rel_partition_info(rel, rte, ppi_clauses, &rtp, false); + } + /* EXTERN PARAM */ + if (query_quals) + { + pathnode->et_rtp_list = get_rel_partition_info(rel, rte, query_quals, &rtp, true); + } + pathnode->parentoid = poid; + + } + } + + + } + return pathnode; } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index f0973b8..d9cc3f8 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -1346,6 +1346,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)) @@ -1357,11 +1360,30 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) if ((ppi = find_param_path_info(appendrel, required_outer))) return ppi; + + /* + * FIXME: Generally for appendrel we don't fetch the clause from the the + * join clause (only we do so for baserel), but for identifying whether + * the appendrel is applicable for runtime pruning or not we need clause + * also, I think there should be better way to do that. + */ + 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/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index ad8a82f..458767d 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -1041,7 +1041,7 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams) if (plansource->num_custom_plans < 5) return true; - avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans; + avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans + 100000; /* * Prefer generic plan if it's less expensive than the average custom diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index c6d3021..e7de368 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -990,6 +990,14 @@ typedef struct ModifyTableState /* Per plan/partition tuple conversion */ } ModifyTableState; +typedef struct RuntimePruningExec +{ + ExprState *minop; + ExprState *maxop; + bool minincl; + bool maxincl; +} RuntimePruningExec; + /* ---------------- * AppendState information * @@ -1003,6 +1011,11 @@ typedef struct AppendState PlanState **appendplans; /* array of PlanStates for my inputs */ int as_nplans; int as_whichplan; + Oid parentoid; + + int as_whichpartition; + List *index; + List *rtp_list; } AppendState; /* ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 63196a1..2c16f8b 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -87,6 +87,7 @@ typedef enum NodeTag T_NestLoopParam, T_PlanRowMark, T_PlanInvalItem, + T_RuntimePruningInfo, /* * TAGS FOR PLAN STATE NODES (execnodes.h) diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index a382331..c10d554 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -237,6 +237,15 @@ typedef struct ModifyTable List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ } ModifyTable; +typedef struct RuntimePruning +{ + Expr *minop; + Expr *maxop; + bool minincl; + bool maxincl; + Oid parentoid; +} RuntimePruning; + /* ---------------- * Append node - * Generate the concatenation of the results of sub-plans. @@ -248,6 +257,10 @@ typedef struct Append /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *appendplans; + Oid parentoid; + RuntimePruning *rtp; + List *rtp_list; + List *et_rtp_list; } Append; /* ---------------- diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 0f4996b..f1a2234 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1246,6 +1246,15 @@ typedef struct CustomPath const struct CustomPathMethods *methods; } CustomPath; +typedef struct RuntimePruningInfo +{ + NodeTag type; + Expr *minop; + Expr *maxop; + bool minincl; + bool maxincl; +} RuntimePruningInfo; + /* * AppendPath represents an Append plan, ie, successive execution of * several member plans. @@ -1261,6 +1270,10 @@ typedef struct AppendPath /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *subpaths; /* list of component Paths */ + bool runtime_prunable; + Oid parentoid; + List *rtp_list; + List *et_rtp_list; } AppendPath; #define IS_DUMMY_PATH(p) \ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index e367221..c19ee31 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_expressions2(ParamListInfo prm_list, 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 e372f88..4bcc3c7 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -63,7 +63,7 @@ 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, +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, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 4e06b2e..b89cb95 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -59,6 +59,9 @@ extern int compute_parallel_worker(RelOptInfo *rel, double heap_pages, extern void create_partial_bitmap_paths(PlannerInfo *root, RelOptInfo *rel, Path *bitmapqual); +List *get_rel_partition_info(RelOptInfo *rel, RangeTblEntry *rte, List *clauses, + RuntimePruningInfo **prtp, bool is_extern); + #ifdef OPTIMIZER_DEBUG extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel); #endif -- 1.8.3.1