blob: d5775fb5d1d22ddb15431e2462c30d03b38f3bf9 [file] [log] [blame]
-- Tests exercising different behaviour of the WITH RECURSIVE implementation in GPDB
-- GPDB's distributed nature requires thorough testing of many use cases in order to ensure correctness
-- Setup
create schema recursive_cte;
set search_path=recursive_cte;
create table recursive_table_1(id int);
insert into recursive_table_1 values (1), (2), (100);
-- Test the featureblocking GUC for recursive CTE
set gp_recursive_cte to off;
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where recursive_table_1.id IN (select * from r limit 10);
set gp_recursive_cte to on;
-- WITH RECURSIVE ref used with IN without correlation
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where recursive_table_1.id IN (select * from r limit 10);
-- WITH RECURSIVE ref used with NOT IN without correlation
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where recursive_table_1.id NOT IN (select * from r limit 10);
-- WITH RECURSIVE ref used with EXISTS without correlation
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where EXISTS (select * from r limit 10);
-- WITH RECURSIVE ref used with NOT EXISTS without correlation
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where NOT EXISTS (select * from r limit 10);
create table recursive_table_2(id int);
insert into recursive_table_2 values (11) , (21), (31);
-- WITH RECURSIVE ref used with IN & correlation
with recursive r(i) as (
select * from recursive_table_2
union all
select r.i + 1 from r, recursive_table_2 where r.i = recursive_table_2.id
)
select recursive_table_1.id from recursive_table_1, recursive_table_2 where recursive_table_1.id IN (select * from r where r.i = recursive_table_2.id);
-- WITH RECURSIVE ref used with NOT IN & correlation
with recursive r(i) as (
select * from recursive_table_2
union all
select r.i + 1 from r, recursive_table_2 where r.i = recursive_table_2.id
)
select recursive_table_1.id from recursive_table_1, recursive_table_2 where recursive_table_1.id NOT IN (select * from r where r.i = recursive_table_2.id);
-- WITH RECURSIVE ref used with EXISTS & correlation
with recursive r(i) as (
select * from recursive_table_2
union all
select r.i + 1 from r, recursive_table_2 where r.i = recursive_table_2.id
)
select recursive_table_1.id from recursive_table_1, recursive_table_2 where recursive_table_1.id = recursive_table_2.id and EXISTS (select * from r where r.i = recursive_table_2.id);
-- WITH RECURSIVE ref used with NOT EXISTS & correlation
with recursive r(i) as (
select * from recursive_table_2
union all
select r.i + 1 from r, recursive_table_2 where r.i = recursive_table_2.id
)
select recursive_table_1.id from recursive_table_1, recursive_table_2 where recursive_table_1.id = recursive_table_2.id and NOT EXISTS (select * from r where r.i = recursive_table_2.id);
-- WITH RECURSIVE ref used within a Expression sublink
with recursive r(i) as (
select 1
union all
select i + 1 from r
)
select * from recursive_table_1 where recursive_table_1.id >= (select i from r limit 1) order by recursive_table_1.id;
-- WITH RECURSIVE ref used within an EXISTS subquery in another recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and EXISTS (select * from r limit 10)
)
select * from y limit 10;
-- WITH RECURSIVE ref used within a NOT EXISTS subquery in another recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and NOT EXISTS (select * from r limit 10)
)
select * from y limit 10;
-- WITH RECURSIVE ref used within an IN subquery in a non-recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y as (
select * from recursive_table_1 where recursive_table_1.id IN (select * from r limit 10)
)
select * from y;
-- WITH RECURSIVE ref used within a NOT IN subquery in a non-recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y as (
select * from recursive_table_1 where recursive_table_1.id NOT IN (select * from r limit 10)
)
select * from y;
-- WITH RECURSIVE ref used within an EXISTS subquery in a non-recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y as (
select * from recursive_table_1 where EXISTS (select * from r limit 10)
)
select * from y;
-- WITH RECURSIVE ref used within a NOT EXISTS subquery in a non-recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y as (
select * from recursive_table_1 where NOT EXISTS (select * from r limit 10)
)
select * from y;
-- WITH RECURSIVE non-recursive ref used within an EXISTS subquery in a recursive CTE
with recursive
r as (
select * from recursive_table_2
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and EXISTS (select * from r)
)
select * from y limit 10;
-- WITH RECURSIVE non-recursive ref used within a NOT EXISTS subquery in a recursive CTE
with recursive
r as (
select * from recursive_table_2
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and NOT EXISTS (select * from r)
)
select * from y limit 10;
-- WITH ref used within an IN subquery in another CTE
with
r as (
select * from recursive_table_2 where id < 21
),
y as (
select * from recursive_table_1 where id IN (select * from r)
)
select * from y;
-- WITH ref used within a NOT IN subquery in another CTE
with
r as (
select * from recursive_table_2 where id < 21
),
y as (
select * from recursive_table_1 where id NOT IN (select * from r)
)
select * from y;
-- WITH ref used within an EXISTS subquery in another CTE
with
r as (
select * from recursive_table_2 where id < 21
),
y as (
select * from recursive_table_1 where EXISTS (select * from r)
)
select * from y;
-- WITH ref used within a NOT EXISTS subquery in another CTE
with
r as (
select * from recursive_table_2 where id < 21
),
y as (
select * from recursive_table_1 where NOT EXISTS (select * from r)
)
select * from y;
-- WITH RECURSIVE ref used within a IN subquery in another recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and i IN (select * from r limit 10)
)
select * from y limit 10;
-- WITH RECURSIVE ref used within a NOT IN subquery in another recursive CTE
with recursive
r(i) as (
select 1
union all
select r.i + 1 from r, recursive_table_2 where i = recursive_table_2.id
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and i NOT IN (select * from r limit 10)
)
select * from y limit 10;
-- WITH RECURSIVE non-recursive ref used within an IN subquery in a recursive CTE
with recursive
r as (
select * from recursive_table_2
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and i IN (select * from r)
)
select * from y limit 10;
-- WITH RECURSIVE non-recursive ref used within a NOT IN subquery in a recursive CTE
with recursive
r as (
select * from recursive_table_2
),
y(i) as (
select 1
union all
select i + 1 from y, recursive_table_1 where i = recursive_table_1.id and i NOT IN (select * from r)
)
select * from y limit 10;
create table recursive_table_3(id int, a int);
insert into recursive_table_3 values (1, 2), (2, 3);
-- WITH RECURSIVE ref used within a window function
with recursive r(i, j) as (
select * from recursive_table_3
union all
select i + 1, j from r, recursive_table_3 where r.i < recursive_table_3.id
)
select avg(i) over(partition by j) from r limit 100;
-- WITH RECURSIVE ref used within a UDF
create function sum_to_zero(integer) returns bigint as $$
with recursive r(i) as (
select $1
union all
select i - 1 from r where i > 0
)
select sum(i) from r;
$$ language sql;
select sum_to_zero(10);
-- WITH RECURSIVE ref used within a UDF against a distributed table
create table people(name text, parent_of text);
insert into people values ('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e');
create function get_lineage(text) returns setof text as $$
with recursive r(person) as (
select name from people where name = $1
union all
select name from r, people where people.parent_of = r.person
)
select * from r;
$$ language sql;
select get_lineage('d');
-- non-recursive CTE nested in non-recursive enclosing CTE
INSERT INTO recursive_table_1 SELECT i FROM generate_series(0, 100) i;
SELECT MAX(j)
FROM
(
WITH nr1(i) AS (SELECT id FROM recursive_table_1 WHERE id >= 10)
SELECT * FROM
(
WITH nr2(j) AS (SELECT i FROM nr1 WHERE i >= 50)
SELECT nr2.j FROM nr2, nr1
) sub2
) sub1;
-- non-recursive CTE nested in recursive enclosing CTE
WITH RECURSIVE r1(i) AS
(
SELECT 1
UNION ALL
(
WITH r2(j) AS
(
SELECT id FROM recursive_table_1 WHERE id < 5
)
SELECT SUM(j) FROM r2
)
)
SELECT * FROM r1;
-- recursive CTE nested in recursive enclosing CTE
WITH RECURSIVE r1(i) AS
(
SELECT 1
UNION ALL
(
WITH RECURSIVE r2(j) AS
(
SELECT 1
UNION ALL
SELECT j + 1 FROM r2 WHERE j < 5
)
SELECT i + 1 FROM r1, r2 WHERE i < 5
)
)
SELECT SUM(i) FROM r1;
-- recursive CTE nested in non-recursive enclosing CTE
WITH nr(i) AS
(
WITH RECURSIVE r(j) AS
(
SELECT 1
UNION ALL
SELECT j + 1 FROM r WHERE j < 5
)
SELECT SUM(j) FROM r
)
SELECT SUM(i) FROM nr;
-- WITH RECURSIVE ref within a correlated subquery
create table recursive_table_4(a int, b int);
create table recursive_table_5(c int, d int);
insert into recursive_table_4 select i, i* 2 from generate_series(1, 10) i;
insert into recursive_table_5 select i/2, i from generate_series(1, 10) i;
select * from recursive_table_4 where a > ALL (
with recursive r(i) as (
select sum(c) from recursive_table_5 where d < recursive_table_4.b
union all
select i / 2 from r where i > 0
)
select * from r
);
with recursive x(i) as (
select 1
),
y(i) as (
select sum(i) from x
union all
select i + 1 from y
),
z(i) as (
select avg(i) from x
union all
select i + 1 from z
)
(select * from y limit 5)
union
(select * from z limit 10);
-- WTIH RECURSIVE and replicated table
create table t_rep_test_rcte(c int) ;
create table t_rand_test_rcte(c int);
insert into t_rep_test_rcte values (1);
insert into t_rand_test_rcte values (1), (2), (3);
analyze t_rep_test_rcte;
analyze t_rand_test_rcte;
explain
with recursive the_cte_here(n) as (
select * from t_rep_test_rcte
union all
select n+1 from the_cte_here join t_rand_test_rcte
on t_rand_test_rcte.c = the_cte_here.n)
select * from the_cte_here;
with recursive the_cte_here(n) as (
select * from t_rep_test_rcte
union all
select n+1 from the_cte_here join t_rand_test_rcte
on t_rand_test_rcte.c = the_cte_here.n)
select * from the_cte_here;
-- WITH RECURSIVE non-recursive shouldn't have hash locus
create table recursive_locus_t1(id int, pid int, name text) ;
insert into recursive_locus_t1 values(0, -1, 'AAA');
insert into recursive_locus_t1 values(1, 0, 'B1');
insert into recursive_locus_t1 values(2, 0, 'B2');
insert into recursive_locus_t1 values(3, 1, 'C1_1');
insert into recursive_locus_t1 values(4, 1, 'C1_2');
insert into recursive_locus_t1 values(5, 1, 'C1_3');
insert into recursive_locus_t1 values(6, 2, 'C2_1');
insert into recursive_locus_t1 values(7, 2, 'C2_2');
insert into recursive_locus_t1 values(8, 2, 'C2_3');
explain (costs off) with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t1 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t1 k inner join cte c on c.id = k.pid
)
select id,name from cte;
with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t1 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t1 k inner join cte c on c.id = k.pid
)
select id,name from cte;
create table recursive_locus_t2(id int, pid int, name text) ;
insert into recursive_locus_t2 values(0, -1, 'AAA');
insert into recursive_locus_t2 values(1, 0, 'B1');
insert into recursive_locus_t2 values(2, 0, 'B2');
insert into recursive_locus_t2 values(3, 1, 'C1_1');
insert into recursive_locus_t2 values(4, 1, 'C1_2');
insert into recursive_locus_t2 values(5, 1, 'C1_3');
insert into recursive_locus_t2 values(6, 2, 'C2_1');
insert into recursive_locus_t2 values(7, 2, 'C2_2');
insert into recursive_locus_t2 values(8, 2, 'C2_3');
explain (costs off) with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t2 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t2 k inner join cte c on c.id = k.pid
)
select id,name from cte;
with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t2 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t2 k inner join cte c on c.id = k.pid
)
select id,name from cte;
create table recursive_locus_t3(id int, pid int, name text);
insert into recursive_locus_t3 values(0, -1, 'AAA');
insert into recursive_locus_t3 values(1, 0, 'B1');
insert into recursive_locus_t3 values(2, 0, 'B2');
insert into recursive_locus_t3 values(3, 1, 'C1_1');
insert into recursive_locus_t3 values(4, 1, 'C1_2');
insert into recursive_locus_t3 values(5, 1, 'C1_3');
insert into recursive_locus_t3 values(6, 2, 'C2_1');
insert into recursive_locus_t3 values(7, 2, 'C2_2');
insert into recursive_locus_t3 values(8, 2, 'C2_3');
explain (costs off) with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t3 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t3 k inner join cte c on c.id = k.pid
)
select id,name from cte;
with RECURSIVE cte as (
select a.id, a.name from recursive_locus_t3 a where id=0
union all
select k.id, (c.name || '>' || k.name) as name from recursive_locus_t3 k inner join cte c on c.id = k.pid
)
select id,name from cte;