Fix some issues with LATERAL(SELECT UNION ALL SELECT).
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Aug 2012 22:42:20 +0000 (18:42 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Aug 2012 22:42:56 +0000 (18:42 -0400)
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.

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/prep/prepjointree.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index bfda05394d6492c623fa57c521278227bc889705..91acebc37295e7addb2c6f98c6bf977bfefa1d8f 100644 (file)
@@ -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);
            }
 
            /*
index 06dbe84540444fa4d72b771b0d08137f3c1b4587..07b35f98bd1da5d0ae1b774a5fad1e358587f489 100644 (file)
@@ -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);
 
    /*
index 5e17432198ec81a53d50943636afbb57edb88b72..6503dd1d2f8e7ac6b6c6c97655fdcae2aa609245 100644 (file)
@@ -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
index 5de98dc0a722c1f855689105b3beacd6e5a810ee..40db5602dd477de7b496ddbabbf4766de08c19fb 100644 (file)
@@ -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;