Commit e76af541 authored by Tom Lane's avatar Tom Lane

Fix some issues with LATERAL(SELECT UNION ALL SELECT).

The LATERAL marking has to be propagated down to the UNION leaf queries
when we pull them up.  Also, fix the formerly stubbed-off
set_append_rel_pathlist().  It does already have enough smarts to cope with
making a parameterized Append path at need; it just has to not assume that
there *must* be an unparameterized path.
parent 83af58f6
......@@ -661,6 +661,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
int parentRTindex = rti;
List *live_childrels = NIL;
List *subpaths = NIL;
bool subpaths_valid = true;
List *all_child_pathkeys = NIL;
List *all_child_outers = NIL;
ListCell *l;
......@@ -699,19 +700,21 @@ 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 it to the live_childrels list for use below.
*/
live_childrels = lappend(live_childrels, childrel);
/*
* Child is live, so add its cheapest access path to the Append path
* we are constructing for the parent.
* If child has an unparameterized cheapest-total path, add that to
* the unparameterized Append path we are constructing for the parent.
* If not, there's no workable unparameterized path.
*/
subpaths = accumulate_append_subpath(subpaths,
if (childrel->cheapest_total_path)
subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path);
/* Remember which childrels are live, for logic below */
live_childrels = lappend(live_childrels, childrel);
else
subpaths_valid = false;
/*
* Collect lists of all the available path orderings and
......@@ -779,17 +782,20 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
}
/*
* Next, build an unordered, unparameterized Append path for the rel.
* (Note: this is correct even if we have zero or one live subpath due to
* constraint exclusion.)
* If we found unparameterized paths for all children, build an unordered,
* unparameterized Append path for the rel. (Note: this is correct even
* if we have zero or one live subpath due to constraint exclusion.)
*/
add_path(rel, (Path *) create_append_path(rel, subpaths, NULL));
if (subpaths_valid)
add_path(rel, (Path *) create_append_path(rel, subpaths, NULL));
/*
* Build unparameterized MergeAppend paths based on the collected list of
* child pathkeys.
* Also build unparameterized MergeAppend paths based on the collected
* list of child pathkeys.
*/
generate_mergeappend_paths(root, rel, live_childrels, all_child_pathkeys);
if (subpaths_valid)
generate_mergeappend_paths(root, rel, live_childrels,
all_child_pathkeys);
/*
* Build Append paths for each parameterization seen among the child rels.
......@@ -807,11 +813,11 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
foreach(l, all_child_outers)
{
Relids required_outer = (Relids) lfirst(l);
bool ok = true;
ListCell *lcr;
/* Select the child paths for an Append with this parameterization */
subpaths = NIL;
subpaths_valid = true;
foreach(lcr, live_childrels)
{
RelOptInfo *childrel = (RelOptInfo *) lfirst(lcr);
......@@ -831,7 +837,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
required_outer, 1.0);
if (cheapest_total == NULL)
{
ok = false;
subpaths_valid = false;
break;
}
}
......@@ -839,7 +845,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
subpaths = accumulate_append_subpath(subpaths, cheapest_total);
}
if (ok)
if (subpaths_valid)
add_path(rel, (Path *)
create_append_path(rel, subpaths, required_outer));
}
......@@ -911,13 +917,11 @@ 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 we do have an unparameterized path for this child */
Assert(cheapest_total != NULL);
Assert(cheapest_total->param_info == NULL);
}
/*
......
......@@ -995,20 +995,45 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
Query *subquery = rte->subquery;
int rtoffset;
int rtoffset = list_length(root->parse->rtable);
List *rtable;
/*
* Append child RTEs to parent rtable.
*
* Make a modifiable copy of the subquery's rtable, so we can adjust
* upper-level Vars in it. There are no such Vars in the setOperations
* tree proper, so fixing the rtable should be sufficient.
*/
rtable = copyObject(subquery->rtable);
/*
* Upper-level vars in subquery are now one level closer to their parent
* than before. We don't have to worry about offsetting varnos, though,
* because any such vars must refer to stuff above the level of the query
* we are pulling into.
* because the UNION leaf queries can't cross-reference each other.
*/
rtoffset = list_length(root->parse->rtable);
rtable = copyObject(subquery->rtable);
IncrementVarSublevelsUp_rtable(rtable, -1, 1);
/*
* If the UNION ALL subquery had a LATERAL marker, propagate that to all
* its children. The individual children might or might not contain any
* actual lateral cross-references, but we have to mark the pulled-up
* child RTEs so that later planner stages will check for such.
*/
if (rte->lateral)
{
ListCell *rt;
foreach(rt, rtable)
{
RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(rt);
Assert(child_rte->rtekind == RTE_SUBQUERY);
child_rte->lateral = true;
}
}
/*
* Append child RTEs to parent rtable.
*/
root->parse->rtable = list_concat(root->parse->rtable, rtable);
/*
......
......@@ -3109,6 +3109,32 @@ explain (costs off)
-> Function Scan on generate_series g
(4 rows)
-- lateral with UNION ALL subselect
explain (costs off)
select * from generate_series(100,200) g,
lateral (select * from int8_tbl a where g = q1 union all
select * from int8_tbl b where g = q2) ss;
QUERY PLAN
------------------------------------------
Nested Loop
-> Function Scan on generate_series g
-> Append
-> Seq Scan on int8_tbl a
Filter: (g.g = q1)
-> Seq Scan on int8_tbl b
Filter: (g.g = q2)
(7 rows)
select * from generate_series(100,200) g,
lateral (select * from int8_tbl a where g = q1 union all
select * from int8_tbl b where g = q2) ss;
g | q1 | q2
-----+------------------+------------------
123 | 123 | 456
123 | 123 | 4567890123456789
123 | 4567890123456789 | 123
(3 rows)
-- 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;
ERROR: column "f1" does not exist
......
......@@ -876,6 +876,15 @@ explain (costs off)
explain (costs off)
select count(*) from tenk1 a cross join lateral generate_series(1,two) g;
-- lateral with UNION ALL subselect
explain (costs off)
select * from generate_series(100,200) g,
lateral (select * from int8_tbl a where g = q1 union all
select * from int8_tbl b where g = q2) ss;
select * from generate_series(100,200) g,
lateral (select * from int8_tbl a where g = q1 union all
select * from int8_tbl b where g = q2) ss;
-- 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, 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