Commit 084a29c9 authored by Tom Lane's avatar Tom Lane

Another round of planner fixes for LATERAL.

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.
parent 18226849
...@@ -237,14 +237,30 @@ extract_lateral_references(PlannerInfo *root, int rtindex) ...@@ -237,14 +237,30 @@ extract_lateral_references(PlannerInfo *root, int rtindex)
else else
return; 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; newvars = NIL;
foreach(lc, vars) foreach(lc, vars)
{ {
Var *var = (Var *) lfirst(lc); Node *var = (Node *) lfirst(lc);
var = copyObject(var); 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); newvars = lappend(newvars, var);
} }
......
...@@ -89,6 +89,8 @@ static Node *pullup_replace_vars(Node *expr, ...@@ -89,6 +89,8 @@ static Node *pullup_replace_vars(Node *expr,
pullup_replace_vars_context *context); pullup_replace_vars_context *context);
static Node *pullup_replace_vars_callback(Var *var, static Node *pullup_replace_vars_callback(Var *var,
replace_rte_variables_context *context); 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 reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
static void reduce_outer_joins_pass2(Node *jtnode, static void reduce_outer_joins_pass2(Node *jtnode,
reduce_outer_joins_state *state, reduce_outer_joins_state *state,
...@@ -1472,7 +1474,50 @@ replace_vars_in_jointree(Node *jtnode, ...@@ -1472,7 +1474,50 @@ replace_vars_in_jointree(Node *jtnode,
return; return;
if (IsA(jtnode, RangeTblRef)) 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)) else if (IsA(jtnode, FromExpr))
{ {
...@@ -1695,6 +1740,25 @@ pullup_replace_vars_callback(Var *var, ...@@ -1695,6 +1740,25 @@ pullup_replace_vars_callback(Var *var,
return newnode; 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 * flatten_simple_union_all
......
...@@ -247,11 +247,8 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context) ...@@ -247,11 +247,8 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
/* /*
* pull_vars_of_level * pull_vars_of_level
* Create a list of all Vars referencing the specified query level * Create a list of all Vars (and PlaceHolderVars) referencing the
* in the given parsetree. * specified query level in the given parsetree.
*
* This is used on unplanned parsetrees, so we don't expect to see any
* PlaceHolderVars.
* *
* Caution: the Vars are not copied, only linked into the list. * Caution: the Vars are not copied, only linked into the list.
*/ */
...@@ -288,7 +285,15 @@ pull_vars_walker(Node *node, pull_vars_context *context) ...@@ -288,7 +285,15 @@ pull_vars_walker(Node *node, pull_vars_context *context)
context->vars = lappend(context->vars, var); context->vars = lappend(context->vars, var);
return false; 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)) if (IsA(node, Query))
{ {
/* Recurse into RTE subquery or not-yet-planned sublink subquery */ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
......
...@@ -3242,6 +3242,100 @@ select * from int8_tbl a, ...@@ -3242,6 +3242,100 @@ select * from int8_tbl a,
4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 |
(57 rows) (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 -- 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, f1) g;
ERROR: column "f1" does not exist ERROR: column "f1" does not exist
......
...@@ -901,6 +901,22 @@ select * from int8_tbl a, ...@@ -901,6 +901,22 @@ select * from int8_tbl a,
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
on x.q2 = 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 -- 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, f1) g;
select f1,g from int4_tbl a, generate_series(0, a.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