mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix bogus Assert in find_var_for_subquery_tle
find_var_for_subquery_tle() searches a subquery rel's targetlist for a Var referencing a given subquery output column, and asserted that any Var it finds there belongs to that rel. That does not hold for an appendrel child. set_append_rel_size() builds the child's targetlist by translating the parent's, and as noted there the result can contain arbitrary expressions. In particular, when a member of a LATERAL UNION ALL subquery is pulled up and its output is a lateral reference, the translated expression is a Var of the laterally referenced rel. If the child is itself a subquery with sorted paths, convert_subquery_pathkeys() would trip the Assert. In non-assert builds, the function went on to compare such a Var's varattno with the subquery column's resno, and so could match a Var of an unrelated rel. To fix, ignore Vars that do not belong to the subquery rel, the same way set_rel_width() already does. Also correct the adjacent comment, which claimed that the only non-Var entries are placeholders. Back-patch to all supported branches. Author: Richard Guo <guofenglinux@gmail.com> Reviewed-by: shihao zhong <zhong950419@gmail.com> Discussion: https://postgr.es/m/CAMbWs48fFiPi7Q5yJCoM_nAs47zd5XgvPHd9nZyM_ewq+k_jpQ@mail.gmail.com Backpatch-through: 14
This commit is contained in:
@@ -1262,10 +1262,18 @@ find_var_for_subquery_tle(RelOptInfo *rel, TargetEntry *tle)
|
||||
{
|
||||
Var *var = (Var *) lfirst(lc);
|
||||
|
||||
/* Ignore placeholders */
|
||||
/* Ignore placeholders and other non-Var expressions */
|
||||
if (!IsA(var, Var))
|
||||
continue;
|
||||
Assert(var->varno == rel->relid);
|
||||
|
||||
/*
|
||||
* Ordinarily, a Var in a rel's targetlist must belong to that rel;
|
||||
* but there are corner cases involving LATERAL references where that
|
||||
* isn't so. If the Var has the wrong varno, ignore it, since it
|
||||
* cannot be referencing the subquery's output.
|
||||
*/
|
||||
if (var->varno != rel->relid)
|
||||
continue;
|
||||
|
||||
/* If we find a Var referencing this TLE, we're good */
|
||||
if (var->varattno == tle->resno)
|
||||
|
||||
@@ -9591,6 +9591,24 @@ select * from generate_series(100,200) g,
|
||||
123 | 4567890123456789 | 123
|
||||
(3 rows)
|
||||
|
||||
-- lateral with UNION ALL subselect, where a member is a subquery whose
|
||||
-- output contains a lateral reference
|
||||
explain (costs off)
|
||||
select ss.v from int8_tbl x,
|
||||
lateral (select x.q1 from (select distinct q2 from int8_tbl) s union all
|
||||
select q2 from int8_tbl) ss(v);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------
|
||||
Nested Loop
|
||||
-> Seq Scan on int8_tbl x
|
||||
-> Append
|
||||
-> Subquery Scan on s
|
||||
-> HashAggregate
|
||||
Group Key: int8_tbl_1.q2
|
||||
-> Seq Scan on int8_tbl int8_tbl_1
|
||||
-> Seq Scan on int8_tbl
|
||||
(8 rows)
|
||||
|
||||
-- lateral with VALUES
|
||||
explain (costs off)
|
||||
select count(*) from tenk1 a,
|
||||
|
||||
@@ -3754,6 +3754,13 @@ select * from generate_series(100,200) g,
|
||||
lateral (select * from int8_tbl a where g = q1 union all
|
||||
select * from int8_tbl b where g = q2) ss;
|
||||
|
||||
-- lateral with UNION ALL subselect, where a member is a subquery whose
|
||||
-- output contains a lateral reference
|
||||
explain (costs off)
|
||||
select ss.v from int8_tbl x,
|
||||
lateral (select x.q1 from (select distinct q2 from int8_tbl) s union all
|
||||
select q2 from int8_tbl) ss(v);
|
||||
|
||||
-- lateral with VALUES
|
||||
explain (costs off)
|
||||
select count(*) from tenk1 a,
|
||||
|
||||
Reference in New Issue
Block a user