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

Commit a742ecf

Browse files
committed
Cope with lateral references in the quals of a subquery RTE.
The qual pushdown logic assumed that all Vars in a restriction clause must be Vars referencing subquery outputs; but since we introduced LATERAL, it's possible for such a Var to be a lateral reference instead. This led to an assertion failure in debug builds. In a non-debug build, there might be no ill effects (if qual_is_pushdown_safe decided the qual was unsafe anyway), or we could get failures later due to construction of an invalid plan. I've not gone to much length to characterize the possible failures, but at least segfaults in the executor have been observed. Given that this has been busted since 9.3 and it took this long for anybody to notice, I judge that the case isn't worth going to great lengths to optimize. Hence, fix by just teaching qual_is_pushdown_safe that such quals are unsafe to push down, matching the previous behavior when it accidentally didn't fail. Per report from Tom Ellis. Back-patch to all supported branches. Discussion: https://postgr.es/m/20200713175124.GQ8220@cloudinit-builder
1 parent b5b4c0f commit a742ecf

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3508,8 +3508,10 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
35083508
Assert(!contain_window_function(qual));
35093509

35103510
/*
3511-
* Examine all Vars used in clause; since it's a restriction clause, all
3512-
* such Vars must refer to subselect output columns.
3511+
* Examine all Vars used in clause. Since it's a restriction clause, all
3512+
* such Vars must refer to subselect output columns ... unless this is
3513+
* part of a LATERAL subquery, in which case there could be lateral
3514+
* references.
35133515
*/
35143516
vars = pull_var_clause(qual, PVC_INCLUDE_PLACEHOLDERS);
35153517
foreach(vl, vars)
@@ -3529,7 +3531,19 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
35293531
break;
35303532
}
35313533

3532-
Assert(var->varno == rti);
3534+
/*
3535+
* Punt if we find any lateral references. It would be safe to push
3536+
* these down, but we'd have to convert them into outer references,
3537+
* which subquery_push_qual lacks the infrastructure to do. The case
3538+
* arises so seldom that it doesn't seem worth working hard on.
3539+
*/
3540+
if (var->varno != rti)
3541+
{
3542+
safe = false;
3543+
break;
3544+
}
3545+
3546+
/* Subqueries have no system columns */
35333547
Assert(var->varattno >= 0);
35343548

35353549
/* Check point 4 */

src/test/regress/expected/subselect.out

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,59 @@ from int4_tbl;
11591159
(4,5,6.0)
11601160
(5 rows)
11611161

1162+
--
1163+
-- Check for sane handling of a lateral reference in a subquery's quals
1164+
-- (most of the complication here is to prevent the test case from being
1165+
-- flattened too much)
1166+
--
1167+
explain (verbose, costs off)
1168+
select * from
1169+
int4_tbl i4,
1170+
lateral (
1171+
select i4.f1 > 1 as b, 1 as id
1172+
from (select random() order by 1) as t1
1173+
union all
1174+
select true as b, 2 as id
1175+
) as t2
1176+
where b and f1 >= 0;
1177+
QUERY PLAN
1178+
--------------------------------------------
1179+
Nested Loop
1180+
Output: i4.f1, ((i4.f1 > 1)), (1)
1181+
-> Seq Scan on public.int4_tbl i4
1182+
Output: i4.f1
1183+
Filter: (i4.f1 >= 0)
1184+
-> Append
1185+
-> Subquery Scan on t1
1186+
Output: (i4.f1 > 1), 1
1187+
Filter: (i4.f1 > 1)
1188+
-> Sort
1189+
Output: (random())
1190+
Sort Key: (random())
1191+
-> Result
1192+
Output: random()
1193+
-> Result
1194+
Output: true, 2
1195+
(16 rows)
1196+
1197+
select * from
1198+
int4_tbl i4,
1199+
lateral (
1200+
select i4.f1 > 1 as b, 1 as id
1201+
from (select random() order by 1) as t1
1202+
union all
1203+
select true as b, 2 as id
1204+
) as t2
1205+
where b and f1 >= 0;
1206+
f1 | b | id
1207+
------------+---+----
1208+
0 | t | 2
1209+
123456 | t | 1
1210+
123456 | t | 2
1211+
2147483647 | t | 1
1212+
2147483647 | t | 2
1213+
(5 rows)
1214+
11621215
--
11631216
-- Check that volatile quals aren't pushed down past a DISTINCT:
11641217
-- nextval() should not be called more than the nominal number of times

src/test/regress/sql/subselect.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,32 @@ select (select q from
627627
) q )
628628
from int4_tbl;
629629

630+
--
631+
-- Check for sane handling of a lateral reference in a subquery's quals
632+
-- (most of the complication here is to prevent the test case from being
633+
-- flattened too much)
634+
--
635+
explain (verbose, costs off)
636+
select * from
637+
int4_tbl i4,
638+
lateral (
639+
select i4.f1 > 1 as b, 1 as id
640+
from (select random() order by 1) as t1
641+
union all
642+
select true as b, 2 as id
643+
) as t2
644+
where b and f1 >= 0;
645+
646+
select * from
647+
int4_tbl i4,
648+
lateral (
649+
select i4.f1 > 1 as b, 1 as id
650+
from (select random() order by 1) as t1
651+
union all
652+
select true as b, 2 as id
653+
) as t2
654+
where b and f1 >= 0;
655+
630656
--
631657
-- Check that volatile quals aren't pushed down past a DISTINCT:
632658
-- nextval() should not be called more than the nominal number of times

0 commit comments

Comments
 (0)