]> granicus.if.org Git - postgresql/commitdiff
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;