Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c1774d2

Browse files
committed
More fixes for planner's handling of LATERAL.
Re-allow subquery pullup for LATERAL subqueries, except when the subquery is below an outer join and contains lateral references to relations outside that outer join. If we pull up in such a case, we risk introducing lateral cross-references into outer joins' ON quals, which is something the code is entirely unprepared to cope with right now; and I'm not sure it'll ever be worth coping with. Support lateral refs in VALUES (this seems to be the only additional path type that needs such support as a consequence of re-allowing subquery pullup). Put in a slightly hacky fix for joinpath.c's refusal to consider parameterized join paths even when there cannot be any unparameterized ones. This was causing "could not devise a query plan for the given query" failures in queries involving more than two FROM items. Put in an even more hacky fix for distribute_qual_to_rels() being unhappy with join quals that contain references to rels outside their syntactic scope; which is to say, disable that test altogether. Need to think about how to preserve some sort of debugging cross-check here, while not expending more cycles than befits a debugging cross-check.
1 parent e76af54 commit c1774d2

File tree

13 files changed

+390
-89
lines changed

13 files changed

+390
-89
lines changed

src/backend/optimizer/path/allpaths.c

+22-4
Original file line numberDiff line numberDiff line change
@@ -1210,15 +1210,33 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12101210
* set_values_pathlist
12111211
* Build the (single) access path for a VALUES RTE
12121212
*
1213-
* There can be no need for a parameterized path here. (Although the SQL
1214-
* spec does allow LATERAL (VALUES (x)), the parser will transform that
1215-
* into a subquery, so it doesn't end up here.)
1213+
* As with subqueries, a VALUES RTE's path might be parameterized due to
1214+
* LATERAL references, but that's inherent in the values expressions and
1215+
* not a result of pushing down join quals.
12161216
*/
12171217
static void
12181218
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12191219
{
1220+
Relids required_outer;
1221+
1222+
/*
1223+
* If it's a LATERAL RTE, it might contain some Vars of the current query
1224+
* level, requiring it to be treated as parameterized. (NB: even though
1225+
* the parser never marks VALUES RTEs as LATERAL, they could be so marked
1226+
* by now, as a result of subquery pullup.)
1227+
*/
1228+
if (rte->lateral)
1229+
{
1230+
required_outer = pull_varnos_of_level((Node *) rte->values_lists, 0);
1231+
/* Enforce convention that empty required_outer is exactly NULL */
1232+
if (bms_is_empty(required_outer))
1233+
required_outer = NULL;
1234+
}
1235+
else
1236+
required_outer = NULL;
1237+
12201238
/* Generate appropriate path */
1221-
add_path(rel, create_valuesscan_path(root, rel));
1239+
add_path(rel, create_valuesscan_path(root, rel, required_outer));
12221240

12231241
/* Select cheapest path (pretty easy in this case...) */
12241242
set_cheapest(rel);

src/backend/optimizer/path/costsize.c

+15-5
Original file line numberDiff line numberDiff line change
@@ -1046,20 +1046,28 @@ cost_functionscan(Path *path, PlannerInfo *root,
10461046
/*
10471047
* cost_valuesscan
10481048
* Determines and returns the cost of scanning a VALUES RTE.
1049+
*
1050+
* 'baserel' is the relation to be scanned
1051+
* 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
10491052
*/
10501053
void
1051-
cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
1054+
cost_valuesscan(Path *path, PlannerInfo *root,
1055+
RelOptInfo *baserel, ParamPathInfo *param_info)
10521056
{
10531057
Cost startup_cost = 0;
10541058
Cost run_cost = 0;
1059+
QualCost qpqual_cost;
10551060
Cost cpu_per_tuple;
10561061

10571062
/* Should only be applied to base relations that are values lists */
10581063
Assert(baserel->relid > 0);
10591064
Assert(baserel->rtekind == RTE_VALUES);
10601065

1061-
/* valuesscans are never parameterized */
1062-
path->rows = baserel->rows;
1066+
/* Mark the path with the correct row estimate */
1067+
if (param_info)
1068+
path->rows = param_info->ppi_rows;
1069+
else
1070+
path->rows = baserel->rows;
10631071

10641072
/*
10651073
* For now, estimate list evaluation cost at one operator eval per list
@@ -1068,8 +1076,10 @@ cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
10681076
cpu_per_tuple = cpu_operator_cost;
10691077

10701078
/* Add scanning CPU costs */
1071-
startup_cost += baserel->baserestrictcost.startup;
1072-
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
1079+
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
1080+
1081+
startup_cost += qpqual_cost.startup;
1082+
cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
10731083
run_cost += cpu_per_tuple * baserel->tuples;
10741084

10751085
path->startup_cost = startup_cost;

src/backend/optimizer/path/joinpath.c

+26
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,32 @@ add_paths_to_joinrel(PlannerInfo *root,
147147
sjinfo->min_lefthand));
148148
}
149149

150+
/*
151+
* However, when a LATERAL subquery is involved, we have to be a bit
152+
* laxer, because there may simply not be any paths for the joinrel that
153+
* aren't parameterized by whatever the subquery is parameterized by.
154+
* Hence, add to param_source_rels anything that is in the minimum
155+
* parameterization of either input (and not in the other input).
156+
*
157+
* XXX need a more principled way of determining minimum parameterization.
158+
*/
159+
if (outerrel->cheapest_total_path == NULL)
160+
{
161+
Path *cheapest = (Path *) linitial(outerrel->cheapest_parameterized_paths);
162+
163+
param_source_rels = bms_join(param_source_rels,
164+
bms_difference(PATH_REQ_OUTER(cheapest),
165+
innerrel->relids));
166+
}
167+
if (innerrel->cheapest_total_path == NULL)
168+
{
169+
Path *cheapest = (Path *) linitial(innerrel->cheapest_parameterized_paths);
170+
171+
param_source_rels = bms_join(param_source_rels,
172+
bms_difference(PATH_REQ_OUTER(cheapest),
173+
outerrel->relids));
174+
}
175+
150176
/*
151177
* 1. Consider mergejoin paths where both relations must be explicitly
152178
* sorted. Skip this if we can't mergejoin.

src/backend/optimizer/plan/createplan.c

+13-1
Original file line numberDiff line numberDiff line change
@@ -1713,20 +1713,32 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path,
17131713
ValuesScan *scan_plan;
17141714
Index scan_relid = best_path->parent->relid;
17151715
RangeTblEntry *rte;
1716+
List *values_lists;
17161717

17171718
/* it should be a values base rel... */
17181719
Assert(scan_relid > 0);
17191720
rte = planner_rt_fetch(scan_relid, root);
17201721
Assert(rte->rtekind == RTE_VALUES);
1722+
values_lists = rte->values_lists;
17211723

17221724
/* Sort clauses into best execution order */
17231725
scan_clauses = order_qual_clauses(root, scan_clauses);
17241726

17251727
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
17261728
scan_clauses = extract_actual_clauses(scan_clauses, false);
17271729

1730+
/* Replace any outer-relation variables with nestloop params */
1731+
if (best_path->param_info)
1732+
{
1733+
scan_clauses = (List *)
1734+
replace_nestloop_params(root, (Node *) scan_clauses);
1735+
/* The values lists could contain nestloop params, too */
1736+
values_lists = (List *)
1737+
replace_nestloop_params(root, (Node *) values_lists);
1738+
}
1739+
17281740
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
1729-
rte->values_lists);
1741+
values_lists);
17301742

17311743
copy_path_costsize(&scan_plan->scan.plan, best_path);
17321744

src/backend/optimizer/plan/initsplan.c

+14
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ extract_lateral_references(PlannerInfo *root, int rtindex)
232232
vars = pull_vars_of_level((Node *) rte->subquery, 1);
233233
else if (rte->rtekind == RTE_FUNCTION)
234234
vars = pull_vars_of_level(rte->funcexpr, 0);
235+
else if (rte->rtekind == RTE_VALUES)
236+
vars = pull_vars_of_level((Node *) rte->values_lists, 0);
235237
else
236238
return;
237239

@@ -874,9 +876,19 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
874876
/*
875877
* Cross-check: clause should contain no relids not within its scope.
876878
* Otherwise the parser messed up.
879+
*
880+
* XXX temporarily disable the qualscope cross-check, which tends to
881+
* reject quals pulled up from LATERAL subqueries. This is only in the
882+
* nature of a debugging crosscheck anyway. I'm loath to remove it
883+
* permanently, but need to think a bit harder about how to replace it.
884+
* See also disabled Assert below. (The ojscope test is still okay
885+
* because we prevent pullup of LATERAL subqueries that might cause it to
886+
* be violated.)
877887
*/
888+
#ifdef NOT_USED
878889
if (!bms_is_subset(relids, qualscope))
879890
elog(ERROR, "JOIN qualification cannot refer to other relations");
891+
#endif
880892
if (ojscope && !bms_is_subset(relids, ojscope))
881893
elog(ERROR, "JOIN qualification cannot refer to other relations");
882894

@@ -1031,7 +1043,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
10311043
if (outerjoin_delayed)
10321044
{
10331045
/* Should still be a subset of current scope ... */
1046+
#ifdef NOT_USED /* XXX temporarily disabled for LATERAL */
10341047
Assert(bms_is_subset(relids, qualscope));
1048+
#endif
10351049

10361050
/*
10371051
* Because application of the qual will be delayed by outer join,

src/backend/optimizer/plan/planner.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
334334
* query.
335335
*/
336336
parse->jointree = (FromExpr *)
337-
pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
337+
pull_up_subqueries(root, (Node *) parse->jointree);
338338

339339
/*
340340
* If this is a simple UNION ALL query, flatten it into an appendrel. We

0 commit comments

Comments
 (0)