Commit c1774d2c authored by Tom Lane's avatar Tom Lane

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.
parent e76af541
...@@ -1210,15 +1210,33 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) ...@@ -1210,15 +1210,33 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
* set_values_pathlist * set_values_pathlist
* Build the (single) access path for a VALUES RTE * Build the (single) access path for a VALUES RTE
* *
* There can be no need for a parameterized path here. (Although the SQL * As with subqueries, a VALUES RTE's path might be parameterized due to
* spec does allow LATERAL (VALUES (x)), the parser will transform that * LATERAL references, but that's inherent in the values expressions and
* into a subquery, so it doesn't end up here.) * not a result of pushing down join quals.
*/ */
static void static void
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{ {
Relids required_outer;
/*
* If it's a LATERAL RTE, it might contain some Vars of the current query
* level, requiring it to be treated as parameterized. (NB: even though
* the parser never marks VALUES RTEs as LATERAL, they could be so marked
* by now, as a result of subquery pullup.)
*/
if (rte->lateral)
{
required_outer = pull_varnos_of_level((Node *) rte->values_lists, 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 */ /* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel)); add_path(rel, create_valuesscan_path(root, rel, required_outer));
/* Select cheapest path (pretty easy in this case...) */ /* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel); set_cheapest(rel);
......
...@@ -1046,19 +1046,27 @@ cost_functionscan(Path *path, PlannerInfo *root, ...@@ -1046,19 +1046,27 @@ cost_functionscan(Path *path, PlannerInfo *root,
/* /*
* cost_valuesscan * cost_valuesscan
* Determines and returns the cost of scanning a VALUES RTE. * Determines and returns the cost of scanning a VALUES RTE.
*
* 'baserel' is the relation to be scanned
* 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
*/ */
void void
cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel) cost_valuesscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info)
{ {
Cost startup_cost = 0; Cost startup_cost = 0;
Cost run_cost = 0; Cost run_cost = 0;
QualCost qpqual_cost;
Cost cpu_per_tuple; Cost cpu_per_tuple;
/* Should only be applied to base relations that are values lists */ /* Should only be applied to base relations that are values lists */
Assert(baserel->relid > 0); Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_VALUES); Assert(baserel->rtekind == RTE_VALUES);
/* valuesscans are never parameterized */ /* Mark the path with the correct row estimate */
if (param_info)
path->rows = param_info->ppi_rows;
else
path->rows = baserel->rows; path->rows = baserel->rows;
/* /*
...@@ -1068,8 +1076,10 @@ cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel) ...@@ -1068,8 +1076,10 @@ cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
cpu_per_tuple = cpu_operator_cost; cpu_per_tuple = cpu_operator_cost;
/* Add scanning CPU costs */ /* Add scanning CPU costs */
startup_cost += baserel->baserestrictcost.startup; get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
startup_cost += qpqual_cost.startup;
cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples; run_cost += cpu_per_tuple * baserel->tuples;
path->startup_cost = startup_cost; path->startup_cost = startup_cost;
......
...@@ -147,6 +147,32 @@ add_paths_to_joinrel(PlannerInfo *root, ...@@ -147,6 +147,32 @@ add_paths_to_joinrel(PlannerInfo *root,
sjinfo->min_lefthand)); sjinfo->min_lefthand));
} }
/*
* However, when a LATERAL subquery is involved, we have to be a bit
* laxer, because there may simply not be any paths for the joinrel that
* aren't parameterized by whatever the subquery is parameterized by.
* Hence, add to param_source_rels anything that is in the minimum
* parameterization of either input (and not in the other input).
*
* XXX need a more principled way of determining minimum parameterization.
*/
if (outerrel->cheapest_total_path == NULL)
{
Path *cheapest = (Path *) linitial(outerrel->cheapest_parameterized_paths);
param_source_rels = bms_join(param_source_rels,
bms_difference(PATH_REQ_OUTER(cheapest),
innerrel->relids));
}
if (innerrel->cheapest_total_path == NULL)
{
Path *cheapest = (Path *) linitial(innerrel->cheapest_parameterized_paths);
param_source_rels = bms_join(param_source_rels,
bms_difference(PATH_REQ_OUTER(cheapest),
outerrel->relids));
}
/* /*
* 1. Consider mergejoin paths where both relations must be explicitly * 1. Consider mergejoin paths where both relations must be explicitly
* sorted. Skip this if we can't mergejoin. * sorted. Skip this if we can't mergejoin.
......
...@@ -1713,11 +1713,13 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path, ...@@ -1713,11 +1713,13 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path,
ValuesScan *scan_plan; ValuesScan *scan_plan;
Index scan_relid = best_path->parent->relid; Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte; RangeTblEntry *rte;
List *values_lists;
/* it should be a values base rel... */ /* it should be a values base rel... */
Assert(scan_relid > 0); Assert(scan_relid > 0);
rte = planner_rt_fetch(scan_relid, root); rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_VALUES); Assert(rte->rtekind == RTE_VALUES);
values_lists = rte->values_lists;
/* Sort clauses into best execution order */ /* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses); scan_clauses = order_qual_clauses(root, scan_clauses);
...@@ -1725,8 +1727,18 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path, ...@@ -1725,8 +1727,18 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path,
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false); 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 values lists could contain nestloop params, too */
values_lists = (List *)
replace_nestloop_params(root, (Node *) values_lists);
}
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid, scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
rte->values_lists); values_lists);
copy_path_costsize(&scan_plan->scan.plan, best_path); copy_path_costsize(&scan_plan->scan.plan, best_path);
......
...@@ -232,6 +232,8 @@ extract_lateral_references(PlannerInfo *root, int rtindex) ...@@ -232,6 +232,8 @@ extract_lateral_references(PlannerInfo *root, int rtindex)
vars = pull_vars_of_level((Node *) rte->subquery, 1); vars = pull_vars_of_level((Node *) rte->subquery, 1);
else if (rte->rtekind == RTE_FUNCTION) else if (rte->rtekind == RTE_FUNCTION)
vars = pull_vars_of_level(rte->funcexpr, 0); vars = pull_vars_of_level(rte->funcexpr, 0);
else if (rte->rtekind == RTE_VALUES)
vars = pull_vars_of_level((Node *) rte->values_lists, 0);
else else
return; return;
...@@ -874,9 +876,19 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -874,9 +876,19 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
/* /*
* Cross-check: clause should contain no relids not within its scope. * Cross-check: clause should contain no relids not within its scope.
* Otherwise the parser messed up. * Otherwise the parser messed up.
*
* XXX temporarily disable the qualscope cross-check, which tends to
* reject quals pulled up from LATERAL subqueries. This is only in the
* nature of a debugging crosscheck anyway. I'm loath to remove it
* permanently, but need to think a bit harder about how to replace it.
* See also disabled Assert below. (The ojscope test is still okay
* because we prevent pullup of LATERAL subqueries that might cause it to
* be violated.)
*/ */
#ifdef NOT_USED
if (!bms_is_subset(relids, qualscope)) if (!bms_is_subset(relids, qualscope))
elog(ERROR, "JOIN qualification cannot refer to other relations"); elog(ERROR, "JOIN qualification cannot refer to other relations");
#endif
if (ojscope && !bms_is_subset(relids, ojscope)) if (ojscope && !bms_is_subset(relids, ojscope))
elog(ERROR, "JOIN qualification cannot refer to other relations"); elog(ERROR, "JOIN qualification cannot refer to other relations");
...@@ -1031,7 +1043,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, ...@@ -1031,7 +1043,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
if (outerjoin_delayed) if (outerjoin_delayed)
{ {
/* Should still be a subset of current scope ... */ /* Should still be a subset of current scope ... */
#ifdef NOT_USED /* XXX temporarily disabled for LATERAL */
Assert(bms_is_subset(relids, qualscope)); Assert(bms_is_subset(relids, qualscope));
#endif
/* /*
* Because application of the qual will be delayed by outer join, * Because application of the qual will be delayed by outer join,
......
...@@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
* query. * query.
*/ */
parse->jointree = (FromExpr *) parse->jointree = (FromExpr *)
pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL); pull_up_subqueries(root, (Node *) parse->jointree);
/* /*
* If this is a simple UNION ALL query, flatten it into an appendrel. We * If this is a simple UNION ALL query, flatten it into an appendrel. We
......
This diff is collapsed.
...@@ -1689,16 +1689,18 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, ...@@ -1689,16 +1689,18 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel,
* returning the pathnode. * returning the pathnode.
*/ */
Path * Path *
create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel) create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer)
{ {
Path *pathnode = makeNode(Path); Path *pathnode = makeNode(Path);
pathnode->pathtype = T_ValuesScan; pathnode->pathtype = T_ValuesScan;
pathnode->parent = rel; pathnode->parent = rel;
pathnode->param_info = NULL; /* never parameterized at present */ pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->pathkeys = NIL; /* result is always unordered */ pathnode->pathkeys = NIL; /* result is always unordered */
cost_valuesscan(pathnode, root, rel); cost_valuesscan(pathnode, root, rel, pathnode->param_info);
return pathnode; return pathnode;
} }
......
...@@ -83,7 +83,7 @@ extern void cost_subqueryscan(Path *path, PlannerInfo *root, ...@@ -83,7 +83,7 @@ extern void cost_subqueryscan(Path *path, PlannerInfo *root,
extern void cost_functionscan(Path *path, PlannerInfo *root, extern void cost_functionscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info); RelOptInfo *baserel, ParamPathInfo *param_info);
extern void cost_valuesscan(Path *path, PlannerInfo *root, extern void cost_valuesscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel); RelOptInfo *baserel, ParamPathInfo *param_info);
extern void cost_ctescan(Path *path, PlannerInfo *root, RelOptInfo *baserel); extern void cost_ctescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
extern void cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm); extern void cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm);
extern void cost_sort(Path *path, PlannerInfo *root, extern void cost_sort(Path *path, PlannerInfo *root,
......
...@@ -71,7 +71,8 @@ extern Path *create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, ...@@ -71,7 +71,8 @@ extern Path *create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys, Relids required_outer); List *pathkeys, Relids required_outer);
extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer); Relids required_outer);
extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel); extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel); extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel); extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel);
extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
......
...@@ -23,9 +23,7 @@ ...@@ -23,9 +23,7 @@
*/ */
extern void pull_up_sublinks(PlannerInfo *root); extern void pull_up_sublinks(PlannerInfo *root);
extern void inline_set_returning_functions(PlannerInfo *root); extern void inline_set_returning_functions(PlannerInfo *root);
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode, extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode);
JoinExpr *lowest_outer_join,
AppendRelInfo *containing_appendrel);
extern void flatten_simple_union_all(PlannerInfo *root); extern void flatten_simple_union_all(PlannerInfo *root);
extern void reduce_outer_joins(PlannerInfo *root); extern void reduce_outer_joins(PlannerInfo *root);
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins); extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
......
...@@ -3000,11 +3000,11 @@ explain (costs off) ...@@ -3000,11 +3000,11 @@ explain (costs off)
select unique2, x.* select unique2, x.*
from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x;
QUERY PLAN QUERY PLAN
---------------------------------- -------------------------------------------------
Nested Loop Nested Loop
-> Seq Scan on tenk1 a
-> Seq Scan on int4_tbl b -> Seq Scan on int4_tbl b
Filter: (f1 = a.unique1) -> Index Scan using tenk1_unique1 on tenk1 a
Index Cond: (unique1 = b.f1)
(4 rows) (4 rows)
select unique2, x.* select unique2, x.*
...@@ -3022,7 +3022,7 @@ explain (costs off) ...@@ -3022,7 +3022,7 @@ explain (costs off)
Nested Loop Nested Loop
-> Seq Scan on int4_tbl x -> Seq Scan on int4_tbl x
-> Index Scan using tenk1_unique1 on tenk1 -> Index Scan using tenk1_unique1 on tenk1
Index Cond: (x.f1 = unique1) Index Cond: (unique1 = x.f1)
(4 rows) (4 rows)
explain (costs off) explain (costs off)
...@@ -3033,7 +3033,7 @@ explain (costs off) ...@@ -3033,7 +3033,7 @@ explain (costs off)
Nested Loop Nested Loop
-> Seq Scan on int4_tbl x -> Seq Scan on int4_tbl x
-> Index Scan using tenk1_unique1 on tenk1 -> Index Scan using tenk1_unique1 on tenk1
Index Cond: (x.f1 = unique1) Index Cond: (unique1 = x.f1)
(4 rows) (4 rows)
select unique2, x.* select unique2, x.*
...@@ -3051,14 +3051,12 @@ explain (costs off) ...@@ -3051,14 +3051,12 @@ explain (costs off)
select unique2, x.* select unique2, x.*
from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on f1 = unique1; from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on f1 = unique1;
QUERY PLAN QUERY PLAN
----------------------------------------------------- -----------------------------------------------
Nested Loop Left Join Nested Loop Left Join
-> Seq Scan on int4_tbl x -> Seq Scan on int4_tbl x
-> Subquery Scan on ss
Filter: (x.f1 = ss.unique1)
-> Index Scan using tenk1_unique1 on tenk1 -> Index Scan using tenk1_unique1 on tenk1
Index Cond: (x.f1 = unique1) Index Cond: (x.f1 = unique1)
(6 rows) (4 rows)
-- check scoping of lateral versus parent references -- check scoping of lateral versus parent references
-- the first of these should return int8_tbl.q2, the second int8_tbl.q1 -- the first of these should return int8_tbl.q2, the second int8_tbl.q1
...@@ -3135,6 +3133,109 @@ select * from generate_series(100,200) g, ...@@ -3135,6 +3133,109 @@ select * from generate_series(100,200) g,
123 | 4567890123456789 | 123 123 | 4567890123456789 | 123
(3 rows) (3 rows)
-- lateral with VALUES
explain (costs off)
select count(*) from tenk1 a,
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
QUERY PLAN
------------------------------------------------------------------
Aggregate
-> Hash Join
Hash Cond: ("*VALUES*".column1 = b.unique2)
-> Nested Loop
-> Index Only Scan using tenk1_unique1 on tenk1 a
-> Values Scan on "*VALUES*"
-> Hash
-> Index Only Scan using tenk1_unique2 on tenk1 b
(8 rows)
select count(*) from tenk1 a,
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
count
-------
10000
(1 row)
-- lateral injecting a strange outer join condition
explain (costs off)
select * from int8_tbl a,
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
on x.q2 = ss.z;
QUERY PLAN
------------------------------------------
Nested Loop Left Join
Join Filter: (x.q2 = ($0))
-> Nested Loop
-> Seq Scan on int8_tbl a
-> Materialize
-> Seq Scan on int8_tbl x
-> Seq Scan on int4_tbl y
(7 rows)
select * from int8_tbl a,
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
on x.q2 = ss.z;
q1 | q2 | q1 | q2 | z
------------------+-------------------+------------------+-------------------+------------------
123 | 456 | 123 | 456 |
123 | 456 | 123 | 4567890123456789 |
123 | 456 | 4567890123456789 | 123 | 123
123 | 456 | 4567890123456789 | 123 | 123
123 | 456 | 4567890123456789 | 123 | 123
123 | 456 | 4567890123456789 | 123 | 123
123 | 456 | 4567890123456789 | 123 | 123
123 | 456 | 4567890123456789 | 4567890123456789 |
123 | 456 | 4567890123456789 | -4567890123456789 |
123 | 4567890123456789 | 123 | 456 |
123 | 4567890123456789 | 123 | 4567890123456789 |
123 | 4567890123456789 | 4567890123456789 | 123 | 123
123 | 4567890123456789 | 4567890123456789 | 123 | 123
123 | 4567890123456789 | 4567890123456789 | 123 | 123
123 | 4567890123456789 | 4567890123456789 | 123 | 123
123 | 4567890123456789 | 4567890123456789 | 123 | 123
123 | 4567890123456789 | 4567890123456789 | 4567890123456789 |
123 | 4567890123456789 | 4567890123456789 | -4567890123456789 |
4567890123456789 | 123 | 123 | 456 |
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | 123 |
4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 123 | 4567890123456789 | -4567890123456789 |
4567890123456789 | 4567890123456789 | 123 | 456 |
4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | 123 |
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 |
4567890123456789 | -4567890123456789 | 123 | 456 |
4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | 123 |
4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 |
(57 rows)
-- test some error cases where LATERAL should have been used but wasn't -- test some error cases where LATERAL should have been used but wasn't
select f1,g from int4_tbl a, generate_series(0, f1) g; select f1,g from int4_tbl a, generate_series(0, f1) g;
ERROR: column "f1" does not exist ERROR: column "f1" does not exist
......
...@@ -885,6 +885,22 @@ select * from generate_series(100,200) g, ...@@ -885,6 +885,22 @@ select * from generate_series(100,200) g,
lateral (select * from int8_tbl a where g = q1 union all lateral (select * from int8_tbl a where g = q1 union all
select * from int8_tbl b where g = q2) ss; select * from int8_tbl b where g = q2) ss;
-- lateral with VALUES
explain (costs off)
select count(*) from tenk1 a,
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
select count(*) from tenk1 a,
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
-- lateral injecting a strange outer join condition
explain (costs off)
select * from int8_tbl a,
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
on x.q2 = ss.z;
select * from int8_tbl a,
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
on x.q2 = ss.z;
-- test some error cases where LATERAL should have been used but wasn't -- test some error cases where LATERAL should have been used but wasn't
select f1,g from int4_tbl a, generate_series(0, f1) g; select f1,g from int4_tbl a, generate_series(0, f1) g;
select f1,g from int4_tbl a, generate_series(0, a.f1) g; select f1,g from int4_tbl a, generate_series(0, a.f1) g;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment