diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 51 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 22 | ||||
-rw-r--r-- | src/backend/optimizer/path/pathkeys.c | 51 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 35 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 34 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 16 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 4 |
11 files changed, 181 insertions, 56 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index bfd3809a007..96fe50f0b27 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -18,6 +18,7 @@ #include <math.h> #include "catalog/pg_class.h" +#include "catalog/pg_operator.h" #include "foreign/fdwapi.h" #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG @@ -1258,6 +1259,7 @@ static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relids required_outer; + List *pathkeys = NIL; /* * We don't support pushing join clauses into the quals of a function @@ -1266,8 +1268,55 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) */ required_outer = rel->lateral_relids; + /* + * The result is considered unordered unless ORDINALITY was used, in which + * case it is ordered by the ordinal column (the last one). See if we + * care, by checking for uses of that Var in equivalence classes. + */ + if (rte->funcordinality) + { + AttrNumber ordattno = rel->max_attr; + Var *var = NULL; + ListCell *lc; + + /* + * Is there a Var for it in reltargetlist? If not, the query did not + * reference the ordinality column, or at least not in any way that + * would be interesting for sorting. + */ + foreach(lc, rel->reltargetlist) + { + Var *node = (Var *) lfirst(lc); + + /* checking varno/varlevelsup is just paranoia */ + if (IsA(node, Var) && + node->varattno == ordattno && + node->varno == rel->relid && + node->varlevelsup == 0) + { + var = node; + break; + } + } + + /* + * Try to build pathkeys for this Var with int8 sorting. We tell + * build_expression_pathkey not to build any new equivalence class; if + * the Var isn't already mentioned in some EC, it means that nothing + * cares about the ordering. + */ + if (var) + pathkeys = build_expression_pathkey(root, + (Expr *) var, + NULL, /* below outer joins */ + Int8LessOperator, + rel->relids, + false); + } + /* Generate appropriate path */ - add_path(rel, create_functionscan_path(root, rel, required_outer)); + add_path(rel, create_functionscan_path(root, rel, + pathkeys, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index e7f8cec0fed..50f08521bfe 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1076,9 +1076,9 @@ cost_functionscan(Path *path, PlannerInfo *root, path->rows = baserel->rows; /* - * Estimate costs of executing the function expression. + * Estimate costs of executing the function expression(s). * - * Currently, nodeFunctionscan.c always executes the function to + * Currently, nodeFunctionscan.c always executes the functions to * completion before returning any rows, and caches the results in a * tuplestore. So the function eval cost is all startup cost, and per-row * costs are minimal. @@ -1088,7 +1088,7 @@ cost_functionscan(Path *path, PlannerInfo *root, * estimates for functions tend to be, there's not a lot of point in that * refinement right now. */ - cost_qual_eval_node(&exprcost, rte->funcexpr, root); + cost_qual_eval_node(&exprcost, (Node *) rte->functions, root); startup_cost += exprcost.startup + exprcost.per_tuple; @@ -3845,14 +3845,26 @@ void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel) { RangeTblEntry *rte; + ListCell *lc; /* Should only be applied to base relations that are functions */ Assert(rel->relid > 0); rte = planner_rt_fetch(rel->relid, root); Assert(rte->rtekind == RTE_FUNCTION); - /* Estimate number of rows the function itself will return */ - rel->tuples = expression_returns_set_rows(rte->funcexpr); + /* + * Estimate number of rows the functions will return. The rowcount of the + * node is that of the largest function result. + */ + rel->tuples = 0; + foreach(lc, rte->functions) + { + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + double ntup = expression_returns_set_rows(rtfunc->funcexpr); + + if (ntup > rel->tuples) + rel->tuples = ntup; + } /* Now estimate number of output rows, etc */ set_baserel_size_estimates(root, rel); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 032b2cdc133..9c8ede658f4 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -502,6 +502,57 @@ build_index_pathkeys(PlannerInfo *root, } /* + * build_expression_pathkey + * Build a pathkeys list that describes an ordering by a single expression + * using the given sort operator. + * + * expr, nullable_relids, and rel are as for make_pathkey_from_sortinfo. + * We induce the other arguments assuming default sort order for the operator. + * + * Similarly to make_pathkey_from_sortinfo, the result is NIL if create_it + * is false and the expression isn't already in some EquivalenceClass. + */ +List * +build_expression_pathkey(PlannerInfo *root, + Expr *expr, + Relids nullable_relids, + Oid opno, + Relids rel, + bool create_it) +{ + List *pathkeys; + Oid opfamily, + opcintype; + int16 strategy; + PathKey *cpathkey; + + /* Find the operator in pg_amop --- failure shouldn't happen */ + if (!get_ordering_op_properties(opno, + &opfamily, &opcintype, &strategy)) + elog(ERROR, "operator %u is not a valid ordering operator", + opno); + + cpathkey = make_pathkey_from_sortinfo(root, + expr, + nullable_relids, + opfamily, + opcintype, + exprCollation((Node *) expr), + (strategy == BTGreaterStrategyNumber), + (strategy == BTGreaterStrategyNumber), + 0, + rel, + create_it); + + if (cpathkey) + pathkeys = list_make1(cpathkey); + else + pathkeys = NIL; + + return pathkeys; +} + +/* * convert_subquery_pathkeys * Build a pathkeys list that describes the ordering of a subquery's * result, in the terms of the outer query. This is essentially a diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 5947e5b136f..f2c122d2959 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -115,9 +115,7 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist, static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, List *tidquals); static FunctionScan *make_functionscan(List *qptlist, List *qpqual, - Index scanrelid, Node *funcexpr, bool ordinality, - List *funccolnames, List *funccoltypes, List *funccoltypmods, - List *funccolcollations); + Index scanrelid, List *functions, bool funcordinality); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, Index scanrelid, List *values_lists); static CteScan *make_ctescan(List *qptlist, List *qpqual, @@ -1709,13 +1707,13 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; RangeTblEntry *rte; - Node *funcexpr; + List *functions; /* it should be a function base rel... */ Assert(scan_relid > 0); rte = planner_rt_fetch(scan_relid, root); Assert(rte->rtekind == RTE_FUNCTION); - funcexpr = rte->funcexpr; + functions = rte->functions; /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); @@ -1728,17 +1726,12 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, { scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); - /* The func expression itself could contain nestloop params, too */ - funcexpr = replace_nestloop_params(root, funcexpr); + /* The function expressions could contain nestloop params, too */ + functions = (List *) replace_nestloop_params(root, (Node *) functions); } scan_plan = make_functionscan(tlist, scan_clauses, scan_relid, - funcexpr, - rte->funcordinality, - rte->eref->colnames, - rte->funccoltypes, - rte->funccoltypmods, - rte->funccolcollations); + functions, rte->funcordinality); copy_path_costsize(&scan_plan->scan.plan, best_path); @@ -3388,12 +3381,8 @@ static FunctionScan * make_functionscan(List *qptlist, List *qpqual, Index scanrelid, - Node *funcexpr, - bool ordinality, - List *funccolnames, - List *funccoltypes, - List *funccoltypmods, - List *funccolcollations) + List *functions, + bool funcordinality) { FunctionScan *node = makeNode(FunctionScan); Plan *plan = &node->scan.plan; @@ -3404,12 +3393,8 @@ make_functionscan(List *qptlist, plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; - node->funcexpr = funcexpr; - node->funcordinality = ordinality; - node->funccolnames = funccolnames; - node->funccoltypes = funccoltypes; - node->funccoltypmods = funccoltypmods; - node->funccolcollations = funccolcollations; + node->functions = functions; + node->funcordinality = funcordinality; return node; } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 04a399ee13c..59606643925 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -307,7 +307,7 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) if (rte->rtekind == RTE_SUBQUERY) vars = pull_vars_of_level((Node *) rte->subquery, 1); else if (rte->rtekind == RTE_FUNCTION) - vars = pull_vars_of_level(rte->funcexpr, 0); + vars = pull_vars_of_level((Node *) rte->functions, 0); else if (rte->rtekind == RTE_VALUES) vars = pull_vars_of_level((Node *) rte->values_lists, 0); else diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d8aa35dee79..66707944a04 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -485,9 +485,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse, } else if (rte->rtekind == RTE_FUNCTION) { - /* Preprocess the function expression fully */ + /* Preprocess the function expression(s) fully */ kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; - rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind); + rte->functions = (List *) preprocess_expression(root, (Node *) rte->functions, kind); } else if (rte->rtekind == RTE_VALUES) { diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index b78d72701b3..5c9f3d64ce7 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -381,10 +381,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) /* zap unneeded sub-structure */ newrte->subquery = NULL; newrte->joinaliasvars = NIL; - newrte->funcexpr = NULL; - newrte->funccoltypes = NIL; - newrte->funccoltypmods = NIL; - newrte->funccolcollations = NIL; + newrte->functions = NIL; newrte->values_lists = NIL; newrte->values_collations = NIL; newrte->ctecoltypes = NIL; @@ -525,8 +522,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) fix_scan_list(root, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = fix_scan_list(root, splan->scan.plan.qual, rtoffset); - splan->funcexpr = - fix_scan_expr(root, splan->funcexpr, rtoffset); + splan->functions = + fix_scan_list(root, splan->functions, rtoffset); } break; case T_ValuesScan: diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 0df70c4443b..d8cabbd5bfe 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2135,9 +2135,37 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_FunctionScan: - finalize_primnode(((FunctionScan *) plan)->funcexpr, - &context); - context.paramids = bms_add_members(context.paramids, scan_params); + { + FunctionScan *fscan = (FunctionScan *) plan; + ListCell *lc; + + /* + * Call finalize_primnode independently on each function + * expression, so that we can record which params are + * referenced in each, in order to decide which need + * re-evaluating during rescan. + */ + foreach(lc, fscan->functions) + { + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + finalize_primnode_context funccontext; + + funccontext = context; + funccontext.paramids = NULL; + + finalize_primnode(rtfunc->funcexpr, &funccontext); + + /* remember results for execution */ + rtfunc->funcparams = funccontext.paramids; + + /* add the function's params to the overall set */ + context.paramids = bms_add_members(context.paramids, + funccontext.paramids); + } + + context.paramids = bms_add_members(context.paramids, + scan_params); + } break; case T_ValuesScan: diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index c742cc9542b..485ac31bd37 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -580,10 +580,7 @@ inline_set_returning_functions(PlannerInfo *root) /* Successful expansion, replace the rtable entry */ rte->rtekind = RTE_SUBQUERY; rte->subquery = funcquery; - rte->funcexpr = NULL; - rte->funccoltypes = NIL; - rte->funccoltypmods = NIL; - rte->funccolcollations = NIL; + rte->functions = NIL; } } } @@ -1623,8 +1620,8 @@ replace_vars_in_jointree(Node *jtnode, context); break; case RTE_FUNCTION: - rte->funcexpr = - pullup_replace_vars(rte->funcexpr, + rte->functions = (List *) + pullup_replace_vars((Node *) rte->functions, context); break; case RTE_VALUES: diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 7ce8a9d8180..a7fdd52c294 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4509,6 +4509,7 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Query * inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) { + RangeTblFunction *rtfunc; FuncExpr *fexpr; Oid func_oid; HeapTuple func_tuple; @@ -4537,14 +4538,18 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) */ check_stack_depth(); - /* Fail if the caller wanted ORDINALITY - we don't implement that here. */ + /* Fail if the RTE has ORDINALITY - we don't implement that here. */ if (rte->funcordinality) return NULL; - /* Fail if FROM item isn't a simple FuncExpr */ - fexpr = (FuncExpr *) rte->funcexpr; - if (fexpr == NULL || !IsA(fexpr, FuncExpr)) + /* Fail if RTE isn't a single, simple FuncExpr */ + if (list_length(rte->functions) != 1) return NULL; + rtfunc = (RangeTblFunction *) linitial(rte->functions); + + if (!IsA(rtfunc->funcexpr, FuncExpr)) + return NULL; + fexpr = (FuncExpr *) rtfunc->funcexpr; func_oid = fexpr->funcid; @@ -4734,7 +4739,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) */ if (fexpr->funcresulttype == RECORDOID && get_func_result_type(func_oid, NULL, NULL) == TYPEFUNC_RECORD && - !tlist_matches_coltypelist(querytree->targetList, rte->funccoltypes)) + !tlist_matches_coltypelist(querytree->targetList, + rtfunc->funccoltypes)) goto fail; /* diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 64b17051913..a7169efd856 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1623,7 +1623,7 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, */ Path * create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, - Relids required_outer) + List *pathkeys, Relids required_outer) { Path *pathnode = makeNode(Path); @@ -1631,7 +1631,7 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, pathnode->parent = rel; pathnode->param_info = get_baserel_parampathinfo(root, rel, required_outer); - pathnode->pathkeys = NIL; /* for now, assume unordered result */ + pathnode->pathkeys = pathkeys; cost_functionscan(pathnode, root, rel, pathnode->param_info); |