Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 084a29c

Browse files
committed
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.
1 parent 1822684 commit 084a29c

File tree

5 files changed

+205
-10
lines changed

5 files changed

+205
-10
lines changed

src/backend/optimizer/plan/initsplan.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -237,14 +237,30 @@ extract_lateral_references(PlannerInfo *root, int rtindex)
237237
else
238238
return;
239239

240-
/* Copy each Var and adjust it to match our level */
240+
/* Copy each Var (or PlaceHolderVar) and adjust it to match our level */
241241
newvars = NIL;
242242
foreach(lc, vars)
243243
{
244-
Var *var = (Var *) lfirst(lc);
244+
Node *var = (Node *) lfirst(lc);
245245

246246
var = copyObject(var);
247-
var->varlevelsup = 0;
247+
if (IsA(var, Var))
248+
{
249+
((Var *) var)->varlevelsup = 0;
250+
}
251+
else if (IsA(var, PlaceHolderVar))
252+
{
253+
/*
254+
* It's sufficient to set phlevelsup = 0, because we call
255+
* add_vars_to_targetlist with create_new_ph = false (as we must,
256+
* because deconstruct_jointree has already started); therefore
257+
* nobody is going to look at the contained expression to notice
258+
* whether its Vars have the right level.
259+
*/
260+
((PlaceHolderVar *) var)->phlevelsup = 0;
261+
}
262+
else
263+
Assert(false);
248264
newvars = lappend(newvars, var);
249265
}
250266

src/backend/optimizer/prep/prepjointree.c

+65-1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ static Node *pullup_replace_vars(Node *expr,
8989
pullup_replace_vars_context *context);
9090
static Node *pullup_replace_vars_callback(Var *var,
9191
replace_rte_variables_context *context);
92+
static Query *pullup_replace_vars_subquery(Query *query,
93+
pullup_replace_vars_context *context);
9294
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
9395
static void reduce_outer_joins_pass2(Node *jtnode,
9496
reduce_outer_joins_state *state,
@@ -1472,7 +1474,50 @@ replace_vars_in_jointree(Node *jtnode,
14721474
return;
14731475
if (IsA(jtnode, RangeTblRef))
14741476
{
1475-
/* nothing to do here */
1477+
/*
1478+
* If the RangeTblRef refers to a LATERAL subquery (that isn't the
1479+
* same subquery we're pulling up), it might contain references to the
1480+
* target subquery, which we must replace. We drive this from the
1481+
* jointree scan, rather than a scan of the rtable, for a couple of
1482+
* reasons: we can avoid processing no-longer-referenced RTEs, and we
1483+
* can use the appropriate setting of need_phvs depending on whether
1484+
* the RTE is above possibly-nulling outer joins or not.
1485+
*/
1486+
int varno = ((RangeTblRef *) jtnode)->rtindex;
1487+
1488+
if (varno != context->varno) /* ignore target subquery itself */
1489+
{
1490+
RangeTblEntry *rte = rt_fetch(varno, context->root->parse->rtable);
1491+
1492+
Assert(rte != context->target_rte);
1493+
if (rte->lateral)
1494+
{
1495+
switch (rte->rtekind)
1496+
{
1497+
case RTE_SUBQUERY:
1498+
rte->subquery =
1499+
pullup_replace_vars_subquery(rte->subquery,
1500+
context);
1501+
break;
1502+
case RTE_FUNCTION:
1503+
rte->funcexpr =
1504+
pullup_replace_vars(rte->funcexpr,
1505+
context);
1506+
break;
1507+
case RTE_VALUES:
1508+
rte->values_lists = (List *)
1509+
pullup_replace_vars((Node *) rte->values_lists,
1510+
context);
1511+
break;
1512+
case RTE_RELATION:
1513+
case RTE_JOIN:
1514+
case RTE_CTE:
1515+
/* these shouldn't be marked LATERAL */
1516+
Assert(false);
1517+
break;
1518+
}
1519+
}
1520+
}
14761521
}
14771522
else if (IsA(jtnode, FromExpr))
14781523
{
@@ -1695,6 +1740,25 @@ pullup_replace_vars_callback(Var *var,
16951740
return newnode;
16961741
}
16971742

1743+
/*
1744+
* Apply pullup variable replacement to a subquery
1745+
*
1746+
* This needs to be different from pullup_replace_vars() because
1747+
* replace_rte_variables will think that it shouldn't increment sublevels_up
1748+
* before entering the Query; so we need to call it with sublevels_up == 1.
1749+
*/
1750+
static Query *
1751+
pullup_replace_vars_subquery(Query *query,
1752+
pullup_replace_vars_context *context)
1753+
{
1754+
Assert(IsA(query, Query));
1755+
return (Query *) replace_rte_variables((Node *) query,
1756+
context->varno, 1,
1757+
pullup_replace_vars_callback,
1758+
(void *) context,
1759+
NULL);
1760+
}
1761+
16981762

16991763
/*
17001764
* flatten_simple_union_all

src/backend/optimizer/util/var.c

+11-6
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,8 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
247247

248248
/*
249249
* pull_vars_of_level
250-
* Create a list of all Vars referencing the specified query level
251-
* in the given parsetree.
252-
*
253-
* This is used on unplanned parsetrees, so we don't expect to see any
254-
* PlaceHolderVars.
250+
* Create a list of all Vars (and PlaceHolderVars) referencing the
251+
* specified query level in the given parsetree.
255252
*
256253
* Caution: the Vars are not copied, only linked into the list.
257254
*/
@@ -288,7 +285,15 @@ pull_vars_walker(Node *node, pull_vars_context *context)
288285
context->vars = lappend(context->vars, var);
289286
return false;
290287
}
291-
Assert(!IsA(node, PlaceHolderVar));
288+
if (IsA(node, PlaceHolderVar))
289+
{
290+
PlaceHolderVar *phv = (PlaceHolderVar *) node;
291+
292+
if (phv->phlevelsup == context->sublevels_up)
293+
context->vars = lappend(context->vars, phv);
294+
/* we don't want to look into the contained expression */
295+
return false;
296+
}
292297
if (IsA(node, Query))
293298
{
294299
/* Recurse into RTE subquery or not-yet-planned sublink subquery */

src/test/regress/expected/join.out

+94
Original file line numberDiff line numberDiff line change
@@ -3242,6 +3242,100 @@ select * from int8_tbl a,
32423242
4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 |
32433243
(57 rows)
32443244

3245+
-- lateral references requiring pullup
3246+
select * from (values(1)) x(lb),
3247+
lateral generate_series(lb,4) x4;
3248+
lb | x4
3249+
----+----
3250+
1 | 1
3251+
1 | 2
3252+
1 | 3
3253+
1 | 4
3254+
(4 rows)
3255+
3256+
select * from (select f1/1000000000 from int4_tbl) x(lb),
3257+
lateral generate_series(lb,4) x4;
3258+
lb | x4
3259+
----+----
3260+
0 | 0
3261+
0 | 1
3262+
0 | 2
3263+
0 | 3
3264+
0 | 4
3265+
0 | 0
3266+
0 | 1
3267+
0 | 2
3268+
0 | 3
3269+
0 | 4
3270+
0 | 0
3271+
0 | 1
3272+
0 | 2
3273+
0 | 3
3274+
0 | 4
3275+
2 | 2
3276+
2 | 3
3277+
2 | 4
3278+
-2 | -2
3279+
-2 | -1
3280+
-2 | 0
3281+
-2 | 1
3282+
-2 | 2
3283+
-2 | 3
3284+
-2 | 4
3285+
(25 rows)
3286+
3287+
select * from (values(1)) x(lb),
3288+
lateral (values(lb)) y(lbcopy);
3289+
lb | lbcopy
3290+
----+--------
3291+
1 | 1
3292+
(1 row)
3293+
3294+
select * from (values(1)) x(lb),
3295+
lateral (select lb from int4_tbl) y(lbcopy);
3296+
lb | lbcopy
3297+
----+--------
3298+
1 | 1
3299+
1 | 1
3300+
1 | 1
3301+
1 | 1
3302+
1 | 1
3303+
(5 rows)
3304+
3305+
select * from
3306+
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
3307+
lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2);
3308+
q1 | q2 | q1 | q2 | xq1 | yq1 | yq2
3309+
------------------+-------------------+------------------+-------------------+------------------+------------------+-------------------
3310+
123 | 456 | | | 123 | |
3311+
123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789
3312+
123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
3313+
123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123
3314+
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789
3315+
4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456
3316+
4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789
3317+
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
3318+
4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123
3319+
4567890123456789 | -4567890123456789 | | | 4567890123456789 | |
3320+
(10 rows)
3321+
3322+
select * from
3323+
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
3324+
lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
3325+
q1 | q2 | q1 | q2 | xq1 | yq1 | yq2
3326+
------------------+-------------------+------------------+-------------------+------------------+------------------+-------------------
3327+
123 | 456 | | | 123 | |
3328+
123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789
3329+
123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789
3330+
123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123
3331+
4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789
3332+
4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456
3333+
4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789
3334+
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
3335+
4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123
3336+
4567890123456789 | -4567890123456789 | | | 4567890123456789 | |
3337+
(10 rows)
3338+
32453339
-- test some error cases where LATERAL should have been used but wasn't
32463340
select f1,g from int4_tbl a, generate_series(0, f1) g;
32473341
ERROR: column "f1" does not exist

src/test/regress/sql/join.sql

+16
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,22 @@ select * from int8_tbl a,
901901
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
902902
on x.q2 = ss.z;
903903

904+
-- lateral references requiring pullup
905+
select * from (values(1)) x(lb),
906+
lateral generate_series(lb,4) x4;
907+
select * from (select f1/1000000000 from int4_tbl) x(lb),
908+
lateral generate_series(lb,4) x4;
909+
select * from (values(1)) x(lb),
910+
lateral (values(lb)) y(lbcopy);
911+
select * from (values(1)) x(lb),
912+
lateral (select lb from int4_tbl) y(lbcopy);
913+
select * from
914+
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
915+
lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2);
916+
select * from
917+
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
918+
lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
919+
904920
-- test some error cases where LATERAL should have been used but wasn't
905921
select f1,g from int4_tbl a, generate_series(0, f1) g;
906922
select f1,g from int4_tbl a, generate_series(0, a.f1) g;

0 commit comments

Comments
 (0)