1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Prevent inlining of multiply-referenced CTEs with outer recursive refs.

This has to be prevented because inlining would result in multiple
self-references, which we don't support (and in fact that's disallowed
by the SQL spec, see statements about linearly vs. nonlinearly
recursive queries).  Bug fix for commit 608b167f9.

Per report from Yaroslav Schekin (via Andrew Gierth)

Discussion: https://postgr.es/m/87wolmg60q.fsf@news-spur.riddles.org.uk
This commit is contained in:
Tom Lane
2019-04-09 15:47:26 -04:00
parent 4dba0f6dc4
commit 9476131278
3 changed files with 201 additions and 0 deletions

View File

@ -1299,6 +1299,106 @@ select * from x, x x2 where x.n = x2.n;
Output: subselect_tbl_1.f1
(11 rows)
-- Multiply-referenced CTEs can't be inlined if they contain outer self-refs
explain (verbose, costs off)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z1.a as a from z cross join z as z1
where length(z.a || z1.a) < 5))
select * from x;
QUERY PLAN
----------------------------------------------------------
CTE Scan on x
Output: x.a
CTE x
-> Recursive Union
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1
-> Nested Loop
Output: (z.a || z1.a)
Join Filter: (length((z.a || z1.a)) < 5)
CTE z
-> WorkTable Scan on x x_1
Output: x_1.a
-> CTE Scan on z
Output: z.a
-> CTE Scan on z z1
Output: z1.a
(16 rows)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z1.a as a from z cross join z as z1
where length(z.a || z1.a) < 5))
select * from x;
a
------
a
b
aa
ab
ba
bb
aaaa
aaab
aaba
aabb
abaa
abab
abba
abbb
baaa
baab
baba
babb
bbaa
bbab
bbba
bbbb
(22 rows)
explain (verbose, costs off)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z.a as a from z
where length(z.a || z.a) < 5))
select * from x;
QUERY PLAN
--------------------------------------------------------
CTE Scan on x
Output: x.a
CTE x
-> Recursive Union
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1
-> WorkTable Scan on x x_1
Output: (x_1.a || x_1.a)
Filter: (length((x_1.a || x_1.a)) < 5)
(9 rows)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z.a as a from z
where length(z.a || z.a) < 5))
select * from x;
a
------
a
b
aa
bb
aaaa
bbbb
(6 rows)
-- Check handling of outer references
explain (verbose, costs off)
with x as (select * from int4_tbl)

View File

@ -690,6 +690,41 @@ explain (verbose, costs off)
with x as not materialized (select * from (select f1, now() as n from subselect_tbl) ss)
select * from x, x x2 where x.n = x2.n;
-- Multiply-referenced CTEs can't be inlined if they contain outer self-refs
explain (verbose, costs off)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z1.a as a from z cross join z as z1
where length(z.a || z1.a) < 5))
select * from x;
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z1.a as a from z cross join z as z1
where length(z.a || z1.a) < 5))
select * from x;
explain (verbose, costs off)
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z.a as a from z
where length(z.a || z.a) < 5))
select * from x;
with recursive x(a) as
((values ('a'), ('b'))
union all
(with z as not materialized (select * from x)
select z.a || z.a as a from z
where length(z.a || z.a) < 5))
select * from x;
-- Check handling of outer references
explain (verbose, costs off)
with x as (select * from int4_tbl)