diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index bfda05394d6492c623fa57c521278227bc889705..91acebc37295e7addb2c6f98c6bf977bfefa1d8f 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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); } /* diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 06dbe84540444fa4d72b771b0d08137f3c1b4587..07b35f98bd1da5d0ae1b774a5fad1e358587f489 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -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); /* diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 5e17432198ec81a53d50943636afbb57edb88b72..6503dd1d2f8e7ac6b6c6c97655fdcae2aa609245 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -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 diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 5de98dc0a722c1f855689105b3beacd6e5a810ee..40db5602dd477de7b496ddbabbf4766de08c19fb 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -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;