-- Test the basic CTE functionality and expected error messages
SET search_path TO 'with_basics';
SET citus.coordinator_aggregation_strategy TO 'disabled';
CREATE TYPE with_basics.xy AS (x int, y int);
-- CTEs in FROM should work
WITH cte AS (
	SELECT user_id, value_2 from users_table WHERE user_id IN (1, 2) ORDER BY 1,2 LIMIT 5
)
SELECT * FROM cte;
 user_id | value_2
---------------------------------------------------------------------
       1 |       0
       1 |       2
       1 |       3
       1 |       3
       1 |       4
(5 rows)

-- CTEs in WHERE should work
WITH cte AS (
	SELECT user_id from users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  value_2
FROM
  users_table
WHERE
  user_id IN (SELECT user_id FROM users_table)
ORDER BY
  value_2
LIMIT
  5;
 value_2
---------------------------------------------------------------------
       0
       0
       0
       0
       0
(5 rows)

-- nested CTEs should work
WITH cte_1 AS (
	WITH cte_1_1 AS (
	    SELECT user_id, value_2 from users_table WHERE user_id IN (1, 2) ORDER BY 2 LIMIT 5
	),
	cte_1_2 AS (
		SELECT max(user_id) AS user_id FROM cte_1_1
	)
	SELECT user_id FROM cte_1_2 ORDER BY user_id
)
SELECT value_2 FROM users_table WHERE user_id IN (SELECT user_id FROM cte_1) ORDER BY value_2 LIMIT 1;
 value_2
---------------------------------------------------------------------
       0
(1 row)

-- Mix of FROM/WHERE queries
WITH cte_from AS (
	SELECT max(user_id) AS user_id, value_2, value_1 FROM users_table GROUP BY value_2, value_1
),
cte_where AS (
	SELECT value_2 FROM events_table
)
SELECT
  *
FROM
  (SELECT max(user_id), max(value_2) AS value_2 FROM cte_from GROUP BY value_1) f
WHERE
  value_2 IN (SELECT * FROM cte_where)
ORDER BY
  1, 2
LIMIT
  5;
 max | value_2
---------------------------------------------------------------------
   5 |       5
   6 |       5
   6 |       5
   6 |       5
   6 |       5
(5 rows)

-- CTE in subquery recursively planned
SELECT user_id FROM (
  WITH cte AS (
    SELECT user_id, value_2 from users_table WHERE user_id IN (1, 2) ORDER BY 2 LIMIT 5
  )
  SELECT user_id FROM cte WHERE value_2 > 0
) a ORDER BY 1 LIMIT 3;
 user_id
---------------------------------------------------------------------
       2
       2
       2
(3 rows)

-- CTE outside of FROM/WHERE errors out
WITH cte AS (
	SELECT user_id FROM users_table WHERE value_2 IN (1, 2)
)
SELECT (SELECT * FROM cte);
ERROR:  more than one row returned by a subquery used as an expression
WITH cte_basic AS (
	SELECT user_id FROM users_table WHERE user_id = 1
)
SELECT
  (SELECT user_id FROM cte_basic), user_id
FROM
  users_table;
ERROR:  could not run distributed query with subquery outside the FROM, WHERE and HAVING clauses
HINT:  Consider using an equality filter on the distributed table's partition column.
-- single-row sublink is acceptable when there is no FROM
WITH cte AS (
	SELECT user_id FROM users_table WHERE value_2 IN (1, 2)
)
SELECT (SELECT * FROM cte ORDER BY 1 LIMIT 1);
 user_id
---------------------------------------------------------------------
       1
(1 row)

-- group by partition column
WITH series AS (
  SELECT s AS once, s*2 AS twice FROM generate_series(1,10) s
)
SELECT user_id, count(*)
FROM
  users_table
JOIN
  series ON (user_id = once)
GROUP BY
  user_id
ORDER BY
  1, 2
LIMIT 5;
 user_id | count
---------------------------------------------------------------------
       1 |     7
       2 |    18
       3 |    17
       4 |    23
       5 |    26
(5 rows)

-- group by non-partition column
WITH series AS (
  SELECT s AS once, s*2 AS twice FROM generate_series(1,10) s
)
SELECT
  twice, min(user_id)
FROM
  users_table
JOIN
  series ON (user_id = once)
GROUP BY
  twice
HAVING
  twice > 5
ORDER BY
  1, 2
LIMIT 5;
 twice | min
---------------------------------------------------------------------
     6 |   3
     8 |   4
    10 |   5
    12 |   6
(4 rows)

-- distinct in subquery on CTE
WITH one_user AS (
	SELECT user_id from users_table WHERE user_id = 1
)
SELECT
  user_id
FROM
  users_table
WHERE
  value_2 IN (SELECT DISTINCT user_id FROM one_user)
ORDER BY
  user_id
LIMIT
  1;
 user_id
---------------------------------------------------------------------
       2
(1 row)

-- having in subquery on CTE
WITH one_user AS (
	SELECT user_id from users_table WHERE user_id = 1
)
SELECT
  user_id
FROM
  users_table
WHERE
  value_2 IN (SELECT user_id FROM one_user GROUP BY user_id HAVING count(*) > 0)
ORDER BY
  user_id
LIMIT
  1;
 user_id
---------------------------------------------------------------------
       2
(1 row)

-- aggregate in subquery on CTE
WITH top_users AS (
	SELECT user_id, value_2 FROM users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  user_id
FROM
  (SELECT min(user_id) AS user_id FROM top_users) top_users
JOIN
  users_table USING (user_id);
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
       6
       6
       6
       6
       6
(10 rows)

-- column aliases in CTE
WITH top_ten(id, val1) AS (
	SELECT user_id, value_1 FROM users_table ORDER BY value_1 DESC, user_id DESC LIMIT 10
)
SELECT * FROM top_ten;
 id | val1
---------------------------------------------------------------------
  6 |    5
  6 |    5
  5 |    5
  5 |    5
  5 |    5
  4 |    5
  4 |    5
  3 |    5
  1 |    5
  5 |    4
(10 rows)

-- verify old name is not valid anymore
WITH top_ten(id, val1) AS (
	SELECT user_id, value_1 FROM users_table ORDER BY value_1 DESC, user_id DESC LIMIT 10
)
SELECT * FROM top_ten ORDER BY user_id DESC;
ERROR:  column "user_id" does not exist
-- verify original name is used if alias is missing
WITH top_ten(id) AS (
	SELECT user_id, value_1 FROM users_table ORDER BY value_1 DESC, user_id DESC LIMIT 10
)
SELECT * FROM top_ten ORDER BY value_1 DESC;
 id | value_1
---------------------------------------------------------------------
  6 |       5
  6 |       5
  5 |       5
  5 |       5
  5 |       5
  4 |       5
  4 |       5
  3 |       5
  1 |       5
  5 |       4
(10 rows)

-- computed targets from columns also work
WITH top_ten(id, val, val_mul, val_sum) AS (
	SELECT user_id, value_1, value_1*2, value_1 + value_2 FROM users_table ORDER BY value_1 DESC, user_id DESC, value_2 DESC LIMIT 10
)
SELECT * FROM top_ten ORDER BY id DESC, val_mul DESC, (val_sum + 1) DESC;
 id | val | val_mul | val_sum
---------------------------------------------------------------------
  6 |   5 |      10 |       7
  6 |   5 |      10 |       5
  5 |   5 |      10 |      10
  5 |   5 |      10 |       7
  5 |   5 |      10 |       6
  5 |   4 |       8 |       7
  4 |   5 |      10 |       9
  4 |   5 |      10 |       8
  3 |   5 |      10 |      10
  1 |   5 |      10 |       9
(10 rows)

-- computed targets from columns in outer query
WITH top_ten(id, val, val_mul, val_sum) AS (
	SELECT user_id, value_1, value_1*2, value_1 + value_2 FROM users_table ORDER BY value_1 DESC, value_2 DESC, user_id DESC LIMIT 10
)
SELECT id, val, id * val, val_sum * 2, val_sum + val_sum FROM top_ten ORDER BY 1 DESC, 2 DESC, 3 DESC, 4 DESC;
 id | val | ?column? | ?column? | ?column?
---------------------------------------------------------------------
  6 |   5 |       30 |       14 |       14
  6 |   5 |       30 |       10 |       10
  5 |   5 |       25 |       20 |       20
  5 |   5 |       25 |       14 |       14
  5 |   5 |       25 |       12 |       12
  4 |   5 |       20 |       18 |       18
  4 |   5 |       20 |       16 |       16
  4 |   4 |       16 |       18 |       18
  3 |   5 |       15 |       20 |       20
  1 |   5 |        5 |       18 |       18
(10 rows)

-- computed targets from columns and group by outside
WITH top_ten(id, val, val_mul, val_sum) AS (
	SELECT user_id, value_1, value_1*2, value_1 + value_2 FROM users_table ORDER BY value_1 DESC, user_id DESC
)
SELECT id, count(*), avg(val), max(val_mul), min(val_sum) FROM top_ten
GROUP BY id
ORDER BY 2 DESC, 1 DESC;
 id | count |        avg         | max | min
---------------------------------------------------------------------
  5 |    26 | 2.6538461538461538 |  10 |   2
  4 |    23 | 2.7391304347826087 |  10 |   0
  2 |    18 | 2.3333333333333333 |   8 |   1
  3 |    17 | 2.3529411764705882 |  10 |   2
  6 |    10 | 2.1000000000000000 |  10 |   0
  1 |     7 | 3.2857142857142857 |  10 |   4
(6 rows)

-- FOR UPDATE in subquery on CTE
WITH top_users AS (
	SELECT user_id, value_2 FROM users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users FOR UPDATE) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
(5 rows)

-- LIMIT in subquery on CTE
WITH top_users AS (
	SELECT user_id, value_2 FROM users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users LIMIT 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
(5 rows)

-- OFFSET in subquery on CTE
WITH top_users AS (
	SELECT user_id, value_2 FROM users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users OFFSET 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
(5 rows)

-- non-equi joins in CTE are supported if accompanied with an equi-join
WITH top_users AS (
	SELECT DISTINCT e.user_id FROM users_table u JOIN events_table e ON (u.user_id = e.user_id AND u.value_1 > e.value_2)
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users WHERE user_id > 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
(5 rows)

-- Join can be supported with another CTE
WITH events_table AS (
  SELECT * FROM events_table
),
top_users AS (
  SELECT DISTINCT e.user_id FROM users_table u JOIN events_table e ON (u.user_id = e.user_id AND u.value_1 > e.value_2)
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users WHERE user_id > 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       6
       6
       6
       6
       6
(5 rows)

-- Window functions in CTE
WITH top_users AS (
	SELECT row_number() OVER(), user_id FROM users_table ORDER BY user_id DESC LIMIT 10
)
SELECT
  user_id
FROM
  (SELECT user_id FROM top_users WHERE user_id > 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
ERROR:  could not run distributed query because the window function that is used cannot be pushed down
HINT:  Window functions are supported in two ways. Either add an equality filter on the distributed tables' partition column or use the window functions with a PARTITION BY clause containing the distribution column
-- Window functions that partition by the distribution column in subqueries in CTEs are ok
WITH top_users AS
  (SELECT *
   FROM
     (SELECT row_number() OVER(PARTITION BY user_id) AS row_number,
                          user_id
      FROM users_table) AS foo
)
SELECT user_id
FROM
  (SELECT user_id
   FROM top_users
   WHERE row_number > 5) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       1
       1
       1
       1
       1
(5 rows)

-- Unsupported aggregate in CTE
WITH top_users AS (
	SELECT array_agg(user_id ORDER BY value_2) user_ids FROM users_table
)
SELECT
  user_id
FROM
  (SELECT unnest(user_ids) user_id FROM top_users) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
ERROR:  array_agg with order by is unsupported
-- array_agg in CTE
WITH top_users AS (
	SELECT array_agg(user_id) user_ids FROM users_table
)
SELECT
  user_id
FROM
  (SELECT unnest(user_ids) user_id FROM top_users) top_users
JOIN
  users_table USING (user_id)
ORDER BY
  user_id
LIMIT
  5;
 user_id
---------------------------------------------------------------------
       1
       1
       1
       1
       1
(5 rows)

-- composite type array
WITH top_users AS (
	SELECT array_agg((value_1,value_2)::xy) AS p FROM users_table WHERE user_id % 2 = 0
)
SELECT
  e.user_id, sum(y)
FROM
  (SELECT (unnest(p)).* FROM top_users) tops
JOIN
  events_table e ON (tops.x = e.user_id)
GROUP BY
  e.user_id
ORDER BY
  2 DESC, 1
LIMIT
  5;
 user_id | sum
---------------------------------------------------------------------
       3 | 651
       2 | 552
       4 | 544
       5 | 126
       1 |  90
(5 rows)

-- SELECT * FROM (SELECT * FROM cte UNION SELECT * FROM distributed_table) a; should work
WITH cte AS (
	SELECT * FROM users_table
)
SELECT * FROM (
	SELECT * FROM cte UNION (SELECT * FROM events_table)
	) a
ORDER BY
	1,2,3,4,5,6
LIMIT
	10;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Wed Nov 22 18:49:42.327403 2017 |       3 |       2 |       1 |
       1 | Wed Nov 22 19:03:01.772353 2017 |       4 |       1 |       2 |
       1 | Wed Nov 22 19:07:03.846437 2017 |       1 |       2 |       5 |
       1 | Wed Nov 22 20:56:21.122638 2017 |       2 |       4 |       4 |
       1 | Wed Nov 22 21:06:57.457147 2017 |       4 |       3 |       2 |
       1 | Wed Nov 22 21:47:04.188168 2017 |       4 |       2 |       0 |
       1 | Wed Nov 22 22:51:43.132261 2017 |       4 |       0 |       3 |
       1 | Wed Nov 22 23:22:09.957743 2017 |       1 |       1 |       1 |
       1 | Thu Nov 23 00:42:37.237615 2017 |       2 |       4 |       3 |
       1 | Thu Nov 23 02:59:23.620864 2017 |       4 |       5 |       4 |
(10 rows)

SELECT * FROM (
	SELECT * FROM (WITH cte AS (
			SELECT * FROM users_table
		)
		SELECT * FROM cte
	)b UNION (SELECT * FROM events_table)) a
ORDER BY
1,2,3,4,5,6
LIMIT
10;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Wed Nov 22 18:49:42.327403 2017 |       3 |       2 |       1 |
       1 | Wed Nov 22 19:03:01.772353 2017 |       4 |       1 |       2 |
       1 | Wed Nov 22 19:07:03.846437 2017 |       1 |       2 |       5 |
       1 | Wed Nov 22 20:56:21.122638 2017 |       2 |       4 |       4 |
       1 | Wed Nov 22 21:06:57.457147 2017 |       4 |       3 |       2 |
       1 | Wed Nov 22 21:47:04.188168 2017 |       4 |       2 |       0 |
       1 | Wed Nov 22 22:51:43.132261 2017 |       4 |       0 |       3 |
       1 | Wed Nov 22 23:22:09.957743 2017 |       1 |       1 |       1 |
       1 | Thu Nov 23 00:42:37.237615 2017 |       2 |       4 |       3 |
       1 | Thu Nov 23 02:59:23.620864 2017 |       4 |       5 |       4 |
(10 rows)

-- SELECT * FROM (SELECT * FROM cte UNION SELECT * FROM cte) a; should work
WITH cte AS (
	SELECT * FROM users_table WHERE user_id IN (1, 2)
)
SELECT
  *
FROM
  (SELECT * FROM cte UNION (SELECT * FROM cte)) a
ORDER BY
  1,2,3,4,5,6
LIMIT
  5;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Wed Nov 22 22:51:43.132261 2017 |       4 |       0 |       3 |
       1 | Thu Nov 23 03:32:50.803031 2017 |       3 |       2 |       1 |
       1 | Thu Nov 23 09:26:42.145043 2017 |       1 |       3 |       3 |
       1 | Thu Nov 23 11:11:24.40789 2017  |       3 |       4 |       0 |
       1 | Thu Nov 23 11:44:57.515981 2017 |       4 |       3 |       4 |
(5 rows)

WITH cte AS (
	SELECT * FROM users_table WHERE user_id IN (1, 2) ORDER BY 1,2,3 LIMIT 5
),
cte_2 AS (
	SELECT * FROM users_table WHERE user_id  IN (3, 4) ORDER BY 1,2,3 LIMIT 5
)
SELECT * FROM cte UNION ALL SELECT * FROM cte_2;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Wed Nov 22 22:51:43.132261 2017 |       4 |       0 |       3 |
       1 | Thu Nov 23 03:32:50.803031 2017 |       3 |       2 |       1 |
       1 | Thu Nov 23 09:26:42.145043 2017 |       1 |       3 |       3 |
       1 | Thu Nov 23 11:11:24.40789 2017  |       3 |       4 |       0 |
       1 | Thu Nov 23 11:44:57.515981 2017 |       4 |       3 |       4 |
       3 | Wed Nov 22 18:43:51.450263 2017 |       1 |       1 |       4 |
       3 | Wed Nov 22 20:43:31.008625 2017 |       1 |       3 |       2 |
       3 | Wed Nov 22 23:24:32.080584 2017 |       3 |       2 |       5 |
       3 | Thu Nov 23 00:15:45.610845 2017 |       1 |       1 |       4 |
       3 | Thu Nov 23 03:23:24.702501 2017 |       1 |       2 |       5 |
(10 rows)

-- basic recursive CTE which should all error out
WITH RECURSIVE basic_recursive(x) AS (
    VALUES (1)
  UNION ALL
    SELECT user_id + 1 FROM users_table JOIN basic_recursive ON (user_id = x) WHERE user_id < 100
)
SELECT sum(x) FROM basic_recursive;
ERROR:  recursive CTEs are not supported in distributed queries
WITH RECURSIVE basic_recursive AS (
    SELECT -1 as user_id, '2017-11-22 20:16:16.614779'::timestamp, -1, -1, -1, -1
  UNION ALL
    SELECT basic_recursive.* FROM users_table JOIN basic_recursive USING (user_id) WHERE user_id>1
)
SELECT * FROM basic_recursive ORDER BY user_id LIMIT 1;
ERROR:  recursive CTEs are not supported in distributed queries
-- basic_recursive in FROM should error out
SELECT
  *
FROM
(WITH RECURSIVE basic_recursive AS (
      SELECT -1 as user_id, '2017-11-22 20:16:16.614779'::timestamp, -1, -1, -1, -1
    UNION ALL
      SELECT basic_recursive.* FROM users_table JOIN basic_recursive USING (user_id) WHERE user_id>1
  )
  SELECT * FROM basic_recursive ORDER BY user_id LIMIT 1) cte_rec;
ERROR:  recursive CTEs are not supported in distributed queries
-- basic_recursive in WHERE with UNION ALL
SELECT
  *
FROM
  users_table
WHERE
  user_id in
(WITH RECURSIVE basic_recursive AS (
      SELECT -1 as user_id
    UNION ALL
      SELECT basic_recursive.* FROM users_table JOIN basic_recursive USING (user_id) WHERE user_id>1
  )
  SELECT * FROM basic_recursive ORDER BY user_id LIMIT 1);
ERROR:  recursive CTEs are not supported in distributed queries
-- one recursive one regular CTE should error out
WITH RECURSIVE basic_recursive(x) AS(
    VALUES (1)
  UNION ALL
    SELECT user_id + 1 FROM users_table JOIN basic_recursive ON (user_id = x) WHERE user_id < 100
),
basic AS (
    SELECT count(user_id) FROM users_table
)
SELECT x FROM basic, basic_recursive;
ERROR:  recursive CTEs are not supported in distributed queries
-- one recursive one regular which SELECTs from the recursive CTE under a simple SELECT
WITH RECURSIVE basic_recursive(x) AS(
    VALUES (1)
  UNION ALL
    SELECT user_id + 1 FROM users_table JOIN basic_recursive ON (user_id = x) WHERE user_id < 100
),
basic AS (
    SELECT count(x) FROM basic_recursive
)
SELECT * FROM basic;
ERROR:  recursive CTEs are not supported in distributed queries
-- recursive CTE in a NESTED manner
WITH regular_cte AS (
  WITH regular_2 AS (
    WITH RECURSIVE recursive(x) AS (
        VALUES (1)
      UNION ALL
        SELECT user_id + 1 FROM users_table JOIN recursive ON (user_id = x) WHERE user_id < 100
    )
    SELECT * FROM recursive
  )
  SELECT * FROM regular_2
)
SELECT * FROM regular_cte;
ERROR:  recursive CTEs are not supported in distributed queries
-- CTEs should work with VIEWs as well
CREATE VIEW basic_view AS
SELECT * FROM users_table;
CREATE VIEW cte_view AS
WITH cte AS (
	SELECT * FROM users_table
)
SELECT user_id, max(value_1) as value_1 FROM cte GROUP BY 1;
WITH cte_user AS (
	SELECT basic_view.user_id,events_table.value_2 FROM basic_view join events_table on (basic_view.user_id = events_table.user_id)
)
SELECT user_id, sum(value_2) FROM cte_user GROUP BY 1 ORDER BY 1, 2;
 user_id | sum
---------------------------------------------------------------------
       1 |  294
       2 | 1026
       3 |  782
       4 |  943
       5 |  806
       6 |  220
(6 rows)

SELECT * FROM cte_view ORDER BY 1, 2 LIMIT 5;
 user_id | value_1
---------------------------------------------------------------------
       1 |       5
       2 |       4
       3 |       5
       4 |       5
       5 |       5
(5 rows)

WITH cte_user_with_view AS
(
	SELECT * FROM cte_view WHERE user_id < 3
)
SELECT user_id, value_1 FROM cte_user_with_view ORDER BY 1, 2 LIMIT 10 OFFSET 2;
 user_id | value_1
---------------------------------------------------------------------
(0 rows)

DROP VIEW basic_view;
DROP VIEW cte_view;
DROP SCHEMA with_basics CASCADE;
NOTICE:  drop cascades to 3 other objects
DETAIL:  drop cascades to table users_table
drop cascades to table events_table
drop cascades to type xy
