--
-- MULTI_MULTIUSERS
--
-- Test user permissions.
--
SET citus.next_shard_id TO 1420000;
SET citus.shard_replication_factor TO 1;
CREATE TABLE test (id integer, val integer);
SELECT create_distributed_table('test', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

CREATE TABLE test_coloc (id integer, val integer);
SELECT create_distributed_table('test_coloc', 'id', colocate_with := 'test');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SET citus.shard_count TO 1;
CREATE TABLE singleshard (id integer, val integer);
SELECT create_distributed_table('singleshard', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- turn off propagation to avoid Enterprise processing the following section
SET citus.enable_ddl_propagation TO off;
CREATE USER full_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER usage_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER read_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER no_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE ROLE some_role;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
GRANT some_role TO full_access;
GRANT some_role TO read_access;
GRANT ALL ON TABLE test TO full_access;
GRANT SELECT ON TABLE test TO read_access;
CREATE SCHEMA full_access_user_schema;
REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC;
GRANT ALL ON SCHEMA full_access_user_schema TO full_access;
GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access;
SET citus.enable_ddl_propagation TO DEFAULT;
\c - - - :worker_1_port
CREATE USER full_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER usage_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER read_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER no_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE ROLE some_role;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
GRANT some_role TO full_access;
GRANT some_role TO read_access;
GRANT ALL ON TABLE test_1420000 TO full_access;
GRANT SELECT ON TABLE test_1420000 TO read_access;
GRANT ALL ON TABLE test_1420002 TO full_access;
GRANT SELECT ON TABLE test_1420002 TO read_access;
CREATE SCHEMA full_access_user_schema;
REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC;
GRANT USAGE ON SCHEMA full_access_user_schema TO full_access;
GRANT ALL ON SCHEMA full_access_user_schema TO full_access;
GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access;
\c - - - :worker_2_port
CREATE USER full_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER usage_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER read_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE USER no_access;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
CREATE ROLE some_role;
NOTICE:  not propagating CREATE ROLE/USER commands to worker nodes
HINT:  Connect to worker nodes directly to manually create all necessary users and roles.
GRANT some_role TO full_access;
GRANT some_role TO read_access;
GRANT ALL ON TABLE test_1420001 TO full_access;
GRANT SELECT ON TABLE test_1420001 TO read_access;
GRANT ALL ON TABLE test_1420003 TO full_access;
GRANT SELECT ON TABLE test_1420003 TO read_access;
CREATE SCHEMA full_access_user_schema;
REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC;
GRANT USAGE ON SCHEMA full_access_user_schema TO full_access;
GRANT ALL ON SCHEMA full_access_user_schema TO full_access;
GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access;
\c - - - :master_port
SET citus.replication_model TO 'streaming';
SET citus.shard_replication_factor TO 1;
-- create prepare tests
PREPARE prepare_insert AS INSERT INTO test VALUES ($1);
PREPARE prepare_select AS SELECT count(*) FROM test;
-- not allowed to read absolute paths, even as superuser
COPY "/etc/passwd" TO STDOUT WITH (format transmit);
ERROR:  absolute path not allowed
-- not allowed to read paths outside pgsql_job_cache, even as superuser
COPY "postgresql.conf" TO STDOUT WITH (format transmit);
ERROR:  path must be in the pgsql_job_cache directory
-- check full permission
SET ROLE full_access;
EXECUTE prepare_insert(1);
EXECUTE prepare_select;
 count
---------------------------------------------------------------------
     1
(1 row)

INSERT INTO test VALUES (2);
SELECT count(*) FROM test;
 count
---------------------------------------------------------------------
     2
(1 row)

SELECT count(*) FROM test WHERE id = 1;
 count
---------------------------------------------------------------------
     1
(1 row)

SET citus.task_executor_type TO 'task-tracker';
SELECT count(*), min(current_user) FROM test;
 count |     min
---------------------------------------------------------------------
     2 | full_access
(1 row)

-- test re-partition query (needs to transmit intermediate results)
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
 count
---------------------------------------------------------------------
     0
(1 row)

SET citus.task_executor_type TO 'adaptive';
SET citus.enable_repartition_joins TO true;
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
 count
---------------------------------------------------------------------
     0
(1 row)

-- should not be able to transmit directly
COPY "postgresql.conf" TO STDOUT WITH (format transmit);
ERROR:  operation is not allowed
HINT:  Run the command with a superuser.
RESET citus.task_executor_type;
-- should not be able to transmit directly
COPY "postgresql.conf" TO STDOUT WITH (format transmit);
ERROR:  operation is not allowed
HINT:  Run the command with a superuser.
-- create a task that other users should not be able to inspect
SELECT task_tracker_assign_task(1, 1, 'SELECT 1');
 task_tracker_assign_task
---------------------------------------------------------------------

(1 row)

-- check read permission
SET ROLE read_access;
-- should be allowed to run commands, as the current user
SELECT result FROM run_command_on_workers($$SELECT current_user$$);
   result
---------------------------------------------------------------------
 read_access
 read_access
(2 rows)

SELECT result FROM run_command_on_placements('test', $$SELECT current_user$$);
   result
---------------------------------------------------------------------
 read_access
 read_access
 read_access
 read_access
(4 rows)

SELECT result FROM run_command_on_colocated_placements('test', 'test_coloc', $$SELECT current_user$$);
   result
---------------------------------------------------------------------
 read_access
 read_access
 read_access
 read_access
(4 rows)

EXECUTE prepare_insert(1);
ERROR:  permission denied for table test
EXECUTE prepare_select;
 count
---------------------------------------------------------------------
     2
(1 row)

INSERT INTO test VALUES (2);
ERROR:  permission denied for table test
SELECT count(*) FROM test;
 count
---------------------------------------------------------------------
     2
(1 row)

SELECT count(*) FROM test WHERE id = 1;
 count
---------------------------------------------------------------------
     1
(1 row)

SET citus.task_executor_type TO 'task-tracker';
SELECT count(*), min(current_user) FROM test;
 count |     min
---------------------------------------------------------------------
     2 | read_access
(1 row)

-- test re-partition query (needs to transmit intermediate results)
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
 count
---------------------------------------------------------------------
     0
(1 row)

SET citus.task_executor_type TO 'adaptive';
SET citus.enable_repartition_joins TO true;
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
 count
---------------------------------------------------------------------
     0
(1 row)

-- should not be able to transmit directly
COPY "postgresql.conf" TO STDOUT WITH (format transmit);
ERROR:  operation is not allowed
HINT:  Run the command with a superuser.
-- should not be able to access tasks or jobs belonging to a different user
SELECT task_tracker_task_status(1, 1);
ERROR:  could not find the worker task
DETAIL:  Task jobId: 1 and taskId: 1
SELECT task_tracker_assign_task(1, 2, 'SELECT 1');
ERROR:  must be owner of schema pg_merge_job_0001
SELECT task_tracker_cleanup_job(1);
ERROR:  must be owner of schema pg_merge_job_0001
-- should not be allowed to take aggressive locks on table
BEGIN;
SELECT lock_relation_if_exists('test', 'ACCESS SHARE');
 lock_relation_if_exists
---------------------------------------------------------------------
 t
(1 row)

SELECT lock_relation_if_exists('test', 'EXCLUSIVE');
ERROR:  permission denied for table test
ABORT;
RESET citus.task_executor_type;
-- check no permission
SET ROLE no_access;
EXECUTE prepare_insert(1);
ERROR:  permission denied for table test
EXECUTE prepare_select;
ERROR:  permission denied for table test
INSERT INTO test VALUES (2);
ERROR:  permission denied for table test
SELECT count(*) FROM test;
ERROR:  permission denied for table test
SELECT count(*) FROM test WHERE id = 1;
ERROR:  permission denied for table test
SET citus.task_executor_type TO 'task-tracker';
SELECT count(*), min(current_user) FROM test;
ERROR:  permission denied for table test
-- test re-partition query
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
ERROR:  permission denied for table test
SET citus.task_executor_type TO 'adaptive';
SET citus.enable_repartition_joins TO true;
SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2;
ERROR:  permission denied for table test
-- should not be able to transmit directly
COPY "postgresql.conf" TO STDOUT WITH (format transmit);
ERROR:  operation is not allowed
HINT:  Run the command with a superuser.
RESET citus.task_executor_type;
-- should be able to use intermediate results as any user
BEGIN;
SELECT create_intermediate_result('topten', 'SELECT s FROM generate_series(1,10) s');
 create_intermediate_result
---------------------------------------------------------------------
                         10
(1 row)

SELECT * FROM read_intermediate_result('topten', 'binary'::citus_copy_format) AS res (s int) ORDER BY s;
 s
---------------------------------------------------------------------
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)

END;
-- as long as we don't read from a table
BEGIN;
SELECT create_intermediate_result('topten', 'SELECT count(*) FROM test');
ERROR:  permission denied for table test
ABORT;
SELECT * FROM citus_stat_statements_reset();
ERROR:  permission denied for function citus_stat_statements_reset
-- should not be allowed to upgrade to reference table
SELECT upgrade_to_reference_table('singleshard');
ERROR:  must be owner of table singleshard
-- should not be allowed to co-located tables
SELECT mark_tables_colocated('test', ARRAY['test_coloc'::regclass]);
ERROR:  must be owner of table test
-- should not be allowed to take any locks
BEGIN;
SELECT lock_relation_if_exists('test', 'ACCESS SHARE');
ERROR:  permission denied for table test
ABORT;
BEGIN;
SELECT lock_relation_if_exists('test', 'EXCLUSIVE');
ERROR:  permission denied for table test
ABORT;
-- table owner should be the same on the shards, even when distributing the table as superuser
SET ROLE full_access;
CREATE TABLE my_table (id integer, val integer);
RESET ROLE;
SELECT create_distributed_table('my_table', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT result FROM run_command_on_workers($$SELECT tableowner FROM pg_tables WHERE tablename LIKE 'my_table_%' LIMIT 1$$);
   result
---------------------------------------------------------------------
 full_access
 full_access
(2 rows)

SELECT task_tracker_cleanup_job(1);
 task_tracker_cleanup_job
---------------------------------------------------------------------

(1 row)

-- table should be distributable by super user when it has data in there
SET ROLE full_access;
CREATE TABLE my_table_with_data (id integer, val integer);
INSERT INTO my_table_with_data VALUES (1,2);
RESET ROLE;
SELECT create_distributed_table('my_table_with_data', 'id');
NOTICE:  Copying data from local table...
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT count(*) FROM my_table_with_data;
 count
---------------------------------------------------------------------
     1
(1 row)

-- table that is owned by a role should be distributable by a user that has that role granted
-- while it should not be if the user has the role not granted
SET ROLE full_access;
CREATE TABLE my_role_table_with_data (id integer, val integer);
ALTER TABLE my_role_table_with_data OWNER TO some_role;
INSERT INTO my_role_table_with_data VALUES (1,2);
RESET ROLE;
-- we first try to distribute it with a user that does not have the role so we can reuse the table
SET ROLE no_access;
SELECT create_distributed_table('my_role_table_with_data', 'id');
ERROR:  must be owner of table my_role_table_with_data
RESET ROLE;
-- then we try to distribute it with a user that has the role but different then the one creating
SET ROLE read_access;
SELECT create_distributed_table('my_role_table_with_data', 'id');
NOTICE:  Copying data from local table...
 create_distributed_table
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- lastly we want to verify the table owner is set to the role, not the user that distributed
SELECT result FROM run_command_on_workers($cmd$
  SELECT tableowner FROM pg_tables WHERE tablename LIKE 'my_role_table_with_data%' LIMIT 1;
$cmd$);
  result
---------------------------------------------------------------------
 some_role
 some_role
(2 rows)

-- we want to verify a user without CREATE access cannot distribute its table, but can get
-- its table distributed by the super user
-- we want to make sure the schema and user are setup in such a way they can't create a
-- table
SET ROLE usage_access;
CREATE TABLE full_access_user_schema.t1 (id int);
ERROR:  permission denied for schema full_access_user_schema
RESET ROLE;
-- now we create the table for the user
CREATE TABLE full_access_user_schema.t1 (id int);
ALTER TABLE full_access_user_schema.t1 OWNER TO usage_access;
-- make sure we can insert data
SET ROLE usage_access;
INSERT INTO full_access_user_schema.t1 VALUES (1),(2),(3);
-- creating the table should fail with a failure on the worker machine since the user is
-- not allowed to create a table
SELECT create_distributed_table('full_access_user_schema.t1', 'id');
ERROR:  permission denied for schema full_access_user_schema
CONTEXT:  while executing command on localhost:xxxxx
RESET ROLE;
SET ROLE usage_access;
CREATE TYPE usage_access_type AS ENUM ('a', 'b');
CREATE FUNCTION usage_access_func(x usage_access_type, variadic v int[]) RETURNS int[]
    LANGUAGE plpgsql AS 'begin return v; end;';
SET ROLE no_access;
SELECT create_distributed_function('usage_access_func(usage_access_type,int[])');
ERROR:  must be owner of function usage_access_func
SET ROLE usage_access;
SELECT create_distributed_function('usage_access_func(usage_access_type,int[])');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

SELECT typowner::regrole FROM pg_type WHERE typname = 'usage_access_type';
   typowner
---------------------------------------------------------------------
 usage_access
(1 row)

SELECT proowner::regrole FROM pg_proc WHERE proname = 'usage_access_func';
   proowner
---------------------------------------------------------------------
 usage_access
(1 row)

SELECT run_command_on_workers($$SELECT typowner::regrole FROM pg_type WHERE typname = 'usage_access_type'$$);
      run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,usage_access)
 (localhost,57638,t,usage_access)
(2 rows)

SELECT run_command_on_workers($$SELECT proowner::regrole FROM pg_proc WHERE proname = 'usage_access_func'$$);
      run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,usage_access)
 (localhost,57638,t,usage_access)
(2 rows)

SELECT wait_until_metadata_sync();
 wait_until_metadata_sync
---------------------------------------------------------------------

(1 row)

CREATE TABLE colocation_table(id text);
SELECT create_distributed_table('colocation_table','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- now, make sure that the user can use the function
-- created in the transaction
BEGIN;
CREATE FUNCTION usage_access_func_second(key int, variadic v int[]) RETURNS text
    LANGUAGE plpgsql AS 'begin return current_user; end;';
SELECT create_distributed_function('usage_access_func_second(int,int[])', '$1', colocate_with := 'colocation_table');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

SELECT usage_access_func_second(1, 2,3,4,5) FROM full_access_user_schema.t1 LIMIT 1;
 usage_access_func_second
---------------------------------------------------------------------
 usage_access
(1 row)

ROLLBACK;
CREATE FUNCTION usage_access_func_third(key int, variadic v int[]) RETURNS text
    LANGUAGE plpgsql AS 'begin return current_user; end;';
-- connect back as super user
\c - - - :master_port
-- show that the current user is a super user
SELECT usesuper FROM pg_user where usename IN (SELECT current_user);
 usesuper
---------------------------------------------------------------------
 t
(1 row)

-- superuser creates the distributed function that is owned by a regular user
SELECT create_distributed_function('usage_access_func_third(int,int[])', '$1', colocate_with := 'colocation_table');
 create_distributed_function
---------------------------------------------------------------------

(1 row)

SELECT proowner::regrole FROM pg_proc WHERE proname = 'usage_access_func_third';
   proowner
---------------------------------------------------------------------
 usage_access
(1 row)

SELECT run_command_on_workers($$SELECT proowner::regrole FROM pg_proc WHERE proname = 'usage_access_func_third'$$);
      run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,usage_access)
 (localhost,57638,t,usage_access)
(2 rows)

-- we don't want other tests to have metadata synced
-- that might change the test outputs, so we're just trying to be careful
SELECT stop_metadata_sync_to_node('localhost', :worker_1_port);
 stop_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

SELECT stop_metadata_sync_to_node('localhost', :worker_2_port);
 stop_metadata_sync_to_node
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- now we distribute the table as super user
SELECT create_distributed_table('full_access_user_schema.t1', 'id');
NOTICE:  Copying data from local table...
 create_distributed_table
---------------------------------------------------------------------

(1 row)

-- verify the owner of the shards for the distributed tables
SELECT result FROM run_command_on_workers($cmd$
  SELECT tableowner FROM pg_tables WHERE
    true
    AND schemaname = 'full_access_user_schema'
    AND tablename LIKE 't1_%'
  LIMIT 1;
$cmd$);
    result
---------------------------------------------------------------------
 usage_access
 usage_access
(2 rows)

-- a user with all privileges on a schema should be able to distribute tables
SET ROLE full_access;
CREATE TABLE full_access_user_schema.t2(id int);
SELECT create_distributed_table('full_access_user_schema.t2', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- a user with all privileges on a schema should be able to upgrade a distributed table to
-- a reference table
SET ROLE full_access;
BEGIN;
CREATE TABLE full_access_user_schema.r1(id int);
SET LOCAL citus.shard_count TO 1;
SELECT create_distributed_table('full_access_user_schema.r1', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

SELECT upgrade_to_reference_table('full_access_user_schema.r1');
 upgrade_to_reference_table
---------------------------------------------------------------------

(1 row)

COMMIT;
RESET ROLE;
-- the super user should be able to upgrade a distributed table to a reference table, even
-- if it is owned by another user
SET ROLE full_access;
BEGIN;
CREATE TABLE full_access_user_schema.r2(id int);
SET LOCAL citus.shard_count TO 1;
SELECT create_distributed_table('full_access_user_schema.r2', 'id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

COMMIT;
RESET ROLE;
-- the usage_access should not be able to upgrade the table
SET ROLE usage_access;
SELECT upgrade_to_reference_table('full_access_user_schema.r2');
ERROR:  must be owner of table r2
RESET ROLE;
-- the super user should be able
SELECT upgrade_to_reference_table('full_access_user_schema.r2');
 upgrade_to_reference_table
---------------------------------------------------------------------

(1 row)

-- verify the owner of the shards for the reference table
SELECT result FROM run_command_on_workers($cmd$
  SELECT tableowner FROM pg_tables WHERE
    true
    AND schemaname = 'full_access_user_schema'
    AND tablename LIKE 'r2_%'
  LIMIT 1;
$cmd$);
   result
---------------------------------------------------------------------
 full_access
 full_access
(2 rows)

-- super user should be the only one being able to call worker_cleanup_job_schema_cache
SELECT worker_cleanup_job_schema_cache();
 worker_cleanup_job_schema_cache
---------------------------------------------------------------------

(1 row)

SET ROLE full_access;
SELECT worker_cleanup_job_schema_cache();
ERROR:  permission denied for function worker_cleanup_job_schema_cache
SET ROLE usage_access;
SELECT worker_cleanup_job_schema_cache();
ERROR:  permission denied for function worker_cleanup_job_schema_cache
SET ROLE read_access;
SELECT worker_cleanup_job_schema_cache();
ERROR:  permission denied for function worker_cleanup_job_schema_cache
SET ROLE no_access;
SELECT worker_cleanup_job_schema_cache();
ERROR:  permission denied for function worker_cleanup_job_schema_cache
RESET ROLE;
-- to test access to files created during repartition we will create some on worker 1
\c - - - :worker_1_port
SET ROLE full_access;
SELECT worker_hash_partition_table(42,1,'SELECT a FROM generate_series(1,100) AS a', 'a', 23, ARRAY[-2147483648, -1073741824, 0, 1073741824]::int4[]);
 worker_hash_partition_table
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- all attempts for transfer are initiated from other workers
\c - - - :worker_2_port
-- super user should not be able to copy files created by a user
SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port);
WARNING:  could not open file "base/pgsql_job_cache/job_0042/task_000001/p_00001.xxxx": No such file or directory
CONTEXT:  while executing command on localhost:xxxxx
ERROR:  could not receive file "base/pgsql_job_cache/job_0042/task_000001/p_00001" from localhost:xxxxx
-- different user should not be able to fetch partition file
SET ROLE usage_access;
SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port);
WARNING:  could not open file "base/pgsql_job_cache/job_0042/task_000001/p_00001.xxxx": No such file or directory
CONTEXT:  while executing command on localhost:xxxxx
ERROR:  could not receive file "base/pgsql_job_cache/job_0042/task_000001/p_00001" from localhost:xxxxx
-- only the user whom created the files should be able to fetch
SET ROLE full_access;
SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port);
 worker_fetch_partition_file
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- now we will test that only the user who owns the fetched file is able to merge it into
-- a table
-- test that no other user can merge the downloaded file before the task is being tracked
SET ROLE usage_access;
SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']);
ERROR:  job schema does not exist
DETAIL:  must be superuser to use public schema
RESET ROLE;
SET ROLE full_access;
-- use the side effect of this function to have a schema to use, otherwise only the super
-- user could call worker_merge_files_into_table and store the results in public, which is
-- not what we want
SELECT task_tracker_assign_task(42, 1, 'SELECT 1');
 task_tracker_assign_task
---------------------------------------------------------------------

(1 row)

RESET ROLE;
-- test that no other user can merge the downloaded file after the task is being tracked
SET ROLE usage_access;
SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']);
ERROR:  must be owner of schema pg_merge_job_0042
RESET ROLE;
-- test that the super user is unable to read the contents of the intermediate file,
-- although it does create the table
SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']);
WARNING:  Task file "task_000001.xxxx" does not have expected suffix ".10"
 worker_merge_files_into_table
---------------------------------------------------------------------

(1 row)

SELECT count(*) FROM pg_merge_job_0042.task_000001;
 count
---------------------------------------------------------------------
     0
(1 row)

DROP TABLE pg_merge_job_0042.task_000001; -- drop table so we can reuse the same files for more tests
SET ROLE full_access;
SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']);
 worker_merge_files_into_table
---------------------------------------------------------------------

(1 row)

SELECT count(*) FROM pg_merge_job_0042.task_000001;
 count
---------------------------------------------------------------------
    25
(1 row)

DROP TABLE pg_merge_job_0042.task_000001; -- drop table so we can reuse the same files for more tests
RESET ROLE;
-- test that no other user can merge files and run query on the already fetched files
SET ROLE usage_access;
SELECT worker_merge_files_and_run_query(42, 1,
    'CREATE TABLE task_000001_merge(merge_column_0 int)',
    'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge'
);
ERROR:  must be owner of schema pg_merge_job_0042
RESET ROLE;
-- test that the super user is unable to read the contents of the partitioned files after
-- trying to merge with run query
SELECT worker_merge_files_and_run_query(42, 1,
    'CREATE TABLE task_000001_merge(merge_column_0 int)',
    'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge'
);
WARNING:  Task file "task_000001.xxxx" does not have expected suffix ".10"
 worker_merge_files_and_run_query
---------------------------------------------------------------------

(1 row)

SELECT count(*) FROM pg_merge_job_0042.task_000001_merge;
 count
---------------------------------------------------------------------
     0
(1 row)

SELECT count(*) FROM pg_merge_job_0042.task_000001;
 count
---------------------------------------------------------------------
     1
(1 row)

DROP TABLE pg_merge_job_0042.task_000001, pg_merge_job_0042.task_000001_merge; -- drop table so we can reuse the same files for more tests
-- test that the owner of the task can merge files and run query correctly
SET ROLE full_access;
SELECT worker_merge_files_and_run_query(42, 1,
    'CREATE TABLE task_000001_merge(merge_column_0 int)',
    'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge'
);
 worker_merge_files_and_run_query
---------------------------------------------------------------------

(1 row)

-- test that owner of task cannot execute arbitrary sql
SELECT worker_merge_files_and_run_query(42, 1,
    'CREATE TABLE task_000002_merge(merge_column_0 int)',
    'DROP USER usage_access'
);
ERROR:  permission denied to drop role
CONTEXT:  SQL statement "DROP USER usage_access"
SELECT worker_merge_files_and_run_query(42, 1,
    'DROP USER usage_access',
    'CREATE TABLE task_000002 (a) AS SELECT sum(merge_column_0) FROM task_000002_merge'
);
ERROR:  permission denied to drop role
CONTEXT:  SQL statement "DROP USER usage_access"
SELECT count(*) FROM pg_merge_job_0042.task_000001_merge;
 count
---------------------------------------------------------------------
    25
(1 row)

SELECT count(*) FROM pg_merge_job_0042.task_000001;
 count
---------------------------------------------------------------------
     1
(1 row)

DROP TABLE pg_merge_job_0042.task_000001, pg_merge_job_0042.task_000001_merge; -- drop table so we can reuse the same files for more tests
RESET ROLE;
\c - - - :master_port
SELECT run_command_on_workers($$SELECT task_tracker_cleanup_job(42);$$);
 run_command_on_workers
---------------------------------------------------------------------
 (localhost,57637,t,"")
 (localhost,57638,t,"")
(2 rows)

DROP SCHEMA full_access_user_schema CASCADE;
NOTICE:  drop cascades to 4 other objects
DETAIL:  drop cascades to table full_access_user_schema.t1
drop cascades to table full_access_user_schema.t2
drop cascades to table full_access_user_schema.r1
drop cascades to table full_access_user_schema.r2
DROP TABLE
    my_table,
    my_table_with_data,
    my_role_table_with_data,
    singleshard,
    test,
    test_coloc,
    colocation_table;
DROP USER full_access;
DROP USER read_access;
DROP USER no_access;
DROP ROLE some_role;
