Replace relids in lateral subquery parse tree during SJE

Reported-by: Alexander Lakhin
Discussion: https://postgr.es/m/56ee4520-e9d1-d519-54fe-c8bff880ce9b%40gmail.com
Author: Alexander Korotkov, Andrei Lepikhov
This commit is contained in:
Alexander Korotkov 2024-02-20 14:10:10 +02:00
parent 74563f6b90
commit 489072ab7a
3 changed files with 84 additions and 1 deletions

View file

@ -395,7 +395,34 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
}
/* Update lateral references. */
replace_varno((Node *) otherrel->lateral_vars, relid, subst);
if (root->hasLateralRTEs)
{
RangeTblEntry *rte = root->simple_rte_array[rti];
ReplaceVarnoContext ctx = {.from = relid,.to = subst};
if (rte->lateral)
{
replace_varno((Node *) otherrel->lateral_vars, relid, subst);
/*
* Although we pass root->parse through cleanup procedure,
* but parse->rtable and rte contains refs to different copies
* of the subquery.
*/
if (otherrel->rtekind == RTE_SUBQUERY)
query_tree_walker(rte->subquery, replace_varno_walker, &ctx,
QTW_EXAMINE_SORTGROUP);
#ifdef USE_ASSERT_CHECKING
/* Just check possibly hidden non-replaced relids */
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->tablesample)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->functions)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->tablefunc)));
Assert(!bms_is_member(relid, pull_varnos(root, (Node *) rte->values_lists)));
#endif
}
}
}
/*

View file

@ -6349,6 +6349,50 @@ on true;
-> Seq Scan on int8_tbl y
(7 rows)
-- Test processing target lists in lateral subqueries
explain (verbose, costs off)
SELECT t3.a FROM sj t1, sj t2,
LATERAL (SELECT t1.a WHERE t1.a <> 1
GROUP BY (t1.a) HAVING t1.a > 0 ORDER BY t1.a LIMIT 1) t3,
LATERAL (SELECT t1.a,t3.a WHERE t1.a <> t3.a+t2.a
GROUP BY (t3.a) HAVING t1.a > t3.a*t3.a+t2.a/t1.a LIMIT 2) t4,
LATERAL (SELECT * FROM sj TABLESAMPLE bernoulli(t1.a/t2.a)
REPEATABLE (t1.a+t2.a)) t5,
LATERAL generate_series(1, t1.a + t2.a) AS t6
WHERE t1.a = t2.a;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
Nested Loop
Output: (t2.a)
-> Nested Loop
Output: t2.a, (t2.a)
-> Nested Loop
Output: t2.a, (t2.a)
-> Nested Loop
Output: t2.a, (t2.a)
-> Seq Scan on public.sj t2
Output: t2.a, t2.b, t2.c
Filter: (t2.a IS NOT NULL)
-> Limit
Output: (t2.a)
-> Group
Output: t2.a
-> Result
One-Time Filter: ((t2.a <> 1) AND (t2.a > 0))
-> Limit
Output: NULL::integer, ((t2.a))
-> Group
Output: NULL::integer, (t2.a)
-> Result
One-Time Filter: ((t2.a <> ((t2.a) + t2.a)) AND (t2.a > (((t2.a) * (t2.a)) + (t2.a / t2.a))))
-> Sample Scan on public.sj
Output: sj.a, sj.b, sj.c
Sampling: bernoulli ((t2.a / t2.a)) REPEATABLE ((t2.a + t2.a))
-> Function Scan on pg_catalog.generate_series t6
Output: t6.t6
Function Call: generate_series(1, (t2.a + t2.a))
(29 rows)
-- Check updating of Lateral links from top-level query to the removing relation
explain (COSTS OFF)
SELECT * FROM pg_am am WHERE am.amname IN (

View file

@ -2406,6 +2406,18 @@ left join (select coalesce(y.q1, 1) from int8_tbl y
on true) z
on true;
-- Test processing target lists in lateral subqueries
explain (verbose, costs off)
SELECT t3.a FROM sj t1, sj t2,
LATERAL (SELECT t1.a WHERE t1.a <> 1
GROUP BY (t1.a) HAVING t1.a > 0 ORDER BY t1.a LIMIT 1) t3,
LATERAL (SELECT t1.a,t3.a WHERE t1.a <> t3.a+t2.a
GROUP BY (t3.a) HAVING t1.a > t3.a*t3.a+t2.a/t1.a LIMIT 2) t4,
LATERAL (SELECT * FROM sj TABLESAMPLE bernoulli(t1.a/t2.a)
REPEATABLE (t1.a+t2.a)) t5,
LATERAL generate_series(1, t1.a + t2.a) AS t6
WHERE t1.a = t2.a;
-- Check updating of Lateral links from top-level query to the removing relation
explain (COSTS OFF)
SELECT * FROM pg_am am WHERE am.amname IN (