From 77e135e68ea1753720921e4805b773fef2259fdc Mon Sep 17 00:00:00 2001 From: Beena Emerson Date: Tue, 14 Nov 2017 11:19:33 +0530 Subject: [PATCH 2/2] Implement runtime partiton pruning Patch by: Beena Emerson, Dilip Kumar Discussion: https://postgr.es/m/CAOG9ApE16ac-_VVZVvv0gePSgkg_BwYEV1NBqZFqDR2bBE0X0A@mail.gmail.com --- src/backend/catalog/partition.c | 53 +++--- src/backend/executor/nodeAppend.c | 274 ++++++++++++++++++++++++++++++-- src/backend/nodes/copyfuncs.c | 27 ++++ src/backend/nodes/readfuncs.c | 2 + src/backend/optimizer/path/allpaths.c | 76 +++++++-- src/backend/optimizer/path/joinrels.c | 2 +- src/backend/optimizer/plan/createplan.c | 46 ++++++ src/backend/optimizer/plan/planner.c | 2 +- src/backend/optimizer/prep/prepunion.c | 4 +- src/backend/optimizer/util/clauses.c | 16 ++ src/backend/optimizer/util/pathnode.c | 36 ++++- src/backend/optimizer/util/relnode.c | 23 ++- src/include/catalog/partition.h | 2 +- src/include/nodes/execnodes.h | 4 + src/include/nodes/nodes.h | 1 + src/include/nodes/plannodes.h | 4 + src/include/nodes/relation.h | 54 ++++--- src/include/optimizer/clauses.h | 1 + src/include/optimizer/pathnode.h | 4 +- src/include/optimizer/paths.h | 5 + 20 files changed, 556 insertions(+), 80 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 97f3050..7ef97fe 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -1529,9 +1529,11 @@ get_partitions_from_clauses_guts(Relation relation, List *clauses) List *or_clauses; ListCell *lc; + memset(&keys, 0, sizeof(PartScanKeyInfo)); + nkeys = classify_partition_bounding_keys(relation, clauses, &keys, &constfalse, - &or_clauses); + &or_clauses, false); /* * Only look up in the partition decriptor if the query provides * constraints on the keys at all. @@ -1595,7 +1597,7 @@ get_partitions_from_clauses_guts(Relation relation, List *clauses) int classify_partition_bounding_keys(Relation relation, List *clauses, PartScanKeyInfo *keys, bool *constfalse, - List **or_clauses) + List **or_clauses, bool is_runtime) { PartitionKey partkey = RelationGetPartitionKey(relation); int i; @@ -2079,7 +2081,6 @@ classify_partition_bounding_keys(Relation relation, List *clauses, n_eqkeys = 0; /* Populate keys. */ - memset(keys, 0, sizeof(PartScanKeyInfo)); if (n_eqkeys + n_minkeys + n_maxkeys + n_keynullness > 0) { Datum value; @@ -2088,11 +2089,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses, n_datums_resolved = 0; for (i = 0; i < n_eqkeys; i++) { - if (partkey_datum_from_expr(eqkey_exprs[i], &value)) - { - keys->eqkeys[i] = value; - n_datums_resolved++; - } + if (IsA(eqkey_exprs[i], Const) && + partkey_datum_from_expr(eqkey_exprs[i], &value)) + keys->eqkeys_datums[i] = value; + else + keys->eqkeys = lappend(keys->eqkeys, eqkey_exprs[i]); + n_datums_resolved++; } keys->n_eqkeys = n_datums_resolved; n_total += keys->n_eqkeys; @@ -2100,11 +2102,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses, n_datums_resolved = 0; for (i = 0; i < n_minkeys; i++) { - if (partkey_datum_from_expr(minkey_exprs[i], &value)) - { - keys->minkeys[i] = value; - n_datums_resolved++; - } + if (IsA(minkey_exprs[i], Const) && + partkey_datum_from_expr(minkey_exprs[i], &value)) + keys->minkeys_datums[i] = value; + else + keys->minkeys = lappend(keys->minkeys, minkey_exprs[i]); + n_datums_resolved++; } keys->n_minkeys = n_datums_resolved; n_total += keys->n_minkeys; @@ -2113,11 +2116,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses, n_datums_resolved = 0; for (i = 0; i < n_maxkeys; i++) { - if (partkey_datum_from_expr(maxkey_exprs[i], &value)) - { - keys->maxkeys[i] = value; - n_datums_resolved++; - } + if (IsA(maxkey_exprs[i], Const) && + partkey_datum_from_expr(maxkey_exprs[i], &value)) + keys->maxkeys_datums[i] = value; + else + keys->maxkeys = lappend(keys->maxkeys, maxkey_exprs[i]); + n_datums_resolved++; } keys->n_maxkeys = n_datums_resolved; n_total += keys->n_maxkeys; @@ -2128,6 +2132,9 @@ classify_partition_bounding_keys(Relation relation, List *clauses, n_total += n_keynullness; } + if (n_total == 0) + keys = NULL; + return n_total; } @@ -2148,6 +2155,10 @@ partkey_datum_from_expr(const Expr *expr, Datum *value) *value = ((Const *) expr)->constvalue; return true; + case T_Param: + *value = '\0'; + return true; + default: return false; } @@ -2427,7 +2438,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys) if (keys->n_eqkeys > 0) { memset(&arg, 0, sizeof(PartitionBoundCmpArg)); - arg.datums = keys->eqkeys; + arg.datums = keys->eqkeys_datums; arg.ndatums = keys->n_eqkeys; eqoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal); @@ -2475,7 +2486,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys) if (keys->n_minkeys > 0) { memset(&arg, 0, sizeof(PartitionBoundCmpArg)); - arg.datums = keys->minkeys; + arg.datums = keys->minkeys_datums; arg.ndatums = keys->n_minkeys; minoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal); @@ -2561,7 +2572,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys) if (keys->n_maxkeys > 0) { memset(&arg, 0, sizeof(PartitionBoundCmpArg)); - arg.datums = keys->maxkeys; + arg.datums = keys->maxkeys_datums; arg.ndatums = keys->n_maxkeys; maxoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal); diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 1d2fb35..c0e0c05 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); @@ -108,6 +110,114 @@ exec_append_initialize_next(AppendState *appendstate) } /* ---------------------------------------------------------------- + * extern_eval_const_expressions + * + * Evalute the expressions to a constant. + * ---------------------------------------------------------------- + */ +static PartScanKeyInfo * +extern_eval_const_expressions(EState *estate, PartScanKeyInfo *old_keys) +{ + PartScanKeyInfo *keys; + int i; + ListCell *lc; + + if (old_keys == NULL) + return NULL; + + keys = makeNode(PartScanKeyInfo); + i = 0; + foreach(lc, old_keys->eqkeys) + { + Node *val = lfirst(lc); + Node *n = + eval_const_expressions_from_list(estate->es_param_list_info, val); + + Assert(IsA(n, Const)); + keys->eqkeys_datums[i++] = ((Const *) n)->constvalue; + } + keys->n_eqkeys = old_keys->n_eqkeys; + + i = 0; + foreach(lc, old_keys->minkeys) + { + Node *val = lfirst(lc); + Node *n = + eval_const_expressions_from_list(estate->es_param_list_info, val); + + Assert(IsA(n, Const)); + keys->minkeys_datums[i++] = ((Const *) n)->constvalue; + } + + keys->n_minkeys = old_keys->n_minkeys; + keys->min_incl = old_keys->min_incl; + + i = 0; + foreach(lc, old_keys->maxkeys) + { + Node *val = lfirst(lc); + Node *n = + eval_const_expressions_from_list(estate->es_param_list_info, val); + + Assert(IsA(n, Const)); + keys->maxkeys_datums[i++] = ((Const *) n)->constvalue; + } + keys->n_maxkeys = old_keys->n_maxkeys; + keys->max_incl = old_keys->max_incl; + + memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness)); + + return keys; +} + +/* ---------------------------------------------------------------- + * exec_InitExpr + * + * Evaluate and store the output of ExecInitExpr for each of the keys. + * ---------------------------------------------------------------- + */ +static PartScanKeyInfo * +exec_InitExpr(PlanState *parent, PartScanKeyInfo *old_keys) +{ + PartScanKeyInfo *keys; + ListCell *lc; + + if (old_keys == NULL) + return NULL; + + keys = makeNode(PartScanKeyInfo); + + foreach(lc, old_keys->eqkeys) + { + Expr *val = (Expr *) lfirst(lc); + + keys->eqkeys = lappend(keys->eqkeys, ExecInitExpr(val, parent)); + } + keys->n_eqkeys = old_keys->n_eqkeys; + + foreach(lc, old_keys->minkeys) + { + Expr *val = (Expr *) lfirst(lc); + + keys->minkeys = lappend(keys->minkeys, ExecInitExpr(val, parent)); + } + keys->n_minkeys = old_keys->n_minkeys; + keys->min_incl = old_keys->min_incl; + + foreach(lc, old_keys->maxkeys) + { + Expr *val = (Expr *) lfirst(lc); + + keys->maxkeys = lappend(keys->maxkeys, ExecInitExpr(val, parent)); + } + keys->n_maxkeys = old_keys->n_maxkeys; + keys->max_incl = old_keys->max_incl; + + memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness)); + return keys; +} + +/* ---------------------------------------------------------------- * ExecInitAppend * * Begin all of the subscans of the append node. @@ -165,22 +272,67 @@ ExecInitAppend(Append *node, EState *estate, int eflags) */ ExecInitResultTupleSlot(estate, &appendstate->ps); - /* - * call ExecInitNode on each of the plans to be executed and save the - * results into the array "appendplans". - */ - i = 0; - foreach(lc, node->appendplans) + if (node->et_keys) { - Plan *initNode = (Plan *) lfirst(lc); + List *temp_appendplans = NIL; + Append *temp_node; + Relation rel; + PartScanKeyInfo *keys; + Bitmapset *partset; + int i, + j; - appendplanstates[i] = ExecInitNode(initNode, estate, eflags); - i++; + keys = extern_eval_const_expressions(estate, node->et_keys); + + rel = relation_open(node->parentoid, NoLock); + partset = get_partitions_for_keys(rel, keys); + relation_close(rel, NoLock); + + if (!bms_is_empty(partset)) + { + i = 0; + while ((j = bms_first_member(partset)) >= 0) + { + Plan *initNode = (Plan *) list_nth(node->appendplans, j); + + 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; } + else + { + appendstate->parentoid = node->parentoid; + ExecAssignExprContext(estate, &appendstate->ps); + + /* + * call ExecInitNode on each of the plans to be executed and save the + * results into the array "appendplans". + */ + i = 0; + foreach(lc, node->appendplans) + { + Plan *initNode = (Plan *) lfirst(lc); + + appendplanstates[i] = ExecInitNode(initNode, estate, eflags); + i++; + } + } + + if (node->ex_keys) + appendstate->ex_keys = + exec_InitExpr((PlanState *) appendstate, node->ex_keys); - /* - * initialize output tuple type - */ ExecAssignResultTypeFromTL(&appendstate->ps); appendstate->ps.ps_ProjInfo = NULL; @@ -237,7 +389,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)) @@ -275,11 +441,84 @@ ExecEndAppend(AppendState *node) ExecEndNode(appendplans[i]); } +/* ---------------------------------------------------------------- + * exec_EvalExpr + * + * Convert the expressions to a constant. + * ---------------------------------------------------------------- + */ +static PartScanKeyInfo * +exec_EvalExpr(ExprContext *econtext, PartScanKeyInfo *old_keys) +{ + PartScanKeyInfo *keys; + int i; + bool isnull; + ListCell *lc; + + if (old_keys == NULL) + return NULL; + + keys = makeNode(PartScanKeyInfo); + i = 0; + foreach(lc, old_keys->eqkeys) + { + ExprState *val = lfirst(lc); + + keys->eqkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull); + } + keys->n_eqkeys = old_keys->n_eqkeys; + + i = 0; + foreach(lc, old_keys->minkeys) + { + ExprState *val = lfirst(lc); + + keys->minkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull); + } + keys->n_minkeys = old_keys->n_minkeys; + keys->min_incl = old_keys->min_incl; + + i = 0; + foreach(lc, old_keys->maxkeys) + { + ExprState *val = lfirst(lc); + + keys->maxkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull); + } + keys->n_maxkeys = old_keys->n_maxkeys; + keys->max_incl = old_keys->max_incl; + + memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness)); + return keys; +} + void ExecReScanAppend(AppendState *node) { int i; + if (node->ps.chgParam != NULL && node->ex_keys) + { + Bitmapset *partset; + PartScanKeyInfo *ex_keys; + + node->index = NIL; + ex_keys = exec_EvalExpr(node->ps.ps_ExprContext, node->ex_keys); + if (node->parentoid) + { + Relation rel = relation_open(node->parentoid, NoLock); + + partset = get_partitions_for_keys(rel, ex_keys); + relation_close(rel, NoLock); + } + if (!bms_is_empty(partset)) + { + int j; + + while ((j = bms_first_member(partset)) >= 0) + node->index = lappend_int(node->index, j); + } + } for (i = 0; i < node->as_nplans; i++) { PlanState *subnode = node->appendplans[i]; @@ -298,6 +537,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 c1a83ca..95dddab 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -224,6 +224,27 @@ _copyModifyTable(const ModifyTable *from) return newnode; } +static PartScanKeyInfo * +_copyPartScanKeyInfo(const PartScanKeyInfo *from) +{ + PartScanKeyInfo *newnode = makeNode(PartScanKeyInfo); + + COPY_SCALAR_FIELD(n_eqkeys); + COPY_NODE_FIELD(eqkeys); + + COPY_SCALAR_FIELD(n_minkeys); + COPY_NODE_FIELD(minkeys); + COPY_SCALAR_FIELD(min_incl); + + COPY_SCALAR_FIELD(n_maxkeys); + COPY_NODE_FIELD(maxkeys); + COPY_SCALAR_FIELD(max_incl); + + memcpy(newnode->keynullness, from->keynullness, sizeof(newnode->keynullness)); + + return newnode; +} + /* * _copyAppend */ @@ -242,6 +263,9 @@ _copyAppend(const Append *from) */ COPY_NODE_FIELD(partitioned_rels); COPY_NODE_FIELD(appendplans); + COPY_NODE_FIELD(ex_keys); + COPY_NODE_FIELD(et_keys); + COPY_SCALAR_FIELD(parentoid); return newnode; } @@ -5014,6 +5038,9 @@ copyObjectImpl(const void *from) case T_PlaceHolderInfo: retval = _copyPlaceHolderInfo(from); break; + case T_PartScanKeyInfo: + retval = _copyPartScanKeyInfo(from); + break; /* * VALUE NODES diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index ccb6a1f..15e6764 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2652,6 +2652,8 @@ parseNodeString(void) return_value = _readLimit(); else if (MATCH("NESTLOOPPARAM", 13)) return_value = _readNestLoopParam(); + else if (MATCH("PARTSCANKEYINFO", 15)) + return_value = _readNestLoopParam(); 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 a3d0468..166eb2e 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -18,6 +18,7 @@ #include #include +#include "catalog/partition.h" #include "access/sysattr.h" #include "access/tsmapi.h" #include "catalog/partition.h" @@ -140,11 +141,11 @@ static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, List *live_childrels); static List *get_append_rel_partitions(PlannerInfo *root, RelOptInfo *rel, - RangeTblEntry *rte); + RangeTblEntry *rte, bool is_runtime); static List *match_clauses_to_partkey(RelOptInfo *rel, List *clauses, bool *contains_const, - bool *constfalse); + bool *constfalse, bool is_runtime); /* @@ -855,6 +856,47 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) rel->fdwroutine->GetForeignPaths(root, rel, rte->relid); } + +PartScanKeyInfo * +get_append_partition_info(RelOptInfo *rel, RangeTblEntry *rte, + bool is_runtime, List *clauses) +{ + bool constfalse, + contains_constant; + PartScanKeyInfo *keys = NULL; + List *partclauses; + + /* + * Get the clauses that match the partition key, including information + * about any nullness tests against partition keys. Set keynullness to a + * invalid value of NullTestType, which 0 is not. + */ + partclauses = match_clauses_to_partkey(rel, + list_copy(clauses), + &contains_constant, + &constfalse, is_runtime); + + if (partclauses != NIL) + { + bool constfalse; + List *or_clauses; + Relation parent = heap_open(rte->relid, NoLock); + PartScanKeyInfo *temp_keys = makeNode(PartScanKeyInfo); + int nkeys = classify_partition_bounding_keys(parent, partclauses, + temp_keys, + &constfalse, + &or_clauses, + true); + + if (nkeys > 0) + keys = temp_keys; + + heap_close(parent, NoLock); + } + return keys; +} + + /* * get_append_rel_partitions * Return the list of partitions of rel that pass the clauses mentioned @@ -865,7 +907,7 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) static List * get_append_rel_partitions(PlannerInfo *root, RelOptInfo *rel, - RangeTblEntry *rte) + RangeTblEntry *rte, bool is_runtime) { Relation parent = heap_open(rte->relid, NoLock); PartitionDesc partdesc = RelationGetPartitionDesc(parent); @@ -884,7 +926,7 @@ get_append_rel_partitions(PlannerInfo *root, partclauses = match_clauses_to_partkey(rel, list_copy(rel->baserestrictinfo), &contains_const, - &constfalse); + &constfalse, is_runtime); /* * If the matched clauses contains at least some constant operands, use @@ -956,7 +998,8 @@ static List * match_clauses_to_partkey(RelOptInfo *rel, List *clauses, bool *contains_const, - bool *constfalse) + bool *constfalse, + bool is_runtime) { PartitionScheme partscheme = rel->part_scheme; List *result = NIL; @@ -1003,7 +1046,8 @@ match_clauses_to_partkey(RelOptInfo *rel, match_clauses_to_partkey(rel, list_copy(((BoolExpr *) clause)->args), &contains_const1, - &constfalse1) != NIL) + &constfalse1, + is_runtime) != NIL) { result = lappend(result, clause); *contains_const = contains_const1; @@ -1079,6 +1123,12 @@ match_clauses_to_partkey(RelOptInfo *rel, /* Neither argument matches the partition key. */ continue; + if (IsA(constexpr, Const) && is_runtime) + continue; + + if (IsA(constexpr, Param) && !is_runtime) + continue; + /* * Only allow strict operators to think sanely about the * behavior with null arguments. @@ -1184,7 +1234,7 @@ match_clauses_to_partkey(RelOptInfo *rel, if (!PartCollMatchesExprColl(partcoll, saop_coll)) continue; } - else if (IsA(clause, NullTest)) + else if (!is_runtime && IsA(clause, NullTest)) { NullTest *nulltest = (NullTest *) clause; Node *arg = (Node *) nulltest->arg; @@ -1200,7 +1250,7 @@ match_clauses_to_partkey(RelOptInfo *rel, * Boolean conditions have a special shape, which accept if the * partitioning opfamily accepts Boolean conditions. */ - else if (IsBooleanOpfamily(partopfamily) && + else if (!is_runtime && IsBooleanOpfamily(partopfamily) && (IsA(clause, BooleanTest) || IsA(clause, Var) || not_clause((Node *) clause))) { @@ -1272,7 +1322,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, } else { - rel_appinfos = get_append_rel_partitions(root, rel, rte); + rel_appinfos = get_append_rel_partitions(root, rel, rte, false); rel->live_partitioned_rels = list_make1_int(rti); } @@ -1834,7 +1884,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)); /* @@ -1861,7 +1911,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); } @@ -1915,7 +1965,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)); } } @@ -2152,7 +2202,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 7356683..af7e790 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 9c74e39..9014ef2 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1001,6 +1001,47 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) return plan; } +static PartScanKeyInfo * +replace_partition_nestloop_params(PlannerInfo *root, PartScanKeyInfo *old_keys) +{ + PartScanKeyInfo *keys; + ListCell *lc; + + if (old_keys == NULL) + return NULL; + + keys = makeNode(PartScanKeyInfo); + foreach(lc, old_keys->eqkeys) + { + Node *n = lfirst(lc); + + keys->eqkeys = lappend(keys->eqkeys, replace_nestloop_params(root, n)); + } + keys->n_eqkeys = old_keys->n_eqkeys; + + foreach(lc, old_keys->minkeys) + { + Node *n = lfirst(lc); + + keys->minkeys = lappend(keys->minkeys, replace_nestloop_params(root, n)); + } + keys->n_minkeys = old_keys->n_minkeys; + keys->min_incl = old_keys->min_incl; + + foreach(lc, old_keys->maxkeys) + { + Node *n = lfirst(lc); + + keys->maxkeys = lappend(keys->maxkeys, replace_nestloop_params(root, n)); + } + keys->n_maxkeys = old_keys->n_maxkeys; + keys->max_incl = old_keys->max_incl; + + memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness)); + return keys; +} + + /* * create_append_plan * Create an Append plan for 'best_path' and (recursively) plans @@ -1063,6 +1104,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) copy_generic_path_info(&plan->plan, (Path *) best_path); + + plan->ex_keys = replace_partition_nestloop_params(root, best_path->ex_keys); + plan->et_keys = best_path->et_keys; + 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 fd0e483..8a8876f 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3675,7 +3675,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 30cdd3d..5479a59f 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -5197,3 +5197,19 @@ tlist_matches_coltypelist(List *tlist, List *coltypelist) return true; } + +Node * +eval_const_expressions_from_list(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); +} diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 36ec025..8910d39 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,40 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer, Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer)); } + if (root && (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 = relation_open(poid, NoLock); + + if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + ParamPathInfo *ppi = pathnode->path.param_info; + + /* Set the ex_keys for Exec Params */ + if (ppi) + { + List *ppi_clauses = ppi->ppi_clauses; + + pathnode->ex_keys = + get_append_partition_info(rel, rte, true, ppi_clauses); + } + /* Set et_keys for extern params */ + if (query_quals) + { + pathnode->et_keys = + get_append_partition_info(rel, rte, true, query_quals); + } + 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 b06696b..9abc79c 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -1556,6 +1556,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)) @@ -1567,11 +1570,29 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) if ((ppi = find_param_path_info(appendrel, required_outer))) return ppi; + + /* + * Generally for appendrel we don't fetch the clause from the join clause + * (only we do so for baserel), but for identifying whether the appendrel + * is applicable for runtime pruning or not. + */ + 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 7956f04..4331695 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -114,7 +114,7 @@ extern Bitmapset *get_partitions_from_clauses(Relation relation, List *partclauses); extern int classify_partition_bounding_keys(Relation relation, List *clauses, PartScanKeyInfo *keys, bool *constfalse, - List **or_clauses); + List **or_clauses, bool is_runtime); Bitmapset *get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e05bc04..adbac23 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1006,6 +1006,10 @@ typedef struct AppendState PlanState **appendplans; /* array of PlanStates for my inputs */ int as_nplans; int as_whichplan; + Oid parentoid; + List *index; /* subplan indexes to scan for runtime pruning */ + int as_whichpartition; /* current partition scanned from list */ + PartScanKeyInfo *ex_keys; } AppendState; /* ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index ffeeb49..5e09258 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_PartScanKeyInfo, /* * TAGS FOR PLAN STATE NODES (execnodes.h) diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index dd74efa..96f0632 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,9 @@ typedef struct Append /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *appendplans; + Oid parentoid; + PartScanKeyInfo *ex_keys; + PartScanKeyInfo *et_keys; } Append; /* ---------------- diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 3e47de7..78c7654 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -370,43 +370,48 @@ typedef struct PartitionSchemeData *PartitionScheme; */ typedef struct PartScanKeyInfo { + NodeTag type; + /* - * Constants constituting the *whole* partition key compared using - * partitioning-compatible equality operator(s). When n_eqkeys > 0, other - * keys (minkeys and maxkeys) are irrelevant. + * Expressions or constants constituting the *whole* partition key + * compared using partitioning-compatible equality operator(s). When + * n_eqkeys > 0, other keys (minkeys and maxkeys) are irrelevant. */ - Datum eqkeys[PARTITION_MAX_KEYS]; - int n_eqkeys; + List *eqkeys; + Datum eqkeys_datums[PARTITION_MAX_KEYS]; + int n_eqkeys; /* - * Constants that constitute the lower bound on the partition key or a - * prefix thereof. The last of those constants is compared using > or >= - * operator compatible with partitioning, making this the lower bound in - * a range query. + * Expressions or constants that constitute the lower bound on the + * partition key or a prefix thereof. The last of those constants is + * compared using > or >= operator compatible with partitioning, making + * this the lower bound in a range query. */ - Datum minkeys[PARTITION_MAX_KEYS]; - int n_minkeys; - bool min_incl; + List *minkeys; + Datum minkeys_datums[PARTITION_MAX_KEYS]; + int n_minkeys; + bool min_incl; /* - * Constants that constitute the upper bound on the partition key or a - * prefix thereof. The last of those constants is compared using < or <= - * operator compatible with partitioning, making this the upper bound in - * a range query. + * Expressions or constants that constitute the upper bound on the + * partition key or a prefix thereof. The last of those constants is + * compared using < or <= operator compatible with partitioning, making + * this the upper bound in a range query. */ - Datum maxkeys[PARTITION_MAX_KEYS]; - int n_maxkeys; - bool max_incl; + List *maxkeys; + Datum maxkeys_datums[PARTITION_MAX_KEYS]; + int n_maxkeys; + bool max_incl; /* * Specifies the type of NullTest that was applied to each of the * partition key columns or -1 if none was applied. Partitioning handles * null partition keys specially depending on the partitioning method in - * use, so get_partitions_for_keys can return partitions according to - * the nullness condition for partition keys. + * use, so get_partitions_for_keys can return partitions according to the + * nullness condition for partition keys. */ - NullTestType keynullness[PARTITION_MAX_KEYS]; -} PartScanKeyInfo; + NullTestType keynullness[PARTITION_MAX_KEYS]; +} PartScanKeyInfo; /*---------- * RelOptInfo @@ -1334,6 +1339,9 @@ typedef struct AppendPath /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; List *subpaths; /* list of component Paths */ + PartScanKeyInfo *ex_keys; /* Runtime Partition Pruning - EXEC Params */ + PartScanKeyInfo *et_keys; /* Runtime Partition Pruning - EXTERN Params */ + Oid parentoid; } AppendPath; #define IS_DUMMY_PATH(p) \ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index e367221..c7f5262 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 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 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, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index ea886b6..525c9df 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -234,4 +234,9 @@ extern PathKey *make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first); +extern PartScanKeyInfo *get_append_partition_info(RelOptInfo *rel, + RangeTblEntry *rte, + bool is_runtime, + List *clauses); + #endif /* PATHS_H */ -- 1.8.3.1