-- 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;

