--
-- MULTI_VIEW
--
-- This file contains test cases for view support. It verifies various
-- Citus features: simple selects, aggregates, joins, outer joins
-- router queries, single row inserts, multi row inserts via insert
-- into select, multi row insert via copy commands.
SELECT count(*) FROM lineitem_hash_part;
 count
---------------------------------------------------------------------
 12000
(1 row)

SELECT count(*) FROM orders_hash_part;
 count
---------------------------------------------------------------------
  2985
(1 row)

-- create a view for priority orders
CREATE VIEW priority_orders AS SELECT * FROM orders_hash_part WHERE o_orderpriority < '3-MEDIUM';
-- aggregate pushdown
SELECT o_orderpriority, count(*)  FROM priority_orders GROUP BY 1 ORDER BY 2, 1;
 o_orderpriority | count
---------------------------------------------------------------------
 2-HIGH          |   593
 1-URGENT        |   604
(2 rows)

SELECT o_orderpriority, count(*) FROM orders_hash_part  WHERE o_orderpriority < '3-MEDIUM' GROUP BY 1 ORDER BY 2,1;
 o_orderpriority | count
---------------------------------------------------------------------
 2-HIGH          |   593
 1-URGENT        |   604
(2 rows)

-- filters
SELECT o_orderpriority, count(*) as all, count(*) FILTER (WHERE o_orderstatus ='F') as fullfilled  FROM priority_orders GROUP BY 1 ORDER BY 2, 1;
 o_orderpriority | all | fullfilled
---------------------------------------------------------------------
 2-HIGH          | 593 |        271
 1-URGENT        | 604 |        280
(2 rows)

-- having
SELECT o_orderdate, count(*) from priority_orders group by 1 having (count(*) > 3)  order by 2 desc, 1 desc;
 o_orderdate | count
---------------------------------------------------------------------
 08-20-1996  |     5
 10-10-1994  |     4
 05-05-1994  |     4
 04-07-1994  |     4
 03-17-1993  |     4
(5 rows)

-- having with filters
SELECT o_orderdate, count(*) as all, count(*) FILTER(WHERE o_orderstatus = 'F') from priority_orders group by 1 having (count(*) > 3)  order by 2 desc, 1 desc;
 o_orderdate | all | count
---------------------------------------------------------------------
 08-20-1996  |   5 |     0
 10-10-1994  |   4 |     4
 05-05-1994  |   4 |     4
 04-07-1994  |   4 |     4
 03-17-1993  |   4 |     4
(5 rows)

-- limit
SELECT o_orderkey, o_totalprice from orders_hash_part order by 2 desc, 1 asc limit 5 ;
 o_orderkey | o_totalprice
---------------------------------------------------------------------
       4421 |    401055.62
      10209 |    400191.77
      11142 |    395039.05
      14179 |    384265.43
      11296 |    378166.33
(5 rows)

SELECT o_orderkey, o_totalprice from priority_orders order by 2 desc, 1 asc limit 1 ;
 o_orderkey | o_totalprice
---------------------------------------------------------------------
      14179 |    384265.43
(1 row)

CREATE VIEW priority_lineitem AS SELECT li.* FROM lineitem_hash_part li JOIN priority_orders ON (l_orderkey = o_orderkey);
SELECT l_orderkey, count(*) FROM priority_lineitem GROUP BY 1 ORDER BY 2 DESC, 1 LIMIT 5;
 l_orderkey | count
---------------------------------------------------------------------
          7 |     7
        225 |     7
        226 |     7
        322 |     7
        326 |     7
(5 rows)

CREATE VIEW air_shipped_lineitems AS SELECT * FROM lineitem_hash_part WHERE l_shipmode = 'AIR';
-- join between view and table
SELECT count(*) FROM orders_hash_part join air_shipped_lineitems ON (o_orderkey = l_orderkey);
 count
---------------------------------------------------------------------
  1706
(1 row)

-- join between views
SELECT count(*) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey);
 count
---------------------------------------------------------------------
   700
(1 row)

-- count distinct on partition column is supported
SELECT count(distinct o_orderkey) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey);
 count
---------------------------------------------------------------------
   551
(1 row)

-- count distinct on non-partition column is supported
SELECT count(distinct o_orderpriority) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey);
 count
---------------------------------------------------------------------
     2
(1 row)

-- count distinct on partition column is supported on router queries
SELECT count(distinct o_orderkey) FROM priority_orders join air_shipped_lineitems
	ON (o_orderkey = l_orderkey)
	WHERE (o_orderkey = 231);
 count
---------------------------------------------------------------------
     1
(1 row)

-- select distinct on router joins of views also works
SELECT distinct(o_orderkey) FROM priority_orders join air_shipped_lineitems
	ON (o_orderkey = l_orderkey)
	WHERE (o_orderkey = 231);
 o_orderkey
---------------------------------------------------------------------
        231
(1 row)

-- left join support depends on flattening of the query
SELECT o_orderkey, l_orderkey FROM priority_orders left join air_shipped_lineitems ON (o_orderkey = l_orderkey) ORDER BY o_orderkey LIMIT 1;
 o_orderkey | l_orderkey
---------------------------------------------------------------------
          2 |
(1 row)

-- however, this works
SELECT count(*) FROM priority_orders left join lineitem_hash_part ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR';
 count
---------------------------------------------------------------------
   700
(1 row)

-- view on the inner side is supported
SELECT count(*) FROM priority_orders right join lineitem_hash_part ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR';
 count
---------------------------------------------------------------------
  1706
(1 row)

-- view on the outer side is supported
SELECT count(*) FROM lineitem_hash_part right join priority_orders ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR';
 count
---------------------------------------------------------------------
   700
(1 row)

-- left join on router query is supported
SELECT o_orderkey, l_linenumber FROM priority_orders left join air_shipped_lineitems ON (o_orderkey = l_orderkey)
	WHERE o_orderkey = 2;
 o_orderkey | l_linenumber
---------------------------------------------------------------------
          2 |
(1 row)

-- repartition query on view join
-- it passes planning, fails at execution stage
SET client_min_messages TO DEBUG1;
SELECT * FROM priority_orders JOIN air_shipped_lineitems ON (o_custkey = l_suppkey) ORDER BY o_orderkey DESC, o_custkey DESC, o_orderpriority DESC LIMIT 5;
DEBUG:  generating subplan XXX_1 for subquery SELECT lineitem_hash_part.l_orderkey, lineitem_hash_part.l_partkey, lineitem_hash_part.l_suppkey, lineitem_hash_part.l_linenumber, lineitem_hash_part.l_quantity, lineitem_hash_part.l_extendedprice, lineitem_hash_part.l_discount, lineitem_hash_part.l_tax, lineitem_hash_part.l_returnflag, lineitem_hash_part.l_linestatus, lineitem_hash_part.l_shipdate, lineitem_hash_part.l_commitdate, lineitem_hash_part.l_receiptdate, lineitem_hash_part.l_shipinstruct, lineitem_hash_part.l_shipmode, lineitem_hash_part.l_comment FROM public.lineitem_hash_part WHERE (lineitem_hash_part.l_shipmode OPERATOR(pg_catalog.=) 'AIR'::bpchar)
DEBUG:  Plan XXX query after replacing subqueries and CTEs: SELECT priority_orders.o_orderkey, priority_orders.o_custkey, priority_orders.o_orderstatus, priority_orders.o_totalprice, priority_orders.o_orderdate, priority_orders.o_orderpriority, priority_orders.o_clerk, priority_orders.o_shippriority, priority_orders.o_comment, air_shipped_lineitems.l_orderkey, air_shipped_lineitems.l_partkey, air_shipped_lineitems.l_suppkey, air_shipped_lineitems.l_linenumber, air_shipped_lineitems.l_quantity, air_shipped_lineitems.l_extendedprice, air_shipped_lineitems.l_discount, air_shipped_lineitems.l_tax, air_shipped_lineitems.l_returnflag, air_shipped_lineitems.l_linestatus, air_shipped_lineitems.l_shipdate, air_shipped_lineitems.l_commitdate, air_shipped_lineitems.l_receiptdate, air_shipped_lineitems.l_shipinstruct, air_shipped_lineitems.l_shipmode, air_shipped_lineitems.l_comment FROM ((SELECT orders_hash_part.o_orderkey, orders_hash_part.o_custkey, orders_hash_part.o_orderstatus, orders_hash_part.o_totalprice, orders_hash_part.o_orderdate, orders_hash_part.o_orderpriority, orders_hash_part.o_clerk, orders_hash_part.o_shippriority, orders_hash_part.o_comment FROM public.orders_hash_part WHERE (orders_hash_part.o_orderpriority OPERATOR(pg_catalog.<) '3-MEDIUM'::bpchar)) priority_orders JOIN (SELECT intermediate_result.l_orderkey, intermediate_result.l_partkey, intermediate_result.l_suppkey, intermediate_result.l_linenumber, intermediate_result.l_quantity, intermediate_result.l_extendedprice, intermediate_result.l_discount, intermediate_result.l_tax, intermediate_result.l_returnflag, intermediate_result.l_linestatus, intermediate_result.l_shipdate, intermediate_result.l_commitdate, intermediate_result.l_receiptdate, intermediate_result.l_shipinstruct, intermediate_result.l_shipmode, intermediate_result.l_comment FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(l_orderkey bigint, l_partkey integer, l_suppkey integer, l_linenumber integer, l_quantity numeric(15,2), l_extendedprice numeric(15,2), l_discount numeric(15,2), l_tax numeric(15,2), l_returnflag character(1), l_linestatus character(1), l_shipdate date, l_commitdate date, l_receiptdate date, l_shipinstruct character(25), l_shipmode character(10), l_comment character varying(44))) air_shipped_lineitems ON ((priority_orders.o_custkey OPERATOR(pg_catalog.=) air_shipped_lineitems.l_suppkey))) ORDER BY priority_orders.o_orderkey DESC, priority_orders.o_custkey DESC, priority_orders.o_orderpriority DESC LIMIT 5
DEBUG:  push down of limit count: 5
 o_orderkey | o_custkey | o_orderstatus | o_totalprice | o_orderdate | o_orderpriority |     o_clerk     | o_shippriority |                       o_comment                       | l_orderkey | l_partkey | l_suppkey | l_linenumber | l_quantity | l_extendedprice | l_discount | l_tax | l_returnflag | l_linestatus | l_shipdate | l_commitdate | l_receiptdate |      l_shipinstruct       | l_shipmode |                 l_comment
---------------------------------------------------------------------
      14821 |      1435 | O             |    322002.95 | 06-12-1998  | 2-HIGH          | Clerk#000000630 |              0 | n packages are furiously ironic ideas. d              |       1607 |    118923 |      1435 |            2 |      37.00 |        71851.04 |       0.05 |  0.02 | N            | O            | 02-27-1996 | 02-18-1996   | 03-16-1996    | NONE                      | AIR        | alongside
      14790 |       613 | O             |    270163.54 | 08-21-1996  | 2-HIGH          | Clerk#000000347 |              0 | p. regular deposits wake. final n                     |       2629 |    123076 |       613 |            2 |      31.00 |        34071.17 |       0.08 |  0.03 | N            | O            | 05-24-1998 | 05-26-1998   | 06-10-1998    | COLLECT COD               | AIR        | ate blithely bold, regular deposits. bold
      14758 |      1225 | F             |     37812.49 | 10-27-1993  | 2-HIGH          | Clerk#000000687 |              0 | ages nag about the furio                              |       9156 |    176190 |      1225 |            2 |      22.00 |        27856.18 |       0.03 |  0.00 | R            | F            | 02-08-1994 | 04-01-1994   | 02-24-1994    | DELIVER IN PERSON         | AIR        | equests dete
      14725 |       569 | O             |    261801.45 | 06-17-1995  | 2-HIGH          | Clerk#000000177 |              0 | ng asymptotes. final, ironic accounts cajole after    |      14688 |    173017 |       569 |            3 |      10.00 |        10900.10 |       0.02 |  0.08 | N            | O            | 03-14-1997 | 04-22-1997   | 04-05-1997    | COLLECT COD               | AIR        | riously even packages sleep a
      14657 |       370 | F             |    116160.53 | 02-28-1994  | 1-URGENT        | Clerk#000000756 |              0 | ly across the ironic, ironic instructions. bold ideas |       5153 |     67863 |       370 |            3 |      30.00 |        54925.80 |       0.09 |  0.01 | N            | O            | 11-10-1995 | 11-14-1995   | 11-16-1995    | DELIVER IN PERSON         | AIR        | beans sleep bl
(5 rows)

RESET client_min_messages;
SELECT count(*) FROM priority_orders JOIN air_shipped_lineitems ON (o_custkey = l_suppkey);
 count
---------------------------------------------------------------------
   192
(1 row)

SET citus.task_executor_type to "task-tracker";
-- single view repartition subqueries are not supported
SELECT l_suppkey, count(*) FROM
	(SELECT l_suppkey, l_shipdate, count(*)
		FROM air_shipped_lineitems GROUP BY l_suppkey, l_shipdate) supps
	GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5;
ERROR:  cannot perform distributed planning on this query
DETAIL:  Subqueries without group by clause are not supported yet
-- logically same query without a view works fine
SELECT l_suppkey, count(*) FROM
	(SELECT l_suppkey, l_shipdate, count(*)
		FROM lineitem_hash_part WHERE l_shipmode = 'AIR' GROUP BY l_suppkey, l_shipdate) supps
	GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5;
 l_suppkey | count
---------------------------------------------------------------------
      7680 |     4
       160 |     3
      1042 |     3
      1318 |     3
      5873 |     3
(5 rows)

-- when a view is replaced by actual query it still fails
SELECT l_suppkey, count(*) FROM
	(SELECT l_suppkey, l_shipdate, count(*)
		FROM (SELECT * FROM lineitem_hash_part WHERE l_shipmode = 'AIR') asi
		GROUP BY l_suppkey, l_shipdate) supps
	GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5;
ERROR:  cannot perform distributed planning on this query
DETAIL:  Subqueries without group by clause are not supported yet
-- repartition query on view with single table subquery
CREATE VIEW supp_count_view AS SELECT * FROM (SELECT l_suppkey, count(*) FROM lineitem_hash_part GROUP BY 1) s1;
SELECT * FROM supp_count_view ORDER BY 2 DESC, 1 LIMIT 10;
 l_suppkey | count
---------------------------------------------------------------------
      6104 |     8
      1868 |     6
      5532 |     6
      5849 |     6
      6169 |     6
      6669 |     6
      6692 |     6
      7703 |     6
      7869 |     6
      8426 |     6
(10 rows)

SET citus.task_executor_type to DEFAULT;
-- create a view with aggregate
CREATE VIEW lineitems_by_shipping_method AS
	SELECT l_shipmode, count(*) as cnt FROM lineitem_hash_part GROUP BY 1;
-- following will be supported via recursive planning
SELECT * FROM  lineitems_by_shipping_method ORDER BY 1,2 LIMIT 5;
 l_shipmode | cnt
---------------------------------------------------------------------
 AIR        | 1706
 FOB        | 1709
 MAIL       | 1739
 RAIL       | 1706
 REG AIR    | 1679
(5 rows)

-- create a view with group by on partition column
CREATE VIEW lineitems_by_orderkey AS
	SELECT
		l_orderkey, count(*)
	FROM
		lineitem_hash_part
	GROUP BY 1;
-- this should work since we're able to push down this query
SELECT * FROM  lineitems_by_orderkey ORDER BY 2 DESC, 1 ASC LIMIT 10;
 l_orderkey | count
---------------------------------------------------------------------
          7 |     7
         68 |     7
        129 |     7
        164 |     7
        194 |     7
        225 |     7
        226 |     7
        322 |     7
        326 |     7
        354 |     7
(10 rows)

-- it would also work since it is made router plannable
SELECT * FROM  lineitems_by_orderkey WHERE l_orderkey = 100;
 l_orderkey | count
---------------------------------------------------------------------
        100 |     5
(1 row)

DROP VIEW supp_count_view;
DROP VIEW lineitems_by_orderkey;
DROP VIEW lineitems_by_shipping_method;
DROP VIEW air_shipped_lineitems;
DROP VIEW priority_lineitem;
DROP VIEW priority_orders;
-- new tests for real time use case including views and subqueries
-- create view to display recent user who has an activity after a timestamp
CREATE VIEW recent_users AS
	SELECT user_id, max(time) as lastseen FROM users_table
	GROUP BY user_id
	HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC;
SELECT * FROM recent_users ORDER BY 2 DESC, 1 DESC;
 user_id |            lastseen
---------------------------------------------------------------------
       1 | Thu Nov 23 17:30:34.635085 2017
       3 | Thu Nov 23 17:18:51.048758 2017
       5 | Thu Nov 23 16:48:32.08896 2017
(3 rows)

-- create a view for recent_events
CREATE VIEW recent_events AS
	SELECT user_id, time FROM events_table
	WHERE time > '2017-11-23 16:20:33.264457'::timestamp;
SELECT count(*) FROM recent_events;
 count
---------------------------------------------------------------------
     6
(1 row)

-- count number of events of recent_users
SELECT count(*) FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id);
 count
---------------------------------------------------------------------
    50
(1 row)

-- count number of events of per recent users order by count
SELECT ru.user_id, count(*)
	FROM recent_users ru
		JOIN events_table et
		ON (ru.user_id = et.user_id)
	GROUP BY ru.user_id
	ORDER BY 2 DESC, 1;
 user_id | count
---------------------------------------------------------------------
       3 |    21
       1 |    15
       5 |    14
(3 rows)

-- the same query with a left join however, it would still generate the same result
SELECT ru.user_id, count(*)
	FROM recent_users ru
		LEFT JOIN events_table et
		ON (ru.user_id = et.user_id)
	GROUP BY ru.user_id
	ORDER BY 2 DESC, 1;
 user_id | count
---------------------------------------------------------------------
       3 |    21
       1 |    15
       5 |    14
(3 rows)

-- query wrapped inside a subquery, it needs another top level order by
SELECT * FROM
	(SELECT ru.user_id, count(*)
		FROM recent_users ru
			JOIN events_table et
			ON (ru.user_id = et.user_id)
		GROUP BY ru.user_id
		ORDER BY 2 DESC, 1) s1
ORDER BY 2 DESC, 1;
 user_id | count
---------------------------------------------------------------------
       3 |    21
       1 |    15
       5 |    14
(3 rows)

-- non-partition key joins are supported inside subquery
-- via pull-push execution
SELECT * FROM
	(SELECT ru.user_id, count(*)
		FROM recent_users ru
			JOIN events_table et
			ON (ru.user_id = et.event_type)
		GROUP BY ru.user_id
		ORDER BY 2 DESC, 1) s1
ORDER BY 2 DESC, 1;
 user_id | count
---------------------------------------------------------------------
       1 |    24
       3 |    23
       5 |     7
(3 rows)

-- join between views
-- recent users who has an event in recent events
SELECT ru.user_id FROM recent_users ru JOIN recent_events re USING(user_id) GROUP BY ru.user_id ORDER BY ru.user_id;
 user_id
---------------------------------------------------------------------
       1
       3
(2 rows)

-- outer join inside a subquery
-- recent_events who are not done by recent users
SELECT count(*) FROM (
	SELECT re.*, ru.user_id AS recent_user
		FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu
	WHERE recent_user IS NULL;
 count
---------------------------------------------------------------------
     2
(1 row)

-- same query with anti-join
SELECT count(*)
	FROM recent_events re LEFT JOIN recent_users ru ON(ru.user_id = re.user_id)
	WHERE ru.user_id IS NULL;
 count
---------------------------------------------------------------------
     2
(1 row)

-- join between view and table
-- users who has recent activity and they have an entry with value_1 is less than 3
SELECT ut.* FROM recent_users ru JOIN users_table ut USING (user_id) WHERE ut.value_1 < 3 ORDER BY 1,2;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Thu Nov 23 09:26:42.145043 2017 |       1 |       3 |       3 |
       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 | Thu Nov 23 00:15:45.610845 2017 |       1 |       1 |       4 |
       3 | Thu Nov 23 03:23:24.702501 2017 |       1 |       2 |       5 |
       3 | Thu Nov 23 06:20:05.854857 2017 |       1 |       4 |       2 |
       3 | Thu Nov 23 09:57:41.540228 2017 |       2 |       2 |       3 |
       3 | Thu Nov 23 11:18:53.114408 2017 |       2 |       2 |       0 |
       3 | Thu Nov 23 12:56:49.29191 2017  |       0 |       5 |       1 |
       3 | Thu Nov 23 17:18:51.048758 2017 |       1 |       5 |       5 |
       5 | Wed Nov 22 20:43:18.667473 2017 |       0 |       3 |       2 |
       5 | Wed Nov 22 21:02:07.575129 2017 |       2 |       0 |       2 |
       5 | Wed Nov 22 22:10:24.315371 2017 |       1 |       2 |       1 |
       5 | Thu Nov 23 00:54:44.192608 2017 |       1 |       3 |       2 |
       5 | Thu Nov 23 07:47:09.542999 2017 |       1 |       4 |       3 |
       5 | Thu Nov 23 09:05:08.53142 2017  |       2 |       2 |       2 |
       5 | Thu Nov 23 09:17:47.706703 2017 |       2 |       5 |       3 |
       5 | Thu Nov 23 10:15:31.764558 2017 |       2 |       2 |       2 |
       5 | Thu Nov 23 14:29:02.557934 2017 |       2 |       1 |       2 |
       5 | Thu Nov 23 15:55:08.493462 2017 |       0 |       3 |       3 |
       5 | Thu Nov 23 16:28:38.455322 2017 |       2 |       5 |       4 |
(21 rows)

-- determine if a recent user has done a given event type or not
SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event
	FROM recent_users ru
	LEFT JOIN events_table et
	ON(ru.user_id = et.user_id AND et.event_type = 6)
	ORDER BY 2 DESC, 1;
 user_id | done_event
---------------------------------------------------------------------
       1 | YES
       3 | NO
       5 | NO
(3 rows)

-- view vs table join wrapped inside a subquery
SELECT * FROM
	(SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event
		FROM recent_users ru
		LEFT JOIN events_table et
		ON(ru.user_id = et.user_id AND et.event_type = 6)
	) s1
ORDER BY 2 DESC, 1;
 user_id | done_event
---------------------------------------------------------------------
       1 | YES
       3 | NO
       5 | NO
(3 rows)

-- event vs table non-partition-key join is not supported
-- given that we cannot recursively plan tables yet
SELECT * FROM
	(SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event
		FROM recent_users ru
		LEFT JOIN events_table et
		ON(ru.user_id = et.event_type)
	) s1
ORDER BY 2 DESC, 1;
ERROR:  cannot pushdown the subquery
DETAIL:  Complex subqueries and CTEs cannot be in the outer part of the outer join
-- create a select only view
CREATE VIEW selected_users AS SELECT * FROM users_table WHERE value_1 >= 1 and value_1 <3;
CREATE VIEW recent_selected_users AS SELECT su.* FROM selected_users su JOIN recent_users ru USING(user_id);
SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER BY 1;
 user_id
---------------------------------------------------------------------
       1
       3
       5
(3 rows)

-- this would be supported when we implement where partition_key in (subquery) support
SELECT et.user_id, et.time FROM events_table et WHERE et.user_id IN (SELECT user_id FROM recent_selected_users) GROUP BY 1,2 ORDER BY 1 DESC,2 DESC LIMIT 5;
 user_id |              time
---------------------------------------------------------------------
       5 | Thu Nov 23 16:11:02.929469 2017
       5 | Thu Nov 23 14:40:40.467511 2017
       5 | Thu Nov 23 14:28:51.833214 2017
       5 | Thu Nov 23 14:23:09.889786 2017
       5 | Thu Nov 23 13:26:45.571108 2017
(5 rows)

-- it is supported when it is a router query
SELECT count(*) FROM events_table et WHERE et.user_id IN (SELECT user_id FROM recent_selected_users WHERE user_id = 1);
 count
---------------------------------------------------------------------
    15
(1 row)

-- union between views is supported through recursive planning
(SELECT user_id FROM recent_users)
UNION
(SELECT user_id FROM selected_users)
ORDER BY 1;
 user_id
---------------------------------------------------------------------
       1
       2
       3
       4
       5
       6
(6 rows)

-- wrapping it inside a SELECT * works
SELECT *
	FROM (
		(SELECT user_id FROM recent_users)
		UNION
		(SELECT user_id FROM selected_users) ) u
	WHERE user_id < 2 AND user_id > 0
	ORDER BY user_id;
 user_id
---------------------------------------------------------------------
       1
(1 row)

-- union all also works for views
SELECT *
	FROM (
		(SELECT user_id FROM recent_users)
		UNION ALL
		(SELECT user_id FROM selected_users) ) u
	WHERE user_id < 2 AND user_id > 0
	ORDER BY user_id;
 user_id
---------------------------------------------------------------------
       1
       1
(2 rows)

SELECT count(*)
	FROM (
		(SELECT user_id FROM recent_users)
		UNION
		(SELECT user_id FROM selected_users) ) u
	WHERE user_id < 2 AND user_id > 0;
 count
---------------------------------------------------------------------
     1
(1 row)

-- UNION ALL between views is supported through recursive planning
SELECT count(*)
	FROM (
		(SELECT user_id FROM recent_users)
		UNION ALL
		(SELECT user_id FROM selected_users) ) u
	WHERE user_id < 2 AND user_id > 0;
 count
---------------------------------------------------------------------
     2
(1 row)

-- expand view definitions and re-run last 2 queries
SELECT count(*)
	FROM (
		(SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table
			GROUP BY user_id
			HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa
		)
		UNION
		(SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u
	WHERE user_id < 2 AND user_id > 0;
 count
---------------------------------------------------------------------
     1
(1 row)

SELECT count(*)
	FROM (
		(SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table
			GROUP BY user_id
			HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa
		)
		UNION ALL
		(SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u
	WHERE user_id < 2 AND user_id > 0;
 count
---------------------------------------------------------------------
     2
(1 row)

-- test distinct
-- distinct is supported if it is on a partition key
CREATE VIEW distinct_user_with_value_1_3 AS SELECT DISTINCT user_id FROM users_table WHERE value_1 = 3;
SELECT * FROM distinct_user_with_value_1_3 ORDER BY user_id;
 user_id
---------------------------------------------------------------------
       1
       2
       3
       4
       5
       6
(6 rows)

-- distinct is not supported if it is on a non-partition key
-- but will be supported via recursive planning
CREATE VIEW distinct_value_1 AS SELECT DISTINCT value_1 FROM users_table WHERE value_2 = 3;
SELECT * FROM distinct_value_1 ORDER BY 1 DESC LIMIT 5;
 value_1
---------------------------------------------------------------------
       5
       4
       3
       2
       1
(5 rows)

-- CTEs are supported even if they are on views
CREATE VIEW cte_view_1 AS
WITH c1 AS (SELECT * FROM users_table WHERE value_1 = 3) SELECT * FROM c1 WHERE value_2 < 4 AND EXISTS (SELECT * FROM c1);
SELECT * FROM cte_view_1 ORDER BY 1,2,3,4,5 LIMIT 5;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Thu Nov 23 03:32:50.803031 2017 |       3 |       2 |       1 |
       2 | Thu Nov 23 13:52:54.83829 2017  |       3 |       1 |       4 |
       3 | Wed Nov 22 23:24:32.080584 2017 |       3 |       2 |       5 |
       4 | Wed Nov 22 23:59:46.493416 2017 |       3 |       1 |       3 |
       4 | Thu Nov 23 01:55:21.824618 2017 |       3 |       1 |       4 |
(5 rows)

-- this is single shard query and still not supported since it has view + cte
-- router planner can't detect it
SELECT * FROM cte_view_1 WHERE user_id = 2 ORDER BY 1,2,3,4,5;
 user_id |              time              | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       2 | Thu Nov 23 13:52:54.83829 2017 |       3 |       1 |       4 |
(1 row)

-- if CTE itself prunes down to a single shard than the view is supported (router plannable)
CREATE VIEW cte_view_2 AS
WITH c1 AS (SELECT * FROM users_table WHERE user_id = 2) SELECT * FROM c1 WHERE value_1 = 3;
SELECT * FROM cte_view_2;
 user_id |              time               | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
       2 | Thu Nov 23 00:19:14.138058 2017 |       3 |       4 |       0 |
       2 | Thu Nov 23 13:52:54.83829 2017  |       3 |       1 |       4 |
       2 | Wed Nov 22 18:19:49.944985 2017 |       3 |       5 |       1 |
       2 | Thu Nov 23 11:41:04.042936 2017 |       3 |       4 |       1 |
(4 rows)

CREATE VIEW router_view AS SELECT * FROM users_table WHERE user_id = 2;
-- router plannable
SELECT user_id FROM router_view GROUP BY 1;
 user_id
---------------------------------------------------------------------
       2
(1 row)

-- join a router view
 SELECT * FROM (SELECT user_id FROM router_view GROUP BY 1) rv JOIN recent_events USING (user_id) ORDER BY 2 LIMIT 3;
 user_id |              time
---------------------------------------------------------------------
       2 | Thu Nov 23 17:26:14.563216 2017
(1 row)

 SELECT * FROM (SELECT user_id FROM router_view GROUP BY 1) rv JOIN (SELECT * FROM recent_events) re USING (user_id) ORDER BY 2 LIMIT 3;
 user_id |              time
---------------------------------------------------------------------
       2 | Thu Nov 23 17:26:14.563216 2017
(1 row)

-- views with limits
CREATE VIEW recent_10_users AS
	SELECT user_id, max(time) as lastseen FROM users_table
	GROUP BY user_id
	ORDER BY lastseen DESC
	LIMIT 10;
-- this is not supported since it has limit in it and subquery_pushdown is not set
SELECT * FROM recent_10_users;
 user_id |            lastseen
---------------------------------------------------------------------
       1 | Thu Nov 23 17:30:34.635085 2017
       3 | Thu Nov 23 17:18:51.048758 2017
       5 | Thu Nov 23 16:48:32.08896 2017
       4 | Thu Nov 23 15:32:02.360969 2017
       6 | Thu Nov 23 14:43:18.024104 2017
       2 | Thu Nov 23 13:52:54.83829 2017
(6 rows)

SET citus.subquery_pushdown to ON;
-- still not supported since outer query does not have limit
-- it shows a different (subquery with single relation) error message
SELECT * FROM recent_10_users;
ERROR:  cannot perform distributed planning on this query
DETAIL:  Subqueries with limit are not supported yet
-- now it displays more correct error message
SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id);
ERROR:  cannot push down this subquery
DETAIL:  Limit in subquery without limit in the outermost query is unsupported
-- now both are supported when there is a limit on the outer most query
SELECT * FROM recent_10_users ORDER BY lastseen DESC LIMIT 10;
 user_id |            lastseen
---------------------------------------------------------------------
       1 | Thu Nov 23 17:30:34.635085 2017
       3 | Thu Nov 23 17:18:51.048758 2017
       5 | Thu Nov 23 16:48:32.08896 2017
       4 | Thu Nov 23 15:32:02.360969 2017
       6 | Thu Nov 23 14:43:18.024104 2017
       2 | Thu Nov 23 13:52:54.83829 2017
(6 rows)

SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10;
 user_id |              time               | event_type | value_2 | value_3 | value_4
---------------------------------------------------------------------
       1 | Thu Nov 23 21:54:46.924477 2017 |          6 |       4 |       5 |
       4 | Thu Nov 23 18:10:21.338399 2017 |          1 |       2 |       4 |
       3 | Thu Nov 23 18:08:26.550729 2017 |          2 |       4 |       3 |
       2 | Thu Nov 23 17:26:14.563216 2017 |          1 |       5 |       3 |
       3 | Thu Nov 23 16:44:41.903713 2017 |          4 |       2 |       2 |
       3 | Thu Nov 23 16:31:56.219594 2017 |          5 |       1 |       2 |
       4 | Thu Nov 23 16:20:33.264457 2017 |          0 |       0 |       3 |
       5 | Thu Nov 23 16:11:02.929469 2017 |          4 |       2 |       0 |
       2 | Thu Nov 23 15:58:49.273421 2017 |          5 |       1 |       2 |
       5 | Thu Nov 23 14:40:40.467511 2017 |          1 |       4 |       1 |
(10 rows)

RESET citus.subquery_pushdown;
VACUUM ANALYZE users_table;
-- explain tests
EXPLAIN (COSTS FALSE) SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER BY 1;
                                                                 QUERY PLAN
---------------------------------------------------------------------
 Sort
   Sort Key: remote_scan.user_id
   ->  HashAggregate
         Group Key: remote_scan.user_id
         ->  Custom Scan (Citus Adaptive)
               Task Count: 4
               Tasks Shown: One of 4
               ->  Task
                     Node: host=localhost port=xxxxx dbname=regression
                     ->  HashAggregate
                           Group Key: users_table.user_id
                           ->  Nested Loop
                                 Join Filter: (users_table.user_id = users_table_1.user_id)
                                 ->  Sort
                                       Sort Key: (max(users_table_1."time")) DESC
                                       ->  HashAggregate
                                             Group Key: users_table_1.user_id
                                             Filter: (max(users_table_1."time") > '2017-11-23 16:20:33.264457'::timestamp without time zone)
                                             ->  Seq Scan on users_table_1400256 users_table_1
                                 ->  Seq Scan on users_table_1400256 users_table
                                       Filter: ((value_1 >= 1) AND (value_1 < 3))
(21 rows)

EXPLAIN (COSTS FALSE) SELECT *
	FROM (
		(SELECT user_id FROM recent_users)
		UNION
		(SELECT user_id FROM selected_users) ) u
	WHERE user_id < 4 AND user_id > 1
	ORDER BY user_id;
                                                                   QUERY PLAN
---------------------------------------------------------------------
 Sort
   Sort Key: remote_scan.user_id
   ->  Custom Scan (Citus Adaptive)
         Task Count: 4
         Tasks Shown: One of 4
         ->  Task
               Node: host=localhost port=xxxxx dbname=regression
               ->  Unique
                     ->  Sort
                           Sort Key: recent_users.user_id
                           ->  Append
                                 ->  Subquery Scan on recent_users
                                       ->  Sort
                                             Sort Key: (max(users_table."time")) DESC
                                             ->  GroupAggregate
                                                   Group Key: users_table.user_id
                                                   Filter: (max(users_table."time") > '2017-11-23 16:20:33.264457'::timestamp without time zone)
                                                   ->  Sort
                                                         Sort Key: users_table.user_id
                                                         ->  Seq Scan on users_table_1400256 users_table
                                                               Filter: ((user_id < 4) AND (user_id > 1))
                                 ->  Seq Scan on users_table_1400256 users_table_1
                                       Filter: ((value_1 >= 1) AND (value_1 < 3) AND (user_id < 4) AND (user_id > 1))
(23 rows)

EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10;
                                                  QUERY PLAN
---------------------------------------------------------------------
 Limit
   ->  Sort
         Sort Key: remote_scan."time" DESC
         ->  Custom Scan (Citus Adaptive)
               ->  Distributed Subplan XXX_1
                     ->  Limit
                           ->  Sort
                                 Sort Key: remote_scan.lastseen DESC
                                 ->  Custom Scan (Citus Adaptive)
                                       Task Count: 4
                                       Tasks Shown: One of 4
                                       ->  Task
                                             Node: host=localhost port=xxxxx dbname=regression
                                             ->  Limit
                                                   ->  Sort
                                                         Sort Key: (max("time")) DESC
                                                         ->  HashAggregate
                                                               Group Key: user_id
                                                               ->  Seq Scan on users_table_1400256 users_table
               Task Count: 4
               Tasks Shown: One of 4
               ->  Task
                     Node: host=localhost port=xxxxx dbname=regression
                     ->  Limit
                           ->  Sort
                                 Sort Key: et."time" DESC
                                 ->  Hash Join
                                       Hash Cond: (intermediate_result.user_id = et.user_id)
                                       ->  Function Scan on read_intermediate_result intermediate_result
                                       ->  Hash
                                             ->  Seq Scan on events_table_1400260 et
(31 rows)

SET citus.subquery_pushdown to ON;
EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10;
                                                     QUERY PLAN
---------------------------------------------------------------------
 Limit
   ->  Sort
         Sort Key: remote_scan."time" DESC
         ->  Custom Scan (Citus Adaptive)
               Task Count: 4
               Tasks Shown: One of 4
               ->  Task
                     Node: host=localhost port=xxxxx dbname=regression
                     ->  Limit
                           ->  Sort
                                 Sort Key: et."time" DESC
                                 ->  Hash Join
                                       Hash Cond: (et.user_id = recent_10_users.user_id)
                                       ->  Seq Scan on events_table_1400260 et
                                       ->  Hash
                                             ->  Subquery Scan on recent_10_users
                                                   ->  Limit
                                                         ->  Sort
                                                               Sort Key: (max(users_table."time")) DESC
                                                               ->  HashAggregate
                                                                     Group Key: users_table.user_id
                                                                     ->  Seq Scan on users_table_1400256 users_table
(22 rows)

RESET citus.subquery_pushdown;
DROP VIEW recent_10_users;
DROP VIEW router_view;
DROP VIEW cte_view_2;
DROP VIEW cte_view_1;
DROP VIEW distinct_value_1;
DROP VIEW distinct_user_with_value_1_3;
DROP VIEW recent_selected_users;
DROP VIEW selected_users;
DROP VIEW recent_events;
DROP VIEW recent_users;
-- modify statements on/with views
-- create two tables and a view
CREATE TABLE large (id int, tenant_id int);
-- constraint id to be unique for "insert into on conflict" test
CREATE TABLE small (id int, tenant_id int, unique(tenant_id));
SELECT create_distributed_table('large','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT create_distributed_table('small','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW small_view AS SELECT * from small where id < 100;
\copy small FROM STDIN DELIMITER ','
\copy large FROM STDIN DELIMITER ','
-- running modify statements "on" views is still not supported, hence below two statements will fail
UPDATE small_view SET id = 1;
ERROR:  cannot modify views over distributed tables
DELETE FROM small_view;
ERROR:  cannot modify views over distributed tables
INSERT INTO small_view VALUES(8, 5) ON CONFLICT(tenant_id) DO UPDATE SET tenant_id=99;
ERROR:  cannot modify views over distributed tables
-- using views in modify statements' FROM / WHERE clauses is still valid
UPDATE large SET id=20 FROM small_view WHERE small_view.id=large.id;
SELECT * FROM large order by 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  2 |         3
  5 |         4
 20 |         2
 20 |         5
(4 rows)

-- we should still have identical rows for next test statements, then insert new rows to both tables
INSERT INTO large VALUES(14, 14);
INSERT INTO small VALUES(14, 14);
-- using views in subqueries within modify statements is still valid
UPDATE large SET id=23 FROM (SELECT *, id*2 from small_view ORDER BY 1,2 LIMIT 5) as small_view WHERE small_view.id=large.id;
SELECT * FROM large order by 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  2 |         3
  5 |         4
 20 |         2
 20 |         5
 23 |        14
(5 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- using views in modify statements' FROM / WHERE clauses is still valid
UPDATE large SET id=27 FROM small_view WHERE small_view.tenant_id=large.tenant_id;
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
(6 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- test on a router executable update statement
UPDATE large SET id=28 FROM small_view WHERE small_view.id=large.id and small_view.tenant_id=14 and large.tenant_id=14;
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
(7 rows)

-- we should still have identical rows for next test statements, then insert new rows to both tables
INSERT INTO large VALUES(14, 14);
INSERT INTO large VALUES(99, 78);
INSERT INTO small VALUES(99, 99);
-- run these tests with RETURNING clause to observe the functionality
-- print the columns from the "view" as well to test "rewrite resjunk" behaviour
UPDATE large SET id=36 FROM small_view WHERE small_view.id=large.id RETURNING large.id, large.tenant_id, small_view.tenant_id;
 id | tenant_id | tenant_id
---------------------------------------------------------------------
 36 |        14 |        14
 36 |        78 |        99
(2 rows)

SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 36 |        78
(9 rows)

-- below statement should not update anything. so it should return empty
UPDATE large SET id=46 FROM small_view WHERE small_view.id=large.id and large.id=15 RETURNING large.id, large.tenant_id;
 id | tenant_id
---------------------------------------------------------------------
(0 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- delete statement on large
DELETE FROM large WHERE id in (SELECT id FROM small_view);
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 36 |        78
(9 rows)

-- we should still have identical rows for next test statement, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- delete statement with CTE
WITH all_small_view_ids AS (SELECT id FROM small_view)
DELETE FROM large WHERE id in (SELECT * FROM all_small_view_ids);
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 36 |        78
(9 rows)

-- INSERT INTO views is still not supported
INSERT INTO small_view VALUES(3, 3);
ERROR:  cannot modify views over distributed tables
DROP TABLE large;
DROP TABLE small CASCADE;
NOTICE:  drop cascades to view small_view
-- now, run the same modify statement tests on a partitioned table
CREATE TABLE small (id int, tenant_id int);
CREATE TABLE large_partitioned (id int, tenant_id int) partition by range(tenant_id);
CREATE TABLE large_partitioned_p1 PARTITION OF large_partitioned FOR VALUES FROM (1) TO (10);
CREATE TABLE large_partitioned_p2 PARTITION OF large_partitioned FOR VALUES FROM (10) TO (20);
CREATE TABLE large_partitioned_p3 PARTITION OF large_partitioned FOR VALUES FROM (20) TO (100);
SELECT create_distributed_table('large_partitioned','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT create_distributed_table('small','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW small_view AS SELECT * from small where id < 100;
\copy small FROM STDIN DELIMITER ','
\copy large_partitioned FROM STDIN DELIMITER ','
-- running modify statements "on" views is still not supported, hence below two statements will fail
UPDATE small_view SET id = 1;
ERROR:  cannot modify views over distributed tables
DELETE FROM small_view;
ERROR:  cannot modify views over distributed tables
UPDATE large_partitioned SET id=27 FROM small_view WHERE small_view.tenant_id=large_partitioned.tenant_id;
SELECT * FROM large_partitioned ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  6 |         5
 26 |        32
 27 |         2
 27 |         3
 27 |         4
 29 |        15
 60 |        51
(7 rows)

-- we should still have identical rows for next test statements, then insert identical rows to both tables
INSERT INTO small VALUES(14, 14);
INSERT INTO large_partitioned VALUES(14, 14);
-- test on a router executable update statement
UPDATE large_partitioned SET id=28 FROM small_view WHERE small_view.id=large_partitioned.id and small_view.tenant_id=14 and large_partitioned.tenant_id=14;
SELECT * FROM large_partitioned ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  6 |         5
 26 |        32
 27 |         2
 27 |         3
 27 |         4
 28 |        14
 29 |        15
 60 |        51
(8 rows)

-- we should still have identical rows for next test statements, then insert a new row to large_partitioned table
INSERT INTO large_partitioned VALUES(14, 14);
-- delete statement on large
DELETE FROM large_partitioned WHERE tenant_id in (SELECT tenant_id FROM small_view);
SELECT * FROM large_partitioned ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  6 |         5
 26 |        32
 29 |        15
 60 |        51
(4 rows)

-- we should still have identical rows for next test statement, then insert a new row to large table
INSERT INTO large_partitioned VALUES(14, 14);
-- delete statement with CTE
WITH all_small_view_tenant_ids AS (SELECT tenant_id FROM small_view)
DELETE FROM large_partitioned WHERE tenant_id in (SELECT * FROM all_small_view_tenant_ids);
SELECT * FROM large_partitioned ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  6 |         5
 26 |        32
 29 |        15
 60 |        51
(4 rows)

DROP TABLE large_partitioned;
DROP TABLE small CASCADE;
NOTICE:  drop cascades to view small_view
-- perform similar tests with a little bit complicated view
-- create two tables and a view
CREATE TABLE large (id int, tenant_id int);
-- constraint id to be unique for "insert into on conflict" test
CREATE TABLE small (id int, tenant_id int, unique(tenant_id));
SELECT create_distributed_table('large','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT create_distributed_table('small','tenant_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW small_view AS SELECT id, tenant_id FROM (SELECT *, id*2 FROM small WHERE id < 100 ORDER BY 1,2 LIMIT 5) as foo;
\copy small FROM STDIN DELIMITER ','
\copy large FROM STDIN DELIMITER ','
-- using views in modify statements' FROM / WHERE clauses is still valid
UPDATE large SET id=20 FROM small_view WHERE small_view.id=large.id;
SELECT * FROM large order by 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  2 |         3
  5 |         4
 20 |         2
 20 |         5
(4 rows)

-- we should still have identical rows for next test statements, then insert new rows to both tables
INSERT INTO large VALUES(14, 14);
INSERT INTO small VALUES(14, 14);
-- using views in subqueries within modify statements is still valid
UPDATE large SET id=23 FROM (SELECT *, id*2 from small_view ORDER BY 1,2 LIMIT 5) as small_view WHERE small_view.id=large.id;
SELECT * FROM large order by 1, 2;
 id | tenant_id
---------------------------------------------------------------------
  2 |         3
  5 |         4
 20 |         2
 20 |         5
 23 |        14
(5 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- using views in modify statements' FROM / WHERE clauses is still valid
UPDATE large SET id=27 FROM small_view WHERE small_view.tenant_id=large.tenant_id;
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
(6 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- test on a router executable update statement
UPDATE large SET id=28 FROM small_view WHERE small_view.id=large.id and small_view.tenant_id=14 and large.tenant_id=14;
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
(7 rows)

-- we should still have identical rows for next test statements, then insert new rows to both tables
INSERT INTO large VALUES(14, 14);
INSERT INTO large VALUES(99, 78);
INSERT INTO small VALUES(99, 99);
-- run these tests with RETURNING clause to observe the functionality
-- print the columns from the "view" as well to test "rewrite resjunk" behaviour
UPDATE large SET id=36 FROM small_view WHERE small_view.id=large.id RETURNING large.id, large.tenant_id, small_view.tenant_id;
 id | tenant_id | tenant_id
---------------------------------------------------------------------
 36 |        14 |        14
(1 row)

SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 99 |        78
(9 rows)

-- below statement should not update anything. so it should return empty
UPDATE large SET id=46 FROM small_view WHERE small_view.id=large.id and large.id=15 RETURNING large.id, large.tenant_id;
 id | tenant_id
---------------------------------------------------------------------
(0 rows)

-- we should still have identical rows for next test statements, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- delete statement on large
DELETE FROM large WHERE id in (SELECT id FROM small_view);
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 99 |        78
(9 rows)

-- we should still have identical rows for next test statement, then insert a new row to large table
INSERT INTO large VALUES(14, 14);
-- delete statement with CTE
WITH all_small_view_ids AS (SELECT id FROM small_view)
DELETE FROM large WHERE id in (SELECT * FROM all_small_view_ids);
SELECT * FROM large ORDER BY 1, 2;
 id | tenant_id
---------------------------------------------------------------------
 27 |         2
 27 |         3
 27 |         4
 27 |         5
 27 |        14
 27 |        14
 28 |        14
 36 |        14
 99 |        78
(9 rows)

-- INSERT INTO views is still not supported
INSERT INTO small_view VALUES(3, 3);
ERROR:  cannot insert into view "small_view"
DETAIL:  Views that do not select from a single table or view are not automatically updatable.
HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.
DROP TABLE large;
DROP TABLE small CASCADE;
NOTICE:  drop cascades to view small_view
