]> granicus.if.org Git - postgresql/commitdiff
Another round of planner fixes for LATERAL.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Aug 2012 18:10:17 +0000 (14:10 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Aug 2012 18:10:17 +0000 (14:10 -0400)
Formerly, subquery pullup had no need to examine other entries in the range
table, since they could not contain any references to the subquery being
pulled up.  That's no longer true with LATERAL, so now we need to be able
to visit rangetable subexpressions to replace Vars referencing the
pulled-up subquery.  Also, this means that extract_lateral_references must
be unsurprised at encountering lateral PlaceHolderVars, since such might be
created when pulling up a subquery that's underneath an outer join with
respect to the lateral reference.

src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/var.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 996f91d3091fe5e058a442ea8cb0f4daff3f2d22..81f7cf2e9d85ee0975c57405d8441596a42cd4e3 100644 (file)
@@ -237,14 +237,30 @@ extract_lateral_references(PlannerInfo *root, int rtindex)
        else
                return;
 
-       /* Copy each Var and adjust it to match our level */
+       /* Copy each Var (or PlaceHolderVar) and adjust it to match our level */
        newvars = NIL;
        foreach(lc, vars)
        {
-               Var                *var = (Var *) lfirst(lc);
+               Node   *var = (Node *) lfirst(lc);
 
                var = copyObject(var);
-               var->varlevelsup = 0;
+               if (IsA(var, Var))
+               {
+                       ((Var *) var)->varlevelsup = 0;
+               }
+               else if (IsA(var, PlaceHolderVar))
+               {
+                       /*
+                        * It's sufficient to set phlevelsup = 0, because we call
+                        * add_vars_to_targetlist with create_new_ph = false (as we must,
+                        * because deconstruct_jointree has already started); therefore
+                        * nobody is going to look at the contained expression to notice
+                        * whether its Vars have the right level.
+                        */
+                       ((PlaceHolderVar *) var)->phlevelsup = 0;
+               }
+               else
+                       Assert(false);
                newvars = lappend(newvars, var);
        }
 
index f5deed4bc8268278db58d8ad927c6f3e3c85f4d7..d72c78f8db4a13ee93e889e4e2b178113d90dc73 100644 (file)
@@ -89,6 +89,8 @@ static Node *pullup_replace_vars(Node *expr,
                                        pullup_replace_vars_context *context);
 static Node *pullup_replace_vars_callback(Var *var,
                                                         replace_rte_variables_context *context);
+static Query *pullup_replace_vars_subquery(Query *query,
+                                                        pullup_replace_vars_context *context);
 static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
 static void reduce_outer_joins_pass2(Node *jtnode,
                                                 reduce_outer_joins_state *state,
@@ -1472,7 +1474,50 @@ replace_vars_in_jointree(Node *jtnode,
                return;
        if (IsA(jtnode, RangeTblRef))
        {
-               /* nothing to do here */
+               /*
+                * If the RangeTblRef refers to a LATERAL subquery (that isn't the
+                * same subquery we're pulling up), it might contain references to the
+                * target subquery, which we must replace.  We drive this from the
+                * jointree scan, rather than a scan of the rtable, for a couple of
+                * reasons: we can avoid processing no-longer-referenced RTEs, and we
+                * can use the appropriate setting of need_phvs depending on whether
+                * the RTE is above possibly-nulling outer joins or not.
+                */
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+
+               if (varno != context->varno)    /* ignore target subquery itself */
+               {
+                       RangeTblEntry *rte = rt_fetch(varno, context->root->parse->rtable);
+
+                       Assert(rte != context->target_rte);
+                       if (rte->lateral)
+                       {
+                               switch (rte->rtekind)
+                               {
+                                       case RTE_SUBQUERY:
+                                               rte->subquery =
+                                                       pullup_replace_vars_subquery(rte->subquery,
+                                                                                                                context);
+                                               break;
+                                       case RTE_FUNCTION:
+                                               rte->funcexpr =
+                                                       pullup_replace_vars(rte->funcexpr,
+                                                                                               context);
+                                               break;
+                                       case RTE_VALUES:
+                                               rte->values_lists = (List *)
+                                                       pullup_replace_vars((Node *) rte->values_lists,
+                                                                                               context);
+                                               break;
+                                       case RTE_RELATION:
+                                       case RTE_JOIN:
+                                       case RTE_CTE:
+                                               /* these shouldn't be marked LATERAL */
+                                               Assert(false);
+                                               break;
+                               }
+                       }
+               }
        }
        else if (IsA(jtnode, FromExpr))
        {
@@ -1695,6 +1740,25 @@ pullup_replace_vars_callback(Var *var,
        return newnode;
 }
 
+/*
+ * Apply pullup variable replacement to a subquery
+ *
+ * This needs to be different from pullup_replace_vars() because
+ * replace_rte_variables will think that it shouldn't increment sublevels_up
+ * before entering the Query; so we need to call it with sublevels_up == 1.
+ */
+static Query *
+pullup_replace_vars_subquery(Query *query,
+                                                        pullup_replace_vars_context *context)
+{
+       Assert(IsA(query, Query));
+       return (Query *) replace_rte_variables((Node *) query,
+                                                                                  context->varno, 1,
+                                                                                  pullup_replace_vars_callback,
+                                                                                  (void *) context,
+                                                                                  NULL);
+}
+
 
 /*
  * flatten_simple_union_all
index 1d88a7782030a6bd595833b047a19a98d94a246e..21b7753f05d456d349a92b2d7fc34feb58125984 100644 (file)
@@ -247,11 +247,8 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
 
 /*
  * pull_vars_of_level
- *             Create a list of all Vars referencing the specified query level
- *             in the given parsetree.
- *
- * This is used on unplanned parsetrees, so we don't expect to see any
- * PlaceHolderVars.
+ *             Create a list of all Vars (and PlaceHolderVars) referencing the
+ *             specified query level in the given parsetree.
  *
  * Caution: the Vars are not copied, only linked into the list.
  */
@@ -288,7 +285,15 @@ pull_vars_walker(Node *node, pull_vars_context *context)
                        context->vars = lappend(context->vars, var);
                return false;
        }
-       Assert(!IsA(node, PlaceHolderVar));
+       if (IsA(node, PlaceHolderVar))
+       {
+               PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+               if (phv->phlevelsup == context->sublevels_up)
+                       context->vars = lappend(context->vars, phv);
+               /* we don't want to look into the contained expression */
+               return false;
+       }
        if (IsA(node, Query))
        {
                /* Recurse into RTE subquery or not-yet-planned sublink subquery */
index 51aeb8de7ba9fc15a2cde6b5a0018a41d76c2200..0856b457bfc4c2521d00e2670b6088c3a6166346 100644 (file)
@@ -3242,6 +3242,100 @@ select * from int8_tbl a,
  4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 |                 
 (57 rows)
 
+-- lateral references requiring pullup
+select * from (values(1)) x(lb),
+  lateral generate_series(lb,4) x4;
+ lb | x4 
+----+----
+  1 |  1
+  1 |  2
+  1 |  3
+  1 |  4
+(4 rows)
+
+select * from (select f1/1000000000 from int4_tbl) x(lb),
+  lateral generate_series(lb,4) x4;
+ lb | x4 
+----+----
+  0 |  0
+  0 |  1
+  0 |  2
+  0 |  3
+  0 |  4
+  0 |  0
+  0 |  1
+  0 |  2
+  0 |  3
+  0 |  4
+  0 |  0
+  0 |  1
+  0 |  2
+  0 |  3
+  0 |  4
+  2 |  2
+  2 |  3
+  2 |  4
+ -2 | -2
+ -2 | -1
+ -2 |  0
+ -2 |  1
+ -2 |  2
+ -2 |  3
+ -2 |  4
+(25 rows)
+
+select * from (values(1)) x(lb),
+  lateral (values(lb)) y(lbcopy);
+ lb | lbcopy 
+----+--------
+  1 |      1
+(1 row)
+
+select * from (values(1)) x(lb),
+  lateral (select lb from int4_tbl) y(lbcopy);
+ lb | lbcopy 
+----+--------
+  1 |      1
+  1 |      1
+  1 |      1
+  1 |      1
+  1 |      1
+(5 rows)
+
+select * from
+  int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
+  lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2);
+        q1        |        q2         |        q1        |        q2         |       xq1        |       yq1        |        yq2        
+------------------+-------------------+------------------+-------------------+------------------+------------------+-------------------
+              123 |               456 |                  |                   |              123 |                  |                  
+              123 |  4567890123456789 | 4567890123456789 | -4567890123456789 |              123 | 4567890123456789 | -4567890123456789
+              123 |  4567890123456789 | 4567890123456789 |  4567890123456789 |              123 | 4567890123456789 |  4567890123456789
+              123 |  4567890123456789 | 4567890123456789 |               123 |              123 | 4567890123456789 |               123
+ 4567890123456789 |               123 |              123 |  4567890123456789 | 4567890123456789 |              123 |  4567890123456789
+ 4567890123456789 |               123 |              123 |               456 | 4567890123456789 |              123 |               456
+ 4567890123456789 |  4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789
+ 4567890123456789 |  4567890123456789 | 4567890123456789 |  4567890123456789 | 4567890123456789 | 4567890123456789 |  4567890123456789
+ 4567890123456789 |  4567890123456789 | 4567890123456789 |               123 | 4567890123456789 | 4567890123456789 |               123
+ 4567890123456789 | -4567890123456789 |                  |                   | 4567890123456789 |                  |                  
+(10 rows)
+
+select * from
+  int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
+  lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
+        q1        |        q2         |        q1        |        q2         |       xq1        |       yq1        |        yq2        
+------------------+-------------------+------------------+-------------------+------------------+------------------+-------------------
+              123 |               456 |                  |                   |              123 |                  |                  
+              123 |  4567890123456789 | 4567890123456789 | -4567890123456789 |              123 | 4567890123456789 | -4567890123456789
+              123 |  4567890123456789 | 4567890123456789 |  4567890123456789 |              123 | 4567890123456789 |  4567890123456789
+              123 |  4567890123456789 | 4567890123456789 |               123 |              123 | 4567890123456789 |               123
+ 4567890123456789 |               123 |              123 |  4567890123456789 | 4567890123456789 |              123 |  4567890123456789
+ 4567890123456789 |               123 |              123 |               456 | 4567890123456789 |              123 |               456
+ 4567890123456789 |  4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789
+ 4567890123456789 |  4567890123456789 | 4567890123456789 |  4567890123456789 | 4567890123456789 | 4567890123456789 |  4567890123456789
+ 4567890123456789 |  4567890123456789 | 4567890123456789 |               123 | 4567890123456789 | 4567890123456789 |               123
+ 4567890123456789 | -4567890123456789 |                  |                   | 4567890123456789 |                  |                  
+(10 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 30ea48cb926bddb0d8d60bf38ab9928633aad73d..3c8ed5027efa275d4689d0c92dbf9c1891380b39 100644 (file)
@@ -901,6 +901,22 @@ 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;
 
+-- lateral references requiring pullup
+select * from (values(1)) x(lb),
+  lateral generate_series(lb,4) x4;
+select * from (select f1/1000000000 from int4_tbl) x(lb),
+  lateral generate_series(lb,4) x4;
+select * from (values(1)) x(lb),
+  lateral (values(lb)) y(lbcopy);
+select * from (values(1)) x(lb),
+  lateral (select lb from int4_tbl) y(lbcopy);
+select * from
+  int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
+  lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2);
+select * from
+  int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
+  lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
+
 -- 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;