aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/geqo/geqo_eval.c11
-rw-r--r--src/backend/optimizer/path/allpaths.c84
-rw-r--r--src/backend/optimizer/path/costsize.c20
-rw-r--r--src/backend/optimizer/path/joinpath.c30
-rw-r--r--src/backend/optimizer/plan/createplan.c111
-rw-r--r--src/backend/optimizer/plan/initsplan.c62
-rw-r--r--src/backend/optimizer/plan/planner.c3
-rw-r--r--src/backend/optimizer/plan/subselect.c1
-rw-r--r--src/backend/optimizer/prep/prepjointree.c7
-rw-r--r--src/backend/optimizer/util/pathnode.c26
-rw-r--r--src/backend/optimizer/util/var.c165
11 files changed, 411 insertions, 109 deletions
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 1e352fd3b5e..57eb2c39a4f 100644
--- a/src/backend/optimizer/geqo/geqo_eval.c
+++ b/src/backend/optimizer/geqo/geqo_eval.c
@@ -56,6 +56,7 @@ geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
MemoryContext mycontext;
MemoryContext oldcxt;
RelOptInfo *joinrel;
+ Path *best_path;
Cost fitness;
int savelength;
struct HTAB *savehash;
@@ -99,6 +100,14 @@ geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
/* construct the best path for the given combination of relations */
joinrel = gimme_tree(root, tour, num_gene);
+ best_path = joinrel->cheapest_total_path;
+
+ /*
+ * If no unparameterized path, use the cheapest parameterized path for
+ * costing purposes. XXX revisit this after LATERAL dust settles
+ */
+ if (!best_path)
+ best_path = linitial(joinrel->cheapest_parameterized_paths);
/*
* compute fitness
@@ -106,7 +115,7 @@ geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
* XXX geqo does not currently support optimization for partial result
* retrieval --- how to fix?
*/
- fitness = joinrel->cheapest_total_path->total_cost;
+ fitness = best_path->total_cost;
/*
* Restore join_rel_list to its former state, and put back original
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index f02954982a7..bfda05394d6 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -253,8 +253,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
case RTE_SUBQUERY:
/*
- * Subqueries don't support parameterized paths, so just go
- * ahead and build their paths immediately.
+ * Subqueries don't support making a choice between
+ * parameterized and unparameterized paths, so just go ahead
+ * and build their paths immediately.
*/
set_subquery_pathlist(root, rel, rti, rte);
break;
@@ -698,6 +699,10 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
if (IS_DUMMY_REL(childrel))
continue;
+ /* XXX need to figure out what to do for LATERAL */
+ if (childrel->cheapest_total_path == NULL)
+ elog(ERROR, "LATERAL within an append relation is not supported yet");
+
/*
* Child is live, so add its cheapest access path to the Append path
* we are constructing for the parent.
@@ -906,6 +911,10 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
*/
if (cheapest_startup == NULL || cheapest_total == NULL)
{
+ /* XXX need to figure out what to do for LATERAL */
+ if (childrel->cheapest_total_path == NULL)
+ elog(ERROR, "LATERAL within an append relation is not supported yet");
+
cheapest_startup = cheapest_total =
childrel->cheapest_total_path;
Assert(cheapest_total != NULL);
@@ -1012,8 +1021,13 @@ has_multiple_baserels(PlannerInfo *root)
* set_subquery_pathlist
* Build the (single) access path for a subquery RTE
*
- * There's no need for a separate set_subquery_size phase, since we don't
- * support parameterized paths for subqueries.
+ * We don't currently support generating parameterized paths for subqueries
+ * by pushing join clauses down into them; it seems too expensive to re-plan
+ * the subquery multiple times to consider different alternatives. So the
+ * subquery will have exactly one path. (The path will be parameterized
+ * if the subquery contains LATERAL references, otherwise not.) Since there's
+ * no freedom of action here, there's no need for a separate set_subquery_size
+ * phase: we just make the path right away.
*/
static void
set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -1021,6 +1035,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
{
Query *parse = root->parse;
Query *subquery = rte->subquery;
+ Relids required_outer;
bool *differentTypes;
double tuple_fraction;
PlannerInfo *subroot;
@@ -1033,6 +1048,20 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
*/
subquery = copyObject(subquery);
+ /*
+ * If it's a LATERAL subquery, it might contain some Vars of the current
+ * query level, requiring it to be treated as parameterized.
+ */
+ if (rte->lateral)
+ {
+ required_outer = pull_varnos_of_level((Node *) subquery, 1);
+ /* Enforce convention that empty required_outer is exactly NULL */
+ if (bms_is_empty(required_outer))
+ required_outer = NULL;
+ }
+ else
+ required_outer = NULL;
+
/* We need a workspace for keeping track of set-op type coercions */
differentTypes = (bool *)
palloc0((list_length(subquery->targetList) + 1) * sizeof(bool));
@@ -1051,10 +1080,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
* pseudoconstant clauses; better to have the gating node above the
* subquery.
*
- * Also, if the sub-query has "security_barrier" flag, it means the
+ * Also, if the sub-query has the "security_barrier" flag, it means the
* sub-query originated from a view that must enforce row-level security.
- * We must not push down quals in order to avoid information leaks, either
- * via side-effects or error output.
+ * Then we must not push down quals that contain leaky functions.
*
* Non-pushed-down clauses will get evaluated as qpquals of the
* SubqueryScan node.
@@ -1134,7 +1162,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
/* Generate appropriate path */
- add_path(rel, create_subqueryscan_path(root, rel, pathkeys, NULL));
+ add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
@@ -1143,12 +1171,32 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
/*
* set_function_pathlist
* Build the (single) access path for a function RTE
+ *
+ * As with subqueries, a function RTE's path might be parameterized due to
+ * LATERAL references, but that's inherent in the function expression and
+ * not a result of pushing down join quals.
*/
static void
set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
+ Relids required_outer;
+
+ /*
+ * If it's a LATERAL function, it might contain some Vars of the current
+ * query level, requiring it to be treated as parameterized.
+ */
+ if (rte->lateral)
+ {
+ required_outer = pull_varnos_of_level(rte->funcexpr, 0);
+ /* Enforce convention that empty required_outer is exactly NULL */
+ if (bms_is_empty(required_outer))
+ required_outer = NULL;
+ }
+ else
+ required_outer = NULL;
+
/* Generate appropriate path */
- add_path(rel, create_functionscan_path(root, rel));
+ add_path(rel, create_functionscan_path(root, rel, required_outer));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
@@ -1157,6 +1205,10 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/*
* set_values_pathlist
* Build the (single) access path for a VALUES RTE
+ *
+ * There can be no need for a parameterized path here. (Although the SQL
+ * spec does allow LATERAL (VALUES (x)), the parser will transform that
+ * into a subquery, so it doesn't end up here.)
*/
static void
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
@@ -1988,10 +2040,16 @@ debug_print_rel(PlannerInfo *root, RelOptInfo *rel)
printf("\tpath list:\n");
foreach(l, rel->pathlist)
print_path(root, lfirst(l), 1);
- printf("\n\tcheapest startup path:\n");
- print_path(root, rel->cheapest_startup_path, 1);
- printf("\n\tcheapest total path:\n");
- print_path(root, rel->cheapest_total_path, 1);
+ if (rel->cheapest_startup_path)
+ {
+ printf("\n\tcheapest startup path:\n");
+ print_path(root, rel->cheapest_startup_path, 1);
+ }
+ if (rel->cheapest_total_path)
+ {
+ printf("\n\tcheapest total path:\n");
+ print_path(root, rel->cheapest_total_path, 1);
+ }
printf("\n");
fflush(stdout);
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 875c611ab50..d3f04eea4b1 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -989,12 +989,17 @@ cost_subqueryscan(Path *path, PlannerInfo *root,
/*
* cost_functionscan
* Determines and returns the cost of scanning a function RTE.
+ *
+ * 'baserel' is the relation to be scanned
+ * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
*/
void
-cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
+cost_functionscan(Path *path, PlannerInfo *root,
+ RelOptInfo *baserel, ParamPathInfo *param_info)
{
Cost startup_cost = 0;
Cost run_cost = 0;
+ QualCost qpqual_cost;
Cost cpu_per_tuple;
RangeTblEntry *rte;
QualCost exprcost;
@@ -1004,8 +1009,11 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
rte = planner_rt_fetch(baserel->relid, root);
Assert(rte->rtekind == RTE_FUNCTION);
- /* functionscans are never parameterized */
- path->rows = baserel->rows;
+ /* Mark the path with the correct row estimate */
+ if (param_info)
+ path->rows = param_info->ppi_rows;
+ else
+ path->rows = baserel->rows;
/*
* Estimate costs of executing the function expression.
@@ -1025,8 +1033,10 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
startup_cost += exprcost.startup + exprcost.per_tuple;
/* Add scanning CPU costs */
- startup_cost += baserel->baserestrictcost.startup;
- cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
+ get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
+
+ startup_cost += qpqual_cost.startup;
+ cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
path->startup_cost = startup_cost;
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 65f86194e15..fe0e4d7c201 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -491,12 +491,18 @@ sort_inner_and_outer(PlannerInfo *root,
* explosion of mergejoin paths of dubious value. This interacts with
* decisions elsewhere that also discriminate against mergejoins with
* parameterized inputs; see comments in src/backend/optimizer/README.
- *
- * If unique-ification is requested, do it and then handle as a plain
- * inner join.
*/
outer_path = outerrel->cheapest_total_path;
inner_path = innerrel->cheapest_total_path;
+
+ /* Punt if either rel has only parameterized paths */
+ if (!outer_path || !inner_path)
+ return;
+
+ /*
+ * If unique-ification is requested, do it and then handle as a plain
+ * inner join.
+ */
if (jointype == JOIN_UNIQUE_OUTER)
{
outer_path = (Path *) create_unique_path(root, outerrel,
@@ -696,6 +702,10 @@ match_unsorted_outer(PlannerInfo *root,
*/
if (save_jointype == JOIN_UNIQUE_INNER)
{
+ /* XXX for the moment, don't crash on LATERAL --- rethink this */
+ if (inner_cheapest_total == NULL)
+ return;
+
inner_cheapest_total = (Path *)
create_unique_path(root, innerrel, inner_cheapest_total, sjinfo);
Assert(inner_cheapest_total);
@@ -707,7 +717,7 @@ match_unsorted_outer(PlannerInfo *root,
* enable_material is off or the path in question materializes its
* output anyway.
*/
- if (enable_material &&
+ if (enable_material && inner_cheapest_total != NULL &&
!ExecMaterializesOutput(inner_cheapest_total->pathtype))
matpath = (Path *)
create_material_path(innerrel, inner_cheapest_total);
@@ -735,6 +745,8 @@ match_unsorted_outer(PlannerInfo *root,
* If we need to unique-ify the outer path, it's pointless to consider
* any but the cheapest outer. (XXX we don't consider parameterized
* outers, nor inners, for unique-ified cases. Should we?)
+ *
+ * XXX does nothing for LATERAL, rethink
*/
if (save_jointype == JOIN_UNIQUE_OUTER)
{
@@ -814,6 +826,10 @@ match_unsorted_outer(PlannerInfo *root,
if (save_jointype == JOIN_UNIQUE_OUTER)
continue;
+ /* Can't do anything else if inner has no unparameterized paths */
+ if (!inner_cheapest_total)
+ continue;
+
/* Look for useful mergeclauses (if any) */
mergeclauses = find_mergeclauses_for_pathkeys(root,
outerpath->pathkeys,
@@ -1092,6 +1108,12 @@ hash_inner_and_outer(PlannerInfo *root,
Path *cheapest_total_outer = outerrel->cheapest_total_path;
Path *cheapest_total_inner = innerrel->cheapest_total_path;
+ /* Punt if either rel has only parameterized paths */
+ if (!cheapest_startup_outer ||
+ !cheapest_total_outer ||
+ !cheapest_total_inner)
+ return;
+
/* Unique-ify if need be; we ignore parameterized possibilities */
if (jointype == JOIN_UNIQUE_OUTER)
{
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 414406bb8a1..6bb821fb385 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -84,6 +84,7 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
+static void identify_nestloop_extparams(PlannerInfo *root, Plan *subplan);
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
@@ -1640,6 +1641,7 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
{
scan_clauses = (List *)
replace_nestloop_params(root, (Node *) scan_clauses);
+ identify_nestloop_extparams(root, best_path->parent->subplan);
}
scan_plan = make_subqueryscan(tlist,
@@ -1664,11 +1666,13 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
FunctionScan *scan_plan;
Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte;
+ Node *funcexpr;
/* 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;
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
@@ -1676,8 +1680,17 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
+ /* Replace any outer-relation variables with nestloop params */
+ if (best_path->param_info)
+ {
+ 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);
+ }
+
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
- rte->funcexpr,
+ funcexpr,
rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods,
@@ -2560,6 +2573,102 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
}
/*
+ * identify_nestloop_extparams
+ * Identify extParams of a parameterized subquery that need to be fed
+ * from an outer nestloop.
+ *
+ * The subplan's references to the outer variables are already represented
+ * as PARAM_EXEC Params, so we need not modify the subplan here. What we
+ * do need to do is add entries to root->curOuterParams to signal the parent
+ * nestloop plan node that it must provide these values.
+ */
+static void
+identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
+{
+ Bitmapset *tmpset;
+ int paramid;
+
+ /* Examine each extParam of the subquery's plan */
+ tmpset = bms_copy(subplan->extParam);
+ while ((paramid = bms_first_member(tmpset)) >= 0)
+ {
+ PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
+
+ /* Ignore anything coming from an upper query level */
+ if (pitem->abslevel != root->query_level)
+ continue;
+
+ if (IsA(pitem->item, Var))
+ {
+ Var *var = (Var *) pitem->item;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* If not from a nestloop outer rel, nothing to do */
+ if (!bms_is_member(var->varno, root->curOuterRels))
+ continue;
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == paramid)
+ {
+ Assert(equal(var, nlp->paramval));
+ /* Present, so nothing to do */
+ break;
+ }
+ }
+ if (lc == NULL)
+ {
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = paramid;
+ nlp->paramval = copyObject(var);
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ }
+ }
+ else if (IsA(pitem->item, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /*
+ * If not from a nestloop outer rel, nothing to do. We use
+ * bms_overlap as a cheap/quick test to see if the PHV might be
+ * evaluated in the outer rels, and then grab its PlaceHolderInfo
+ * to tell for sure.
+ */
+ if (!bms_overlap(phv->phrels, root->curOuterRels))
+ continue;
+ if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
+ root->curOuterRels))
+ continue;
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == paramid)
+ {
+ Assert(equal(phv, nlp->paramval));
+ /* Present, so nothing to do */
+ break;
+ }
+ }
+ if (lc == NULL)
+ {
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = paramid;
+ nlp->paramval = copyObject(phv);
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ }
+ }
+ }
+ bms_free(tmpset);
+}
+
+/*
* fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual
* machinery needs.
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 3c7fa632b8e..4481db5c341 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -204,6 +204,64 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars,
}
}
+/*
+ * extract_lateral_references
+ * If the specified RTE is a LATERAL subquery, extract all its references
+ * to Vars of the current query level, and make sure those Vars will be
+ * available for evaluation of the RTE.
+ *
+ * XXX this is rather duplicative of processing that has to happen elsewhere.
+ * Maybe it'd be a good idea to do this type of extraction further upstream
+ * and save the results?
+ */
+static void
+extract_lateral_references(PlannerInfo *root, int rtindex)
+{
+ RangeTblEntry *rte = root->simple_rte_array[rtindex];
+ List *vars;
+ List *newvars;
+ Relids where_needed;
+ ListCell *lc;
+
+ /* No cross-references are possible if it's not LATERAL */
+ if (!rte->lateral)
+ return;
+
+ /* Fetch the appropriate variables */
+ 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);
+ else
+ return;
+
+ /* Copy each Var and adjust it to match our level */
+ newvars = NIL;
+ foreach(lc, vars)
+ {
+ Var *var = (Var *) lfirst(lc);
+
+ var = copyObject(var);
+ var->varlevelsup = 0;
+ newvars = lappend(newvars, var);
+ }
+
+ /*
+ * We mark the Vars as being "needed" at the LATERAL RTE. This is a bit
+ * of a cheat: a more formal approach would be to mark each one as needed
+ * at the join of the LATERAL RTE with its source RTE. But it will work,
+ * and it's much less tedious than computing a separate where_needed for
+ * each Var.
+ */
+ where_needed = bms_make_singleton(rtindex);
+
+ /* Push the Vars into their source relations' targetlists */
+ add_vars_to_targetlist(root, newvars, where_needed, false);
+
+ list_free(newvars);
+ list_free(vars);
+}
+
/*****************************************************************************
*
@@ -286,7 +344,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- /* No quals to deal with, just return correct result */
+ /* No quals to deal with, but do check for LATERAL subqueries */
+ extract_lateral_references(root, varno);
+ /* Result qualscope is just the one Relid */
*qualscope = bms_make_singleton(varno);
/* A single baserel does not create an inner join */
*inner_join_rels = NULL;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 31fe5570723..26b5dbb559d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1817,7 +1817,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
*
* Once grouping_planner() has applied a general tlist to the topmost
* scan/join plan node, any tlist eval cost for added-on nodes should be
- * accounted for as we create those nodes. Presently, of the node types we
+ * accounted for as we create those nodes. Presently, of the node types we
* can add on later, only Agg, WindowAgg, and Group project new tlists (the
* rest just copy their input tuples) --- so make_agg(), make_windowagg() and
* make_group() are responsible for calling this function to account for their
@@ -3257,6 +3257,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
rte->rtekind = RTE_RELATION;
rte->relid = tableOid;
rte->relkind = RELKIND_RELATION;
+ rte->lateral = false;
rte->inh = false;
rte->inFromCl = true;
query->rtable = list_make1(rte);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 8ce6bee8561..863c943f2a0 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1231,6 +1231,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
rte = addRangeTableEntryForSubquery(NULL,
subselect,
makeAlias("ANY_subquery", NIL),
+ false,
false);
parse->rtable = lappend(parse->rtable, rte);
rtindex = list_length(parse->rtable);
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index be1219eb3d1..06dbe845404 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1176,6 +1176,13 @@ is_simple_subquery(Query *subquery)
return false;
/*
+ * Don't pull up a LATERAL subquery (hopefully, this is just a temporary
+ * implementation restriction).
+ */
+ if (contain_vars_of_level((Node *) subquery, 1))
+ return false;
+
+ /*
* Don't pull up a subquery that has any set-returning functions in its
* targetlist. Otherwise we might well wind up inserting set-returning
* functions into places where they mustn't go, such as quals of higher
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 00052f5c846..11de5c70d80 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -193,7 +193,7 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
* and cheapest_total. The cheapest_parameterized_paths list collects paths
* that are cheapest-total for their parameterization (i.e., there is no
* cheaper path with the same or weaker parameterization). This list always
- * includes the unparameterized cheapest-total path, too.
+ * includes the unparameterized cheapest-total path, too, if there is one.
*
* This is normally called only after we've finished constructing the path
* list for the rel node.
@@ -250,15 +250,18 @@ set_cheapest(RelOptInfo *parent_rel)
cheapest_total_path = path;
}
- if (cheapest_total_path == NULL)
+ if (cheapest_total_path == NULL && !have_parameterized_paths)
elog(ERROR, "could not devise a query plan for the given query");
parent_rel->cheapest_startup_path = cheapest_startup_path;
parent_rel->cheapest_total_path = cheapest_total_path;
parent_rel->cheapest_unique_path = NULL; /* computed only if needed */
- /* Seed the parameterized-paths list with the cheapest total */
- parent_rel->cheapest_parameterized_paths = list_make1(cheapest_total_path);
+ /* Seed the parameterized-paths list with the cheapest total, if any */
+ if (cheapest_total_path)
+ parent_rel->cheapest_parameterized_paths = list_make1(cheapest_total_path);
+ else
+ parent_rel->cheapest_parameterized_paths = NIL;
/* And, if there are any parameterized paths, add them in one at a time */
if (have_parameterized_paths)
@@ -1131,6 +1134,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
int numCols;
ListCell *lc;
+ /* XXX temporary band-aid to not crash on LATERAL queries */
+ if (subpath == NULL)
+ {
+ Assert(subpath == rel->cheapest_total_path);
+ return NULL;
+ }
+
/* Caller made a mistake if subpath isn't cheapest_total ... */
Assert(subpath == rel->cheapest_total_path);
Assert(subpath->parent == rel);
@@ -1657,16 +1667,18 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel,
* returning the pathnode.
*/
Path *
-create_functionscan_path(PlannerInfo *root, RelOptInfo *rel)
+create_functionscan_path(PlannerInfo *root, RelOptInfo *rel,
+ Relids required_outer)
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_FunctionScan;
pathnode->parent = rel;
- pathnode->param_info = NULL; /* never parameterized at present */
+ pathnode->param_info = get_baserel_parampathinfo(root, rel,
+ required_outer);
pathnode->pathkeys = NIL; /* for now, assume unordered result */
- cost_functionscan(pathnode, root, rel);
+ cost_functionscan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 9bc90c25313..81332ff1cd1 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -42,16 +42,15 @@ typedef struct
typedef struct
{
- int var_location;
+ List *vars;
int sublevels_up;
-} locate_var_of_level_context;
+} pull_vars_context;
typedef struct
{
int var_location;
- int relid;
int sublevels_up;
-} locate_var_of_relation_context;
+} locate_var_of_level_context;
typedef struct
{
@@ -77,12 +76,11 @@ typedef struct
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
static bool pull_varattnos_walker(Node *node, pull_varattnos_context *context);
+static bool pull_vars_walker(Node *node, pull_vars_context *context);
static bool contain_var_clause_walker(Node *node, void *context);
static bool contain_vars_of_level_walker(Node *node, int *sublevels_up);
static bool locate_var_of_level_walker(Node *node,
locate_var_of_level_context *context);
-static bool locate_var_of_relation_walker(Node *node,
- locate_var_of_relation_context *context);
static bool find_minimum_var_level_walker(Node *node,
find_minimum_var_level_context *context);
static bool pull_var_clause_walker(Node *node,
@@ -122,6 +120,31 @@ pull_varnos(Node *node)
return context.varnos;
}
+/*
+ * pull_varnos_of_level
+ * Create a set of all the distinct varnos present in a parsetree.
+ * Only Vars of the specified level are considered.
+ */
+Relids
+pull_varnos_of_level(Node *node, int levelsup)
+{
+ pull_varnos_context context;
+
+ context.varnos = NULL;
+ context.sublevels_up = levelsup;
+
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, we don't want to increment sublevels_up.
+ */
+ query_or_expression_tree_walker(node,
+ pull_varnos_walker,
+ (void *) &context,
+ 0);
+
+ return context.varnos;
+}
+
static bool
pull_varnos_walker(Node *node, pull_varnos_context *context)
{
@@ -231,6 +254,66 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
/*
+ * pull_vars_of_level
+ * Create a list of all Vars referencing the specified query level
+ * in the given parsetree.
+ *
+ * This is used on unplanned parsetrees, so we don't expect to see any
+ * PlaceHolderVars.
+ *
+ * Caution: the Vars are not copied, only linked into the list.
+ */
+List *
+pull_vars_of_level(Node *node, int levelsup)
+{
+ pull_vars_context context;
+
+ context.vars = NIL;
+ context.sublevels_up = levelsup;
+
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, we don't want to increment sublevels_up.
+ */
+ query_or_expression_tree_walker(node,
+ pull_vars_walker,
+ (void *) &context,
+ 0);
+
+ return context.vars;
+}
+
+static bool
+pull_vars_walker(Node *node, pull_vars_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup == context->sublevels_up)
+ context->vars = lappend(context->vars, var);
+ return false;
+ }
+ Assert(!IsA(node, PlaceHolderVar));
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, pull_vars_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, pull_vars_walker,
+ (void *) context);
+}
+
+
+/*
* contain_var_clause
* Recursively scan a clause to discover whether it contains any Var nodes
* (of the current query level).
@@ -406,76 +489,6 @@ locate_var_of_level_walker(Node *node,
/*
- * locate_var_of_relation
- * Find the parse location of any Var of the specified relation.
- *
- * Returns -1 if no such Var is in the querytree, or if they all have
- * unknown parse location.
- *
- * Will recurse into sublinks. Also, may be invoked directly on a Query.
- */
-int
-locate_var_of_relation(Node *node, int relid, int levelsup)
-{
- locate_var_of_relation_context context;
-
- context.var_location = -1; /* in case we find nothing */
- context.relid = relid;
- context.sublevels_up = levelsup;
-
- (void) query_or_expression_tree_walker(node,
- locate_var_of_relation_walker,
- (void *) &context,
- 0);
-
- return context.var_location;
-}
-
-static bool
-locate_var_of_relation_walker(Node *node,
- locate_var_of_relation_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varno == context->relid &&
- var->varlevelsup == context->sublevels_up &&
- var->location >= 0)
- {
- context->var_location = var->location;
- return true; /* abort tree traversal and return true */
- }
- return false;
- }
- if (IsA(node, CurrentOfExpr))
- {
- /* since CurrentOfExpr doesn't carry location, nothing we can do */
- return false;
- }
- /* No extra code needed for PlaceHolderVar; just look in contained expr */
- if (IsA(node, Query))
- {
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- locate_var_of_relation_walker,
- (void *) context,
- 0);
- context->sublevels_up--;
- return result;
- }
- return expression_tree_walker(node,
- locate_var_of_relation_walker,
- (void *) context);
-}
-
-
-/*
* find_minimum_var_level
* Recursively scan a clause to find the lowest variable level it
* contains --- for example, zero is returned if there are any local