-- Tests to check propagation of all view commands
CREATE SCHEMA view_prop_schema;
SET search_path to view_prop_schema;
SET citus.next_shard_id TO 1420195;
-- Check creating views depending on different types of tables
-- and from multiple schemas
-- Check the most basic one
CREATE VIEW prop_view_basic AS SELECT 1;
-- Try to create view depending local table, then try to recreate it after distributing the table
CREATE TABLE view_table_1(id int, val_1 text);
CREATE VIEW prop_view_1 AS
    SELECT * FROM view_table_1;
WARNING:  "view prop_view_1" has dependency to "table view_table_1" that is not in Citus' metadata
DETAIL:  "view prop_view_1" will be created only locally
HINT:  Distribute "table view_table_1" first to distribute "view prop_view_1"
SELECT create_distributed_table('view_table_1', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE VIEW prop_view_1 AS
    SELECT * FROM view_table_1;
-- Try to create view depending local table, then try to recreate it after making the table reference table
CREATE TABLE view_table_2(id int PRIMARY KEY, val_1 text);
CREATE VIEW prop_view_2 AS
    SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
    ON view_table_1.id = view_table_2.id;
WARNING:  "view prop_view_2" has dependency to "table view_table_2" that is not in Citus' metadata
DETAIL:  "view prop_view_2" will be created only locally
HINT:  Distribute "table view_table_2" first to distribute "view prop_view_2"
SELECT create_reference_table('view_table_2');
 create_reference_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE VIEW prop_view_2 AS
    SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
    ON view_table_1.id = view_table_2.id;
-- Try to create view depending local table, then try to recreate it after making the table citus local table
CREATE TABLE view_table_3(id int, val_1 text);
CREATE VIEW prop_view_3 AS
    SELECT * FROM view_table_1 WHERE id IN
    (SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
WARNING:  "view prop_view_3" has dependency to "table view_table_3" that is not in Citus' metadata
DETAIL:  "view prop_view_3" will be created only locally
HINT:  Distribute "table view_table_3" first to distribute "view prop_view_3"
SET client_min_messages TO WARNING;
SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0);
 ?column?
---------------------------------------------------------------------
        1
(1 row)

RESET client_min_messages;
ALTER TABLE view_table_3
ADD CONSTRAINT f_key_for_local_table
FOREIGN KEY(id)
REFERENCES view_table_2(id);
CREATE OR REPLACE VIEW prop_view_3 AS
    SELECT * FROM view_table_1 WHERE id IN
    (SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
-- Try to create view depending on PG metadata table
CREATE VIEW prop_view_4 AS
    SELECT * FROM pg_stat_activity;
-- Try to create view depending on Citus metadata table
CREATE VIEW prop_view_5 AS
    SELECT * FROM citus_dist_stat_activity;
-- Try to create table depending on a local table from another schema, then try to create it again after distributing the table
CREATE SCHEMA view_prop_schema_inner;
SET search_path TO view_prop_schema_inner;
-- Create local table for tests below
CREATE TABLE view_table_4(id int, val_1 text);
-- Create a distributed table and view to test drop view below
CREATE TABLE inner_view_table(id int);
SELECT create_distributed_table('inner_view_table','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW inner_view_prop AS SELECT * FROM inner_view_table;
SET search_path to view_prop_schema;
CREATE VIEW prop_view_6 AS
    SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
    INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
WARNING:  "view prop_view_6" has dependency to "table view_prop_schema_inner.view_table_4" that is not in Citus' metadata
DETAIL:  "view prop_view_6" will be created only locally
HINT:  Distribute "table view_prop_schema_inner.view_table_4" first to distribute "view prop_view_6"
SELECT create_distributed_table('view_prop_schema_inner.view_table_4','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE VIEW prop_view_6 AS
    SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
    INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
-- Show that all views are propagated as distributed object
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_%' ORDER BY 1;
                 obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,prop_view_1}",{})
 (view,"{view_prop_schema,prop_view_2}",{})
 (view,"{view_prop_schema,prop_view_3}",{})
 (view,"{view_prop_schema,prop_view_4}",{})
 (view,"{view_prop_schema,prop_view_5}",{})
 (view,"{view_prop_schema,prop_view_6}",{})
 (view,"{view_prop_schema,prop_view_basic}",{})
(7 rows)

-- Check creating views depending various kind of objects
-- Tests will also check propagating dependent objects
-- Depending on function
SET citus.enable_ddl_propagation TO OFF;
CREATE OR REPLACE FUNCTION func_1_for_view(param_1 int)
RETURNS int
LANGUAGE plpgsql AS
$$
BEGIN
    return param_1;
END;
$$;
RESET citus.enable_ddl_propagation;
-- Show that function will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

CREATE VIEW prop_view_7 AS SELECT func_1_for_view(id) FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
                      obj_identifier
---------------------------------------------------------------------
 (function,"{view_prop_schema,func_1_for_view}",{integer})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_7%';
               obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,prop_view_7}",{})
(1 row)

-- Depending on type
SET citus.enable_ddl_propagation TO OFF;
CREATE TYPE type_for_view_prop AS ENUM ('a','b','c');
RESET citus.enable_ddl_propagation;
-- Show that type will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

CREATE VIEW prop_view_8 AS SELECT val_1::type_for_view_prop FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
                 obj_identifier
---------------------------------------------------------------------
 (type,{view_prop_schema.type_for_view_prop},{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_8%';
               obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,prop_view_8}",{})
(1 row)

-- Depending on another view
CREATE TABLE view_table_5(id int);
CREATE VIEW prop_view_9 AS SELECT * FROM view_table_5;
WARNING:  "view prop_view_9" has dependency to "table view_table_5" that is not in Citus' metadata
DETAIL:  "view prop_view_9" will be created only locally
HINT:  Distribute "table view_table_5" first to distribute "view prop_view_9"
CREATE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
WARNING:  "view prop_view_10" has dependency to "table view_table_5" that is not in Citus' metadata
DETAIL:  "view prop_view_10" will be created only locally
HINT:  Distribute "table view_table_5" first to distribute "view prop_view_10"
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

SELECT create_distributed_table('view_table_5', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
               obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,prop_view_9}",{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
               obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,prop_view_10}",{})
(1 row)

-- Check views owned by non-superuser
SET client_min_messages TO ERROR;
CREATE USER view_creation_user;
SELECT 1 FROM run_command_on_workers($$CREATE USER view_creation_user;$$);
 ?column?
---------------------------------------------------------------------
        1
        1
(2 rows)

GRANT ALL PRIVILEGES ON SCHEMA view_prop_schema to view_creation_user;
SET ROLE view_creation_user;
CREATE TABLE user_owned_table_for_view(id int);
SELECT create_distributed_table('user_owned_table_for_view','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW view_owned_by_user AS SELECT * FROM user_owned_table_for_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
                  obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,view_owned_by_user}",{})
(1 row)

DROP VIEW view_owned_by_user;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

DROP TABLE user_owned_table_for_view;
RESET ROLE;
RESET client_min_messages;
-- Create view with different options
CREATE TABLE view_table_6(id int, val_1 text);
SELECT create_distributed_table('view_table_6','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- TEMP VIEW is not supported. View will be created locally.
CREATE TEMP VIEW temp_prop_view AS SELECT * FROM view_table_6;
WARNING:  "view temp_prop_view" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL:  "view temp_prop_view" will be created only locally
-- Recursive views are supported
CREATE RECURSIVE VIEW nums_1_100_prop_view (n) AS
    VALUES (1)
UNION ALL
    SELECT n+1 FROM nums_1_100_prop_view WHERE n < 100;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%nums_1_100_prop_view%';
                   obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,nums_1_100_prop_view}",{})
(1 row)

-- Sequences are supported as dependency
CREATE SEQUENCE sequence_to_prop;
CREATE VIEW seq_view_prop AS SELECT sequence_to_prop.is_called FROM sequence_to_prop;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%sequence_to_prop%';
                   obj_identifier
---------------------------------------------------------------------
 (sequence,"{view_prop_schema,sequence_to_prop}",{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%seq_view_prop%';
                obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,seq_view_prop}",{})
(1 row)

-- Views depend on temp sequences will be created locally
CREATE TEMPORARY SEQUENCE temp_sequence_to_drop;
CREATE VIEW temp_seq_view_prop AS SELECT temp_sequence_to_drop.is_called FROM temp_sequence_to_drop;
NOTICE:  view "temp_seq_view_prop" will be a temporary view
WARNING:  "view temp_seq_view_prop" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL:  "view temp_seq_view_prop" will be created only locally
-- Check circular dependencies are detected
CREATE VIEW circular_view_1 AS SELECT * FROM view_table_6;
CREATE VIEW circular_view_2 AS SELECT * FROM view_table_6;
CREATE OR REPLACE VIEW circular_view_1 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_2 USING (id);
CREATE OR REPLACE VIEW circular_view_2 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_1 USING (id);
ERROR:  Citus can not handle circular dependencies between distributed objects
DETAIL:  "view circular_view_2" circularly depends itself, resolve circular dependency first
-- Recursive views with distributed tables included
CREATE TABLE employees (employee_id int, manager_id int, full_name text);
SELECT create_distributed_table('employees', 'employee_id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE RECURSIVE VIEW reporting_line (employee_id, subordinates) AS
SELECT
	employee_id,
	full_name AS subordinates
FROM
	employees
WHERE
	manager_id IS NULL
UNION ALL
	SELECT
		e.employee_id,
		(
			rl.subordinates || ' > ' || e.full_name
		) AS subordinates
	FROM
		employees e
	INNER JOIN reporting_line rl ON e.manager_id = rl.employee_id;
-- Aliases are supported
CREATE VIEW aliased_opt_prop_view(alias_1, alias_2) AS SELECT * FROM view_table_6 table_name_for_view;
-- View options are supported
CREATE VIEW opt_prop_view
    WITH(check_option=CASCADED, security_barrier=true)
    AS SELECT * FROM view_table_6 table_name_for_view;
CREATE VIEW sep_opt_prop_view
    AS SELECT * FROM view_table_6 table_name_for_view
    WITH LOCAL CHECK OPTION;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%' ORDER BY 1;
                    obj_identifier
---------------------------------------------------------------------
 (view,"{view_prop_schema,aliased_opt_prop_view}",{})
 (view,"{view_prop_schema,opt_prop_view}",{})
 (view,"{view_prop_schema,sep_opt_prop_view}",{})
(3 rows)

-- Check definitions and reltoptions of views are correct on workers
\c - - - :worker_1_port
SELECT definition FROM pg_views WHERE viewname = 'aliased_opt_prop_view';
                         definition
---------------------------------------------------------------------
  SELECT id AS alias_1,                +
     val_1 AS alias_2                  +
    FROM view_prop_schema.view_table_6 table_name_for_view;
(1 row)

SELECT definition FROM pg_views WHERE viewname = 'opt_prop_view';
                         definition
---------------------------------------------------------------------
  SELECT id,                           +
     val_1                             +
    FROM view_prop_schema.view_table_6 table_name_for_view;
(1 row)

SELECT definition FROM pg_views WHERE viewname = 'sep_opt_prop_view';
                         definition
---------------------------------------------------------------------
  SELECT id,                           +
     val_1                             +
    FROM view_prop_schema.view_table_6 table_name_for_view;
(1 row)

SELECT relname, reloptions
FROM pg_class
WHERE
    oid = 'view_prop_schema.aliased_opt_prop_view'::regclass::oid OR
    oid = 'view_prop_schema.opt_prop_view'::regclass::oid OR
    oid = 'view_prop_schema.sep_opt_prop_view'::regclass::oid
ORDER BY 1;
        relname        |                  reloptions
---------------------------------------------------------------------
 aliased_opt_prop_view |
 opt_prop_view         | {check_option=cascaded,security_barrier=true}
 sep_opt_prop_view     | {check_option=local}
(3 rows)

\c - - - :master_port
SET search_path to view_prop_schema;
-- Sync metadata to check it works properly after adding a view
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
 start_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

-- Drop views and check metadata afterwards
DROP VIEW prop_view_9 CASCADE;
NOTICE:  drop cascades to view prop_view_10
DROP VIEW opt_prop_view, aliased_opt_prop_view, view_prop_schema_inner.inner_view_prop, sep_opt_prop_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%inner_view_prop%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

-- Drop a column that view depends on
ALTER TABLE view_table_1 DROP COLUMN val_1 CASCADE;
NOTICE:  drop cascades to 3 other objects
DETAIL:  drop cascades to view prop_view_1
drop cascades to view prop_view_3
drop cascades to view prop_view_8
-- Since prop_view_3 depends on the view_table_1's val_1 column, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_3%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

-- Drop a table that view depends on
DROP TABLE view_table_2 CASCADE;
NOTICE:  drop cascades to 2 other objects
DETAIL:  drop cascades to view prop_view_2
drop cascades to constraint f_key_for_local_table on table view_table_3
NOTICE:  drop cascades to constraint f_key_for_local_table_1420200 on table view_prop_schema.view_table_3_1420200
CONTEXT:  SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)"
PL/pgSQL function citus_drop_trigger() line XX at PERFORM
NOTICE:  removing table view_prop_schema.view_table_3 from metadata as it is not connected to any reference tables via foreign keys
-- Since prop_view_2 depends on the view_table_2, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_2%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

-- Show that unsupported CREATE OR REPLACE VIEW commands are catched by PG on the coordinator
CREATE TABLE table_to_test_unsup_view(id int, val1 text);
SELECT create_distributed_table('table_to_test_unsup_view', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW view_for_unsup_commands AS SELECT * FROM table_to_test_unsup_view;
CREATE OR REPLACE VIEW view_for_unsup_commands(a,b) AS SELECT * FROM table_to_test_unsup_view;
ERROR:  cannot change name of view column "id" to "a"
HINT:  Use ALTER VIEW ... RENAME COLUMN ... to change name of view column instead.
CREATE OR REPLACE VIEW view_for_unsup_commands AS SELECT id FROM table_to_test_unsup_view;
ERROR:  cannot drop columns from view
-- ALTER VIEW PROPAGATION
CREATE TABLE alter_view_table(id int, val1 text);
SELECT create_distributed_table('alter_view_table','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW alter_view_1 AS SELECT * FROM alter_view_table table_name_for_view;
-- Set/drop default value is not supported by Citus
ALTER VIEW alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
ERROR:  Citus doesn't support setting or resetting default values for a column of view
ALTER TABLE alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
ERROR:  Citus doesn't support setting or resetting default values for a column of view
ALTER VIEW alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
ERROR:  Citus doesn't support setting or resetting default values for a column of view
ALTER TABLE alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
ERROR:  Citus doesn't support setting or resetting default values for a column of view
-- Set/reset options view alter view/alter table commands
ALTER VIEW alter_view_1 SET (check_option=cascaded);
ALTER VIEW alter_view_1 SET (security_barrier);
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier);
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier = true);
ALTER TABLE alter_view_1 SET (check_option=cascaded);
ALTER TABLE alter_view_1 SET (security_barrier);
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier);
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier = true);
-- Check the definition on both coordinator and worker node
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
                  definition
---------------------------------------------------------------------
  SELECT id,              +
     val1                 +
    FROM alter_view_table table_name_for_view;
(1 row)

SELECT relname, reloptions
FROM pg_class
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
   relname    |                  reloptions
---------------------------------------------------------------------
 alter_view_1 | {check_option=cascaded,security_barrier=true}
(1 row)

\c - - - :worker_1_port
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
                           definition
---------------------------------------------------------------------
  SELECT id,                               +
     val1                                  +
    FROM view_prop_schema.alter_view_table table_name_for_view;
(1 row)

SELECT relname, reloptions
FROM pg_class
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
   relname    |                  reloptions
---------------------------------------------------------------------
 alter_view_1 | {check_option=cascaded,security_barrier=true}
(1 row)

\c - - - :master_port
SET search_path to view_prop_schema;
ALTER TABLE alter_view_1 RESET (check_option, security_barrier);
ALTER VIEW alter_view_1 RESET (check_option, security_barrier);
-- Change the schema of the view
ALTER TABLE alter_view_1 SET SCHEMA view_prop_schema_inner;
ALTER VIEW view_prop_schema_inner.alter_view_1 SET SCHEMA view_prop_schema;
-- Rename view and view's column name
ALTER VIEW alter_view_1 RENAME COLUMN val1 TO val2;
ALTER VIEW alter_view_1 RENAME val2 TO val1;
ALTER VIEW alter_view_1 RENAME TO alter_view_2;
ALTER TABLE alter_view_2 RENAME COLUMN val1 TO val2;
ALTER TABLE alter_view_2 RENAME val2 TO val1;
ALTER TABLE alter_view_2 RENAME TO alter_view_1;
-- Alter owner vith alter view/alter table
SET client_min_messages TO ERROR;
CREATE USER alter_view_user;
SELECT 1 FROM run_command_on_workers($$CREATE USER alter_view_user;$$);
 ?column?
---------------------------------------------------------------------
        1
        1
(2 rows)

RESET client_min_messages;
ALTER VIEW alter_view_1 OWNER TO alter_view_user;
ALTER TABLE alter_view_1 OWNER TO alter_view_user;
-- Alter view owned by extension
CREATE TABLE table_for_ext_owned_view(id int);
CREATE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
WARNING:  "view extension_owned_view" has dependency to "table table_for_ext_owned_view" that is not in Citus' metadata
DETAIL:  "view extension_owned_view" will be created only locally
HINT:  Distribute "table table_for_ext_owned_view" first to distribute "view extension_owned_view"
CREATE EXTENSION seg;
ALTER EXTENSION seg ADD VIEW extension_owned_view;
NOTICE:  Citus does not propagate adding/dropping member objects
HINT:  You can add/drop the member objects on the workers as well.
SELECT create_distributed_table('table_for_ext_owned_view','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE OR REPLACE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
-- Since the view is owned by extension Citus shouldn't propagate it
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%extension_owned_view%';
 obj_identifier
---------------------------------------------------------------------
(0 rows)

-- Try syncing metadata after running ALTER VIEW commands
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
 start_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

-- Alter non-existing view
ALTER VIEW IF EXISTS non_existing_view ALTER COLUMN val1 SET DEFAULT random()::text;
NOTICE:  relation "non_existing_view" does not exist, skipping
ALTER VIEW IF EXISTS non_existing_view SET (check_option=cascaded);
NOTICE:  relation "non_existing_view" does not exist, skipping
ALTER VIEW IF EXISTS non_existing_view RENAME COLUMN val1 TO val2;
NOTICE:  relation "non_existing_view" does not exist, skipping
ALTER VIEW IF EXISTS non_existing_view RENAME val2 TO val1;
NOTICE:  relation "non_existing_view" does not exist, skipping
ALTER VIEW IF EXISTS non_existing_view SET SCHEMA view_prop_schema;
NOTICE:  relation "non_existing_view" does not exist, skipping
-- Show that privileges should be granted to both view and depending table
-- Set shard id seq and shard replication factor for consistent test result
ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1900000;
SET citus.shard_count to 1;
CREATE USER grant_view_user;
CREATE TABLE table_to_test_grant_view(id int, val1 text);
SELECT create_distributed_table('table_to_test_grant_view','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE VIEW view_to_test_grant_view AS SELECT * FROM table_to_test_grant_view;
GRANT ALL ON SCHEMA view_prop_schema TO grant_view_user;
GRANT ALL ON view_to_test_grant_view TO grant_view_user;
SET ROLE grant_view_user;
-- Should fail since required privileges for dependent tables haven't been granted
SELECT count(*) FROM view_to_test_grant_view;
ERROR:  permission denied for table table_to_test_grant_view_1900000
CONTEXT:  while executing command on localhost:xxxxx
RESET ROLE;
\c - grant_view_user - :worker_1_port
-- Should it fails in a same way on the worker ndoe
SET search_path to view_prop_schema;
SELECT count(*) FROM view_to_test_grant_view;
ERROR:  permission denied for table table_to_test_grant_view_1900000
\c - postgres - :master_port
SET search_path to view_prop_schema;
GRANT ALL ON table_to_test_grant_view TO grant_view_user;
SET ROLE grant_view_user;
-- Should work now as privileges for the table have been granted
SELECT count(*) FROM view_to_test_grant_view;
 count
---------------------------------------------------------------------
     0
(1 row)

RESET ROLE;
RESET citus.shard_count;
-- Show that create view and alter view commands can be run from same transaction
-- but not the drop view. Since we can not use metadata connection for drop view commands
BEGIN;
    SET LOCAL citus.force_max_query_parallelization TO ON;
    CREATE TABLE table_1_to_view_in_transaction(a int);
    SELECT create_distributed_table('table_1_to_view_in_transaction', 'a');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

    CREATE TABLE table_2_to_view_in_transaction(a int);
    SELECT create_distributed_table('table_2_to_view_in_transaction', 'a');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

    -- we can create/alter/drop views even in parallel mode
    CREATE VIEW view_in_transaction AS SELECT table_1_to_view_in_transaction.* FROM table_2_to_view_in_transaction JOIN table_1_to_view_in_transaction USING (a);
    ALTER TABLE view_in_transaction SET (security_barrier);
    ALTER VIEW view_in_transaction SET SCHEMA public;
    ALTER VIEW public.view_in_transaction SET SCHEMA view_prop_schema_inner;
    ALTER TABLE view_prop_schema_inner.view_in_transaction RENAME COLUMN a TO b;
    DROP VIEW view_prop_schema_inner.view_in_transaction;
ERROR:  cannot run view command because there was a parallel operation on a distributed table in the transaction
DETAIL:  When running command on/for a distributed view, Citus needs to perform all operations over a single connection per node to ensure consistency.
HINT:  Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';"
ROLLBACK;
-- verify that the views get distributed after the table is distributed
create table table_to_depend_on_1 (a int);
create table table_to_depend_on_2 (a int);
-- the first view depends on a table
create view dependent_view_1 as select count(*) from table_to_depend_on_1;
WARNING:  "view dependent_view_1" has dependency to "table table_to_depend_on_1" that is not in Citus' metadata
DETAIL:  "view dependent_view_1" will be created only locally
HINT:  Distribute "table table_to_depend_on_1" first to distribute "view dependent_view_1"
-- the second view depends on two tables
create view dependent_view_2 as select count(*) from table_to_depend_on_1 join table_to_depend_on_2 on table_to_depend_on_1.a=table_to_depend_on_2.a;
WARNING:  "view dependent_view_2" has dependency to "table table_to_depend_on_2" that is not in Citus' metadata
DETAIL:  "view dependent_view_2" will be created only locally
HINT:  Distribute "table table_to_depend_on_2" first to distribute "view dependent_view_2"
-- the third view depends on the first view
create view dependent_view_3 as select count(*) from table_to_depend_on_1;
WARNING:  "view dependent_view_3" has dependency to "table table_to_depend_on_1" that is not in Citus' metadata
DETAIL:  "view dependent_view_3" will be created only locally
HINT:  Distribute "table table_to_depend_on_1" first to distribute "view dependent_view_3"
-- the fourth view depends on the second view
create view dependent_view_4 as select count(*) from table_to_depend_on_2;
WARNING:  "view dependent_view_4" has dependency to "table table_to_depend_on_2" that is not in Citus' metadata
DETAIL:  "view dependent_view_4" will be created only locally
HINT:  Distribute "table table_to_depend_on_2" first to distribute "view dependent_view_4"
-- distribute only one table
select create_distributed_table('table_to_depend_on_1','a');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- see all four views on the coordinator
select viewname from pg_views where viewname like 'dependent_view__';
     viewname
---------------------------------------------------------------------
 dependent_view_1
 dependent_view_2
 dependent_view_3
 dependent_view_4
(4 rows)

\c - - - :worker_1_port
-- see 1st and 3rd view on the worker
select viewname from pg_views where viewname like 'dependent_view__';
     viewname
---------------------------------------------------------------------
 dependent_view_1
 dependent_view_3
(2 rows)

\c - - - :master_port
CREATE TABLE parent_1 (a INT UNIQUE) PARTITION BY RANGE(a);
SELECT create_distributed_table('parent_1','a');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE TABLE parent_1_child_1 (a int);
CREATE TABLE parent_1_child_2 (a int);
CREATE VIEW v1 AS SELECT * FROM parent_1_child_1;
WARNING:  "view v1" has dependency to "table parent_1_child_1" that is not in Citus' metadata
DETAIL:  "view v1" will be created only locally
HINT:  Distribute "table parent_1_child_1" first to distribute "view v1"
CREATE VIEW v2 AS SELECT * FROM parent_1_child_2;
WARNING:  "view v2" has dependency to "table parent_1_child_2" that is not in Citus' metadata
DETAIL:  "view v2" will be created only locally
HINT:  Distribute "table parent_1_child_2" first to distribute "view v2"
CREATE VIEW v3 AS SELECT parent_1_child_2.* FROM parent_1_child_2 JOIN parent_1_child_1 USING(a);
WARNING:  "view v3" has dependency to "table parent_1_child_2" that is not in Citus' metadata
DETAIL:  "view v3" will be created only locally
HINT:  Distribute "table parent_1_child_2" first to distribute "view v3"
CREATE VIEW v4 AS SELECT * FROM v3;
WARNING:  "view v4" has dependency to "table parent_1_child_2" that is not in Citus' metadata
DETAIL:  "view v4" will be created only locally
HINT:  Distribute "table parent_1_child_2" first to distribute "view v4"
alter table parent_1 attach partition parent_1_child_1 FOR VALUES FROM (0) TO (10) ;
-- only v1 distributed
SELECT run_command_on_workers($$SELECT count(*) FROM v1$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,0)
 (localhost,57638,t,0)
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v2$$);
                    run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  relation ""v2"" does not exist")
 (localhost,57638,f,"ERROR:  relation ""v2"" does not exist")
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v3$$);
                    run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  relation ""v3"" does not exist")
 (localhost,57638,f,"ERROR:  relation ""v3"" does not exist")
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v4$$);
                    run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  relation ""v4"" does not exist")
 (localhost,57638,f,"ERROR:  relation ""v4"" does not exist")
(2 rows)

-- all views becomes distributed
alter table parent_1 attach partition parent_1_child_2 FOR VALUES FROM (10) TO (20);
SELECT run_command_on_workers($$SELECT count(*) FROM v1$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,0)
 (localhost,57638,t,0)
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v2$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,0)
 (localhost,57638,t,0)
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v3$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,0)
 (localhost,57638,t,0)
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v4$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,0)
 (localhost,57638,t,0)
(2 rows)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%v1%';
     obj_identifier
---------------------------------------------------------------------
 (view,"{public,v1}",{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%v2%';
     obj_identifier
---------------------------------------------------------------------
 (view,"{public,v2}",{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%v3%';
     obj_identifier
---------------------------------------------------------------------
 (view,"{public,v3}",{})
(1 row)

SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%v4%';
     obj_identifier
---------------------------------------------------------------------
 (view,"{public,v4}",{})
(1 row)

CREATE TABLE employees (employee_id int, manager_id int, full_name text);
-- v_test_1 and v_test_2 becomes circularly dependend views
-- so we should not try to distribute any of the views
CREATE VIEW v_test_1 AS SELECT * FROM employees;
WARNING:  "view v_test_1" has dependency to "table employees" that is not in Citus' metadata
DETAIL:  "view v_test_1" will be created only locally
HINT:  Distribute "table employees" first to distribute "view v_test_1"
CREATE VIEW v_test_2 AS SELECT * FROM employees;
WARNING:  "view v_test_2" has dependency to "table employees" that is not in Citus' metadata
DETAIL:  "view v_test_2" will be created only locally
HINT:  Distribute "table employees" first to distribute "view v_test_2"
CREATE OR REPLACE VIEW v_test_1 AS SELECT employees.* FROM employees JOIN v_test_2 USING (employee_id);
WARNING:  "view v_test_1" has dependency to "table employees" that is not in Citus' metadata
DETAIL:  "view v_test_1" will be created only locally
HINT:  Distribute "table employees" first to distribute "view v_test_1"
CREATE OR REPLACE VIEW v_test_2 AS SELECT employees.* FROM employees JOIN v_test_1 USING (employee_id);
WARNING:  "view v_test_2" has dependency to "table employees" that is not in Citus' metadata
DETAIL:  "view v_test_2" will be created only locally
HINT:  Distribute "table employees" first to distribute "view v_test_2"
SELECT create_distributed_table('employees','employee_id');
ERROR:  Citus can not handle circular dependencies between distributed objects
DETAIL:  "view v_test_1" circularly depends itself, resolve circular dependency first
-- verify not distributed
SELECT run_command_on_workers($$SELECT count(*) FROM v_test_1$$);
                       run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  relation ""v_test_1"" does not exist")
 (localhost,57638,f,"ERROR:  relation ""v_test_1"" does not exist")
(2 rows)

SELECT run_command_on_workers($$SELECT count(*) FROM v_test_2$$);
                       run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,f,"ERROR:  relation ""v_test_2"" does not exist")
 (localhost,57638,f,"ERROR:  relation ""v_test_2"" does not exist")
(2 rows)

-- create and drop temporary view
CREATE TEMPORARY VIEW temp_view_to_drop AS SELECT 1;
WARNING:  "view temp_view_to_drop" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL:  "view temp_view_to_drop" will be created only locally
DROP VIEW temp_view_to_drop;
-- drop non-existent view
DROP VIEW IF EXISTS drop_the_nonexistent_view;
NOTICE:  view "drop_the_nonexistent_view" does not exist, skipping
DROP VIEW IF EXISTS non_exist_view_schema.view_to_drop;
NOTICE:  schema "non_exist_view_schema" does not exist, skipping
-- Check materialized view just for sanity
CREATE MATERIALIZED VIEW materialized_view_to_test AS SELECT 1;
DROP VIEW materialized_view_to_test;
ERROR:  "materialized_view_to_test" is not a view
HINT:  Use DROP MATERIALIZED VIEW to remove a materialized view.
DROP MATERIALIZED VIEW materialized_view_to_test;
CREATE SCHEMA axx;
create materialized view axx.temp_view_to_drop AS SELECT 1;
DROP VIEW axx.temp_view_to_drop;
ERROR:  "temp_view_to_drop" is not a view
HINT:  Use DROP MATERIALIZED VIEW to remove a materialized view.
DROP VIEW IF EXISTS axx.temp_view_to_drop;
ERROR:  "temp_view_to_drop" is not a view
HINT:  Use DROP MATERIALIZED VIEW to remove a materialized view.
DROP MATERIALIZED VIEW IF EXISTS axx.temp_view_to_drop;
DROP MATERIALIZED VIEW IF EXISTS axx.temp_view_to_drop;
NOTICE:  materialized view "temp_view_to_drop" does not exist, skipping
-- Test the GUC citus.enforce_object_restrictions_for_local_objects
SET search_path to view_prop_schema;
CREATE TABLE local_t (a int);
-- tests when citus.enforce_object_restrictions_for_local_objects=true
SET citus.enforce_object_restrictions_for_local_objects TO true;
-- views will be created locally with warnings
CREATE VIEW vv1 as SELECT * FROM local_t;
WARNING:  "view vv1" has dependency to "table local_t" that is not in Citus' metadata
DETAIL:  "view vv1" will be created only locally
HINT:  Distribute "table local_t" first to distribute "view vv1"
CREATE OR REPLACE VIEW vv2 as SELECT * FROM vv1;
WARNING:  "view vv2" has dependency to "table local_t" that is not in Citus' metadata
DETAIL:  "view vv2" will be created only locally
HINT:  Distribute "table local_t" first to distribute "view vv2"
-- view will fail due to local circular dependency
CREATE OR REPLACE VIEW vv1 as SELECT * FROM vv2;
ERROR:  Citus can not handle circular dependencies between distributed objects
DETAIL:  "view vv1" circularly depends itself, resolve circular dependency first
-- local view with no citus relation dependency will be distributed
CREATE VIEW v_dist AS SELECT 1;
-- show that the view is in pg_dist_object
SELECT COUNT(*) FROM pg_dist_object WHERE objid = 'v_dist'::regclass;
 count
---------------------------------------------------------------------
     1
(1 row)

-- tests when citus.enforce_object_restrictions_for_local_objects=false
SET citus.enforce_object_restrictions_for_local_objects TO false;
SET citus.enable_unsupported_feature_messages TO false;
-- views will be created locally without errors OR warnings
CREATE VIEW vv3 as SELECT * FROM local_t;
CREATE OR REPLACE VIEW vv4 as SELECT * FROM vv3;
CREATE OR REPLACE VIEW vv3 as SELECT * FROM vv4;
-- show that views are NOT in pg_dist_object
SELECT COUNT(*) FROM pg_dist_object WHERE objid IN ('vv3'::regclass, 'vv4'::regclass);
 count
---------------------------------------------------------------------
     0
(1 row)

-- local view with no citus relation dependency will NOT be distributed
CREATE VIEW v_local_only AS SELECT 1;
-- show that the view is NOT in pg_dist_object
SELECT COUNT(*) FROM pg_dist_object WHERE objid = 'v_local_only'::regclass;
 count
---------------------------------------------------------------------
     0
(1 row)

-- distribute the local table and check the distribution of dependent views
SELECT create_distributed_table('local_t', 'a');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- show that views with no circular dependency are in pg_dist_object
SELECT COUNT(*) FROM pg_dist_object WHERE objid IN ('vv1'::regclass, 'vv2'::regclass);
 count
---------------------------------------------------------------------
     2
(1 row)

-- show that views with circular dependency are NOT in pg_dist_object
SELECT COUNT(*) FROM pg_dist_object WHERE objid IN ('vv3'::regclass, 'vv4'::regclass);
 count
---------------------------------------------------------------------
     0
(1 row)

-- show that we cannot re-create the circular views ever
CREATE OR REPLACE VIEW vv3 as SELECT * FROM local_t;
CREATE OR REPLACE VIEW vv4 as SELECT * FROM vv3;
CREATE OR REPLACE VIEW vv3 as SELECT * FROM vv4;
ERROR:  Citus can not handle circular dependencies between distributed objects
DETAIL:  "view vv3" circularly depends itself, resolve circular dependency first
RESET citus.enable_unsupported_feature_messages;
RESET citus.enforce_object_restrictions_for_local_objects;
SET client_min_messages TO ERROR;
DROP TABLE public.parent_1, public.employees CASCADE;
DROP SCHEMA view_prop_schema_inner CASCADE;
DROP SCHEMA view_prop_schema, axx CASCADE;
DROP ROLE view_creation_user, alter_view_user, grant_view_user;
