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.
This commit is contained in:
Tom Lane 2012-08-18 14:10:17 -04:00
parent 18226849ea
commit 084a29c94f
5 changed files with 205 additions and 10 deletions

View 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);
}

View 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

View 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 */

View 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

View 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;