-- ===================================================================
-- test recursive planning functionality with subqueries and CTEs
-- ===================================================================
SET search_path TO subquery_and_ctes;


CREATE TABLE users_table_local AS SELECT * FROM users_table;

SET client_min_messages TO DEBUG1;

-- CTEs are recursively planned, and subquery foo is also recursively planned
-- final plan becomes a router plan
WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT user_id FROM events_table
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT 
	count(*) 
FROM 
	cte,
	  (SELECT 
    	DISTINCT users_table.user_id 
     FROM 
     	users_table, events_table 
     WHERE 
     	users_table.user_id = events_table.user_id AND 
     event_type IN (1,2,3,4)
     ORDER BY 1 DESC LIMIT 5
     ) as foo 
	  WHERE foo.user_id = cte.user_id;

-- CTEs are recursively planned, and subquery foo is also recursively planned
-- final plan becomes a real-time plan since we also have events_table in the 
-- range table entries 
WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT user_id FROM events_table
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT 
	count(*) 
FROM 
	cte,
	  (SELECT 
    	DISTINCT users_table.user_id 
     FROM 
     	users_table, events_table 
     WHERE 
     	users_table.user_id = events_table.user_id AND 
     event_type IN (1,2,3,4)
     ORDER BY 1 DESC LIMIT 5
     ) as foo, events_table
	  WHERE foo.user_id = cte.user_id AND events_table.user_id = cte.user_id;

-- CTEs are replaced and subquery in WHERE is also replaced
-- but the query is still real-time query since users_table is in the 
-- range table list
WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT user_id FROM events_table
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT DISTINCT cte.user_id
FROM users_table, cte
WHERE 
	users_table.user_id = cte.user_id AND
	users_table.user_id IN (SELECT DISTINCT value_2 FROM users_table WHERE value_1 >= 1 AND value_1 <= 20 ORDER BY 1 LIMIT 5)
    ORDER BY 1 DESC;

-- subquery in WHERE clause is planned recursively due to the recurring table
-- in FROM clause
WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT user_id FROM events_table
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT DISTINCT cte.user_id
FROM cte
WHERE 
	cte.user_id IN (SELECT DISTINCT user_id FROM users_table WHERE value_1 >= 1 AND value_1 <= 20)
    ORDER BY 1 DESC;

-- CTEs inside a subquery and the final query becomes a router
-- query
SELECT
   user_id
FROM
    (
	     WITH cte AS (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	     ) SELECT * FROM cte ORDER BY 1 DESC
     ) as foo;


-- CTEs inside a subquery and the final query becomes a
-- real-time query since the other subquery is safe to pushdown
SELECT
   bar.user_id
FROM
    (
	     WITH cte AS (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	     ) SELECT * FROM cte ORDER BY 1 DESC
     ) as foo, 
    (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	    
     ) as bar  
WHERE foo.user_id = bar.user_id;

-- CTEs inside a deeper subquery
-- and also the subquery that contains the CTE is replaced
SELECT
   DISTINCT bar.user_id
FROM
    (
	     WITH cte AS (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	     ) SELECT * FROM cte ORDER BY 1 DESC
     ) as foo, 
    (
	    SELECT 
	    	users_table.user_id, some_events.event_type
	     FROM 
	     	users_table, 
	     	(
	     		WITH cte AS (
			    SELECT 
			    	event_type, users_table.user_id
			     FROM 
			     	users_table, events_table 
			     WHERE 
			     	users_table.user_id = events_table.user_id AND 
			     value_1 IN (1,2)
			     ) SELECT * FROM cte ORDER BY 1 DESC
	     	) as some_events
	     WHERE 
	     	users_table.user_id = some_events.user_id AND 
	     event_type IN (1,2,3,4)
	     ORDER BY 2,1
	     LIMIT 2
	    
     ) as bar  
WHERE foo.user_id = bar.user_id
ORDER BY 1 DESC LIMIT 5;



-- CTEs on the different parts of the query is replaced 
-- and subquery foo is also replaced since it contains
-- DISTINCT on a non-partition key 
SELECT * FROM 
(
	WITH cte AS (
		WITH local_cte AS (
			SELECT * FROM users_table_local
		),
		dist_cte AS (
			SELECT user_id FROM events_table
		)
		SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
	)
	SELECT DISTINCT cte.user_id
	FROM users_table, cte
	WHERE 
		users_table.user_id = cte.user_id AND
		users_table.user_id IN 
			(WITH cte_in_where AS (SELECT DISTINCT value_2 FROM users_table WHERE value_1 >= 1 AND value_1 <= 20 ORDER BY 1 LIMIT 5) SELECT * FROM cte_in_where)
	    ORDER BY 1 DESC
	    ) as foo, 
			events_table 
		WHERE 
			foo.user_id = events_table.value_2
ORDER BY 3 DESC, 2 DESC, 1 DESC 
LIMIT 5;


-- now recursively plan subqueries inside the CTEs that contains LIMIT and OFFSET
WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT 
			user_id
		FROM 
			events_table, 
			(SELECT DISTINCT value_2 FROM users_table OFFSET 0) as foo
		WHERE 
			events_table.user_id = foo.value_2 AND
			events_table.user_id IN (SELECT DISTINCT value_1 FROM users_table ORDER BY 1 LIMIT 3)
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT 
	count(*) 
FROM 
	cte,
	  (SELECT 
    	DISTINCT users_table.user_id 
     FROM 
     	users_table, events_table 
     WHERE 
     	users_table.user_id = events_table.user_id AND 
     event_type IN (1,2,3,4)
     ORDER BY 1 DESC LIMIT 5
     ) as foo 
	  WHERE foo.user_id = cte.user_id;

-- the same query, but this time the CTEs also live inside a subquery
SELECT 
	* 
FROM 
(

	WITH cte AS (
	WITH local_cte AS (
		SELECT * FROM users_table_local
	),
	dist_cte AS (
		SELECT 
			user_id
		FROM 
			events_table, 
			(SELECT DISTINCT value_2 FROM users_table OFFSET 0) as foo
		WHERE 
			events_table.user_id = foo.value_2 AND
			events_table.user_id IN (SELECT DISTINCT value_1 FROM users_table ORDER BY 1 LIMIT 3)
	)
	SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id
)
SELECT 
	count(*)  as cnt
FROM 
	cte,
	  (SELECT 
    	DISTINCT users_table.user_id 
     FROM 
     	users_table, events_table
     WHERE 
     	users_table.user_id = events_table.user_id AND 
     event_type IN (1,2,3,4)
     ORDER BY 1 DESC LIMIT 5
     ) as foo 
	  WHERE foo.user_id = cte.user_id

) as foo, users_table WHERE foo.cnt > users_table.value_2 
ORDER BY 3 DESC, 1 DESC, 2 DESC, 4 DESC
LIMIT 5;

-- recursive CTES are not supported inside subqueries as well
SELECT
   bar.user_id
FROM
    (
	     WITH RECURSIVE cte AS (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	     ) SELECT * FROM cte ORDER BY 1 DESC
     ) as foo, 
    (
	    SELECT 
	    	DISTINCT users_table.user_id 
	     FROM 
	     	users_table, events_table 
	     WHERE 
	     	users_table.user_id = events_table.user_id AND 
	     event_type IN (1,2,3,4)
	    
     ) as bar  
WHERE foo.user_id = bar.user_id;

-- We error-out when there's an error in execution of the query. By repeating it
-- multiple times, we increase the chance of this test failing before PR #1903.
SET client_min_messages TO ERROR;
DO $$
DECLARE
	errors_received INTEGER;
BEGIN
errors_received := 0;
FOR i IN 1..3 LOOP
	BEGIN
		WITH cte as (
			SELECT
				user_id, value_2
			from
				events_table
		)
		SELECT * FROM users_table where value_2 < (
			SELECT
				min(cte.value_2)
			FROM
				cte
			WHERE
				users_table.user_id=cte.user_id
			GROUP BY
				user_id, cte.value_2);
	EXCEPTION WHEN OTHERS THEN
		IF SQLERRM LIKE 'failed to execute task%' THEN
			errors_received := errors_received + 1;
		END IF;
	END;
END LOOP;
RAISE '(%/3) failed to execute one of the tasks', errors_received;
END;
$$;

SET client_min_messages TO DEFAULT;

DROP SCHEMA subquery_and_ctes CASCADE;
SET search_path TO public;
