-- ===================================================================
-- test recursive planning functionality with complex target entries 
-- and some utilities
-- ===================================================================
CREATE SCHEMA subquery_complex;
SET search_path TO subquery_complex, public;

SET client_min_messages TO DEBUG1;

-- COUNT DISTINCT at the top level query
SELECT
  event_type, count(distinct value_2)
FROM
  events_table
WHERE
  user_id IN (SELECT user_id FROM users_table GROUP BY user_id ORDER BY count(*) DESC LIMIT 20) 
GROUP BY
  event_type
ORDER BY 1 DESC, 2 DESC
LIMIT 3;

-- column renaming in a subquery
SELECT *
FROM
	(
	SELECT user_id, value_1, value_2 FROM users_table OFFSET 0
	) as foo(x, y)
ORDER BY 1 DESC, 2 DESC, 3 DESC LIMIT 5;

-- aggregate distinct in the subqueries
	-- avg distinct on partition key
	-- count distinct on partition key
	-- count distinct on non-partition key
	-- sum distinct on non-partition key when group by is partition key
	-- and the final query is real-time query
SELECT
   DISTINCT ON (avg) avg, cnt_1, cnt_2, sum
FROM
    (
    	SELECT avg(distinct user_id) as avg FROM users_table ORDER BY 1 DESC LIMIT 3
     ) as foo,
    (
    	SELECT count(distinct user_id) as cnt_1 FROM users_table ORDER BY 1 DESC LIMIT 3
    ) as bar,
	(
    	SELECT count(distinct value_2) as cnt_2 FROM users_table ORDER BY 1 DESC LIMIT 4
    ) as baz,
	(
		SELECT user_id, sum(distinct value_2) as sum FROM users_table GROUP BY user_id ORDER BY 1 DESC LIMIT 4   
    ) as bat, events_table 
    WHERE foo.avg != bar.cnt_1 AND baz.cnt_2 = events_table.event_type
    ORDER BY 1 DESC;

-- Aggregate type conversions inside the subqueries
SELECT
   *
FROM
    (
    	SELECT 
    		min(user_id) * 2, max(user_id) / 2, sum(user_id), count(user_id)::float, avg(user_id)::bigint
    	FROM 
    		users_table 
    	ORDER BY 1 DESC 
    	LIMIT 3
    ) as foo,
    (
    	   SELECT 
    		min(value_3) * 2, max(value_3) / 2, sum(value_3), count(value_3), avg(value_3)
    	FROM 
    		users_table 
    	ORDER BY 1 DESC 
    	LIMIT 3
    ) as bar,
	(
    	SELECT 
    		min(time), max(time), count(time), 
    		count(*) FILTER (WHERE user_id = 3) as cnt_with_filter, 
    		count(*) FILTER (WHERE user_id::text LIKE '%3%') as cnt_with_filter_2
    	FROM 
    		users_table 
    	ORDER BY 1 DESC 
    	LIMIT 3
    ) as baz
    ORDER BY 1 DESC;

-- Expressions inside the aggregates
-- parts of the query is inspired by TPCH queries
SELECT
   DISTINCT ON (avg) avg, cnt_1, cnt_2, cnt_3, sum_1,l_year, pos, count_pay
FROM
    (
    	SELECT avg(user_id * (5.0 / (value_1 + 0.1))) as avg FROM users_table ORDER BY 1 DESC LIMIT 3
     ) as foo,
    (
    	SELECT sum(user_id * (5.0 / (value_1 + value_2 + 0.1)) * value_3) as cnt_1 FROM users_table ORDER BY 1 DESC LIMIT 3
    ) as bar,
	(
    	SELECT     
    		avg(case
            	when user_id > 4
            	then value_1
        	end) as cnt_2, 
    		avg(case
            	when user_id > 500
            	then value_1
        	end) as cnt_3,
    		sum(case
				when value_1 = 1
			 	OR value_2 = 1
				then 1
				else 0
			end) as sum_1,
			extract(year FROM max(time)) as l_year,
			strpos(max(user_id)::text, '1') as pos
         FROM 
         		users_table 
         ORDER BY 
         	1 DESC 
         LIMIT 4
    ) as baz, 
	(
		SELECT  COALESCE(value_3, 20) AS count_pay FROM users_table ORDER BY 1 OFFSET 20 LIMIT 5
	) as tar,
    events_table 
    WHERE foo.avg != bar.cnt_1 AND baz.cnt_2 != events_table.event_type
    ORDER BY 1 DESC;

-- Multiple columns in GROUP BYs
-- foo needs to be recursively planned, bar can be pushded down
SELECT
   DISTINCT ON (avg) avg, avg2
FROM
    (
    	SELECT avg(value_3) as avg FROM users_table GROUP BY value_1, value_2
     ) as foo,
    (
    	SELECT avg(value_3) as avg2 FROM users_table GROUP BY value_1, value_2, user_id
     ) as bar
    WHERE foo.avg = bar.avg2
    ORDER BY 1 DESC, 2 DESC
    LIMIT 3;

-- HAVING and ORDER BY tests
SELECT a.user_id, b.value_2, c.avg
FROM (
         SELECT
         	user_id
    	 FROM
         	users_table
         WHERE
           	(value_1 > 2)
          GROUP BY
      			user_id
           HAVING
      		count(distinct value_1) > 2
      	   ORDER BY 1 DESC
      	   LIMIT 3
        ) as a,
		(
         SELECT
         	value_2
    	 FROM
         	users_table
         WHERE
           	(value_1 > 2)
          GROUP BY
      			value_2
           HAVING
      		count(distinct value_1) > 2
      	   ORDER BY 1 DESC
      	   LIMIT 3
        ) as b,
       	(
         SELECT
         	avg(user_id) as avg
    	 FROM
         	users_table
         WHERE
           	(value_1 > 2)
          GROUP BY
      			value_2
           HAVING
      		sum(value_1) > 10
      	   ORDER BY (sum(value_3) - avg(value_1) - COALESCE(array_upper(ARRAY[max(user_id)],1) * 5,0)) DESC
      	   LIMIT 3
        ) as c 

        WHERE b.value_2 != a.user_id
        ORDER BY 3 DESC, 2 DESC, 1 DESC
        LIMIT 5;

-- zero shard subquery joined with a regular one
SELECT
   bar.user_id
FROM
    (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,
    (SELECT 
    	DISTINCT users_table.user_id 
     FROM 
     	users_table, events_table 
     WHERE 
     	users_table.user_id = events_table.user_id AND false AND
     event_type IN (1,2,3,4)
     ORDER BY 1 DESC LIMIT 5
     ) as bar
    WHERE foo.user_id > bar.user_id
    ORDER BY 1 DESC;

-- window functions tests, both is recursively planned
SELECT * FROM 
(
	SELECT
	   user_id, time, rnk
	FROM
	(
		SELECT * FROM (
	  SELECT
	    *, rank() OVER my_win as rnk
	  FROM
	    events_table
	    WINDOW my_win AS (PARTITION BY user_id ORDER BY time DESC)
	    ORDER BY rnk DESC
		) as foo_inner

	   LIMIT 4
	) as foo
	ORDER BY
	  3 DESC, 1 DESC, 2 DESC
) foo,
(
	SELECT
	   user_id, time, rnk
	FROM
	(
	  SELECT
	    *, rank() OVER my_win as rnk
	  FROM
	    events_table
	  WHERE 
	   	user_id = 3
	  WINDOW my_win AS (PARTITION BY event_type ORDER BY time DESC)

	) as foo
	ORDER BY
	  3 DESC, 1 DESC, 2 DESC
) bar WHERE foo.user_id = bar.user_id
ORDER BY foo.rnk DESC, foo.time DESC, bar.time LIMIT 5;

-- cursor test
BEGIN;
	
	DECLARE recursive_subquery CURSOR FOR     
	SELECT
	  event_type, count(distinct value_2)
	FROM
	  events_table
	WHERE
	  user_id IN (SELECT user_id FROM users_table GROUP BY user_id ORDER BY count(*) DESC LIMIT 20) 
	GROUP BY
	  event_type
	ORDER BY 1 DESC, 2 DESC
	LIMIT 3;

	FETCH 1 FROM recursive_subquery;
	FETCH 1 FROM recursive_subquery;
	FETCH 1 FROM recursive_subquery;
	FETCH 1 FROM recursive_subquery;
COMMIT;

-- cursor test with FETCH ALL
BEGIN;
	
	DECLARE recursive_subquery CURSOR FOR     
	SELECT
	  event_type, count(distinct value_2)
	FROM
	  events_table
	WHERE
	  user_id IN (SELECT user_id FROM users_table GROUP BY user_id ORDER BY count(*) DESC LIMIT 20) 
	GROUP BY
	  event_type
	ORDER BY 1 DESC, 2 DESC
	LIMIT 3;

	FETCH ALL FROM recursive_subquery;
	FETCH ALL FROM recursive_subquery;
COMMIT;

SET client_min_messages TO DEFAULT;

DROP SCHEMA subquery_complex CASCADE;
SET search_path TO public;
