summaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/allpaths.c51
-rw-r--r--src/backend/optimizer/path/costsize.c22
-rw-r--r--src/backend/optimizer/path/pathkeys.c51
-rw-r--r--src/backend/optimizer/plan/createplan.c35
-rw-r--r--src/backend/optimizer/plan/initsplan.c2
-rw-r--r--src/backend/optimizer/plan/planner.c4
-rw-r--r--src/backend/optimizer/plan/setrefs.c9
-rw-r--r--src/backend/optimizer/plan/subselect.c34
-rw-r--r--src/backend/optimizer/prep/prepjointree.c9
-rw-r--r--src/backend/optimizer/util/clauses.c16
-rw-r--r--src/backend/optimizer/util/pathnode.c4
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);