diff --git a/src/backend/distributed/deparser/citus_ruleutils.c b/src/backend/distributed/deparser/citus_ruleutils.c index 530f6e720..e5445b3df 100644 --- a/src/backend/distributed/deparser/citus_ruleutils.c +++ b/src/backend/distributed/deparser/citus_ruleutils.c @@ -1359,6 +1359,10 @@ convert_aclright_to_string(int aclright) return "TEMPORARY"; case ACL_CONNECT: return "CONNECT"; +#if PG_VERSION_NUM >= PG_VERSION_17 + case ACL_MAINTAIN: + return "MAINTAIN"; +#endif default: elog(ERROR, "unrecognized aclright: %d", aclright); return NULL; diff --git a/src/test/regress/expected/multi_multiuser_master_protocol.out b/src/test/regress/expected/multi_multiuser_master_protocol.out index a6bddb7f2..9d08bf454 100644 --- a/src/test/regress/expected/multi_multiuser_master_protocol.out +++ b/src/test/regress/expected/multi_multiuser_master_protocol.out @@ -1,6 +1,11 @@ -- -- MULTI_MULTIUSER_MASTER_PROTOCOL -- +-- Test multi_multiuser_master_protocol has an alternative output file because +-- PG17's support for the MAINTAIN privilege: +-- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ecb0fd337 +-- means that calls of master_get_table_ddl_events() can show MAINTAIN and the +-- pg_class.relacl column may have 'm' for MAINTAIN ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 109079; -- Tests that check the metadata returned by the master node. At the -- same time ensure that any user, not just a superuser, can call @@ -19,6 +24,8 @@ SELECT * FROM master_get_table_ddl_events('lineitem') order by 1; GRANT DELETE ON public.lineitem TO postgres GRANT INSERT ON public.lineitem TO full_access GRANT INSERT ON public.lineitem TO postgres + GRANT MAINTAIN ON public.lineitem TO full_access + GRANT MAINTAIN ON public.lineitem TO postgres GRANT REFERENCES ON public.lineitem TO full_access GRANT REFERENCES ON public.lineitem TO postgres GRANT SELECT ON public.lineitem TO full_access @@ -31,7 +38,7 @@ SELECT * FROM master_get_table_ddl_events('lineitem') order by 1; GRANT UPDATE ON public.lineitem TO full_access GRANT UPDATE ON public.lineitem TO postgres REVOKE ALL ON public.lineitem FROM PUBLIC -(20 rows) +(22 rows) SELECT * FROM master_get_new_shardid(); master_get_new_shardid @@ -75,8 +82,9 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO postgres GRANT REFERENCES ON public.checkperm TO postgres GRANT TRIGGER ON public.checkperm TO postgres + GRANT MAINTAIN ON public.checkperm TO postgres ALTER TABLE public.checkperm OWNER TO postgres -(10 rows) +(11 rows) GRANT SELECT ON checkperm TO read_access; GRANT ALL ON checkperm TO full_access; @@ -92,6 +100,7 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO postgres GRANT REFERENCES ON public.checkperm TO postgres GRANT TRIGGER ON public.checkperm TO postgres + GRANT MAINTAIN ON public.checkperm TO postgres GRANT SELECT ON public.checkperm TO read_access GRANT INSERT ON public.checkperm TO full_access GRANT SELECT ON public.checkperm TO full_access @@ -100,8 +109,9 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO full_access GRANT REFERENCES ON public.checkperm TO full_access GRANT TRIGGER ON public.checkperm TO full_access + GRANT MAINTAIN ON public.checkperm TO full_access ALTER TABLE public.checkperm OWNER TO postgres -(18 rows) +(20 rows) REVOKE ALL ON checkperm FROM read_access; GRANT SELECT ON checkperm TO PUBLIC; @@ -117,6 +127,7 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO postgres GRANT REFERENCES ON public.checkperm TO postgres GRANT TRIGGER ON public.checkperm TO postgres + GRANT MAINTAIN ON public.checkperm TO postgres GRANT INSERT ON public.checkperm TO full_access GRANT SELECT ON public.checkperm TO full_access GRANT UPDATE ON public.checkperm TO full_access @@ -124,9 +135,10 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO full_access GRANT REFERENCES ON public.checkperm TO full_access GRANT TRIGGER ON public.checkperm TO full_access + GRANT MAINTAIN ON public.checkperm TO full_access GRANT SELECT ON public.checkperm TO PUBLIC ALTER TABLE public.checkperm OWNER TO postgres -(18 rows) +(20 rows) GRANT ALL ON checkperm TO full_access WITH GRANT OPTION; SELECT * FROM master_get_table_ddl_events('checkperm'); @@ -141,6 +153,7 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO postgres GRANT REFERENCES ON public.checkperm TO postgres GRANT TRIGGER ON public.checkperm TO postgres + GRANT MAINTAIN ON public.checkperm TO postgres GRANT INSERT ON public.checkperm TO full_access WITH GRANT OPTION GRANT SELECT ON public.checkperm TO full_access WITH GRANT OPTION GRANT UPDATE ON public.checkperm TO full_access WITH GRANT OPTION @@ -148,9 +161,10 @@ SELECT * FROM master_get_table_ddl_events('checkperm'); GRANT TRUNCATE ON public.checkperm TO full_access WITH GRANT OPTION GRANT REFERENCES ON public.checkperm TO full_access WITH GRANT OPTION GRANT TRIGGER ON public.checkperm TO full_access WITH GRANT OPTION + GRANT MAINTAIN ON public.checkperm TO full_access WITH GRANT OPTION GRANT SELECT ON public.checkperm TO PUBLIC ALTER TABLE public.checkperm OWNER TO postgres -(18 rows) +(20 rows) -- create table as superuser/postgres CREATE TABLE trivial_postgres (id int); @@ -172,10 +186,10 @@ SELECT create_distributed_table('trivial_full_access', 'id', 'append'); RESET ROLE; SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; - relname | rolname | relacl + relname | rolname | relacl --------------------------------------------------------------------- trivial_full_access | full_access | - trivial_postgres | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} (2 rows) SET citus.shard_replication_factor = 2; -- on all workers... @@ -222,26 +236,26 @@ SELECT master_create_empty_shard('trivial_full_access'); RESET ROLE; \c - - - :worker_1_port SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; - relname | rolname | relacl + relname | rolname | relacl --------------------------------------------------------------------- trivial_full_access_109081 | full_access | trivial_full_access_109083 | full_access | trivial_full_access_109085 | full_access | - trivial_postgres_109080 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} - trivial_postgres_109082 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} - trivial_postgres_109084 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109080 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} + trivial_postgres_109082 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} + trivial_postgres_109084 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} (6 rows) \c - - - :worker_2_port SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; - relname | rolname | relacl + relname | rolname | relacl --------------------------------------------------------------------- trivial_full_access_109081 | full_access | trivial_full_access_109083 | full_access | trivial_full_access_109085 | full_access | - trivial_postgres_109080 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} - trivial_postgres_109082 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} - trivial_postgres_109084 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109080 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} + trivial_postgres_109082 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} + trivial_postgres_109084 | postgres | {postgres=arwdDxtm/postgres,full_access=arwdDxtm/postgres} (6 rows) \c - - - :master_port diff --git a/src/test/regress/expected/multi_multiuser_master_protocol_0.out b/src/test/regress/expected/multi_multiuser_master_protocol_0.out new file mode 100644 index 000000000..f8422ad67 --- /dev/null +++ b/src/test/regress/expected/multi_multiuser_master_protocol_0.out @@ -0,0 +1,498 @@ +-- +-- MULTI_MULTIUSER_MASTER_PROTOCOL +-- +-- Test multi_multiuser_master_protocol has an alternative output file because +-- PG17's support for the MAINTAIN privilege: +-- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ecb0fd337 +-- means that calls of master_get_table_ddl_events() can show MAINTAIN and the +-- pg_class.relacl column may have 'm' for MAINTAIN +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 109079; +-- Tests that check the metadata returned by the master node. At the +-- same time ensure that any user, not just a superuser, can call +-- these. Note that, for now at least, any user can call these. That's +-- OK-ish, since the schema is visible from the catalogs anyway, and +-- exhausting shardids doesn't seem like a super viable attack path. +SET ROLE no_access; +SELECT * FROM master_get_table_ddl_events('lineitem') order by 1; + master_get_table_ddl_events +--------------------------------------------------------------------- + ALTER TABLE public.lineitem ADD CONSTRAINT lineitem_pkey PRIMARY KEY (l_orderkey, l_linenumber) + ALTER TABLE public.lineitem OWNER TO postgres + CREATE INDEX lineitem_time_index ON public.lineitem USING btree (l_shipdate) + CREATE TABLE public.lineitem (l_orderkey bigint NOT NULL, l_partkey integer NOT NULL, l_suppkey integer NOT NULL, l_linenumber integer NOT NULL, l_quantity numeric(15,2) NOT NULL, l_extendedprice numeric(15,2) NOT NULL, l_discount numeric(15,2) NOT NULL, l_tax numeric(15,2) NOT NULL, l_returnflag character(1) NOT NULL, l_linestatus character(1) NOT NULL, l_shipdate date NOT NULL, l_commitdate date NOT NULL, l_receiptdate date NOT NULL, l_shipinstruct character(25) NOT NULL, l_shipmode character(10) NOT NULL, l_comment character varying(44) NOT NULL) USING heap + GRANT DELETE ON public.lineitem TO full_access + GRANT DELETE ON public.lineitem TO postgres + GRANT INSERT ON public.lineitem TO full_access + GRANT INSERT ON public.lineitem TO postgres + GRANT REFERENCES ON public.lineitem TO full_access + GRANT REFERENCES ON public.lineitem TO postgres + GRANT SELECT ON public.lineitem TO full_access + GRANT SELECT ON public.lineitem TO postgres + GRANT SELECT ON public.lineitem TO read_access + GRANT TRIGGER ON public.lineitem TO full_access + GRANT TRIGGER ON public.lineitem TO postgres + GRANT TRUNCATE ON public.lineitem TO full_access + GRANT TRUNCATE ON public.lineitem TO postgres + GRANT UPDATE ON public.lineitem TO full_access + GRANT UPDATE ON public.lineitem TO postgres + REVOKE ALL ON public.lineitem FROM PUBLIC +(20 rows) + +SELECT * FROM master_get_new_shardid(); + master_get_new_shardid +--------------------------------------------------------------------- + 109079 +(1 row) + +SELECT * FROM master_get_active_worker_nodes(); + node_name | node_port +--------------------------------------------------------------------- + localhost | 57638 + localhost | 57637 +(2 rows) + +RESET ROLE; +-- ensure GRANT/REVOKE's do something sane for creating shards of +CREATE TABLE checkperm(key int); +SELECT create_distributed_table('checkperm', 'key', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM master_get_table_ddl_events('checkperm'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE public.checkperm (key integer) USING heap + ALTER TABLE public.checkperm OWNER TO postgres +(2 rows) + +REVOKE ALL ON checkperm FROM PUBLIC; +SELECT * FROM master_get_table_ddl_events('checkperm'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE public.checkperm (key integer) USING heap + REVOKE ALL ON public.checkperm FROM PUBLIC + GRANT INSERT ON public.checkperm TO postgres + GRANT SELECT ON public.checkperm TO postgres + GRANT UPDATE ON public.checkperm TO postgres + GRANT DELETE ON public.checkperm TO postgres + GRANT TRUNCATE ON public.checkperm TO postgres + GRANT REFERENCES ON public.checkperm TO postgres + GRANT TRIGGER ON public.checkperm TO postgres + ALTER TABLE public.checkperm OWNER TO postgres +(10 rows) + +GRANT SELECT ON checkperm TO read_access; +GRANT ALL ON checkperm TO full_access; +SELECT * FROM master_get_table_ddl_events('checkperm'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE public.checkperm (key integer) USING heap + REVOKE ALL ON public.checkperm FROM PUBLIC + GRANT INSERT ON public.checkperm TO postgres + GRANT SELECT ON public.checkperm TO postgres + GRANT UPDATE ON public.checkperm TO postgres + GRANT DELETE ON public.checkperm TO postgres + GRANT TRUNCATE ON public.checkperm TO postgres + GRANT REFERENCES ON public.checkperm TO postgres + GRANT TRIGGER ON public.checkperm TO postgres + GRANT SELECT ON public.checkperm TO read_access + GRANT INSERT ON public.checkperm TO full_access + GRANT SELECT ON public.checkperm TO full_access + GRANT UPDATE ON public.checkperm TO full_access + GRANT DELETE ON public.checkperm TO full_access + GRANT TRUNCATE ON public.checkperm TO full_access + GRANT REFERENCES ON public.checkperm TO full_access + GRANT TRIGGER ON public.checkperm TO full_access + ALTER TABLE public.checkperm OWNER TO postgres +(18 rows) + +REVOKE ALL ON checkperm FROM read_access; +GRANT SELECT ON checkperm TO PUBLIC; +SELECT * FROM master_get_table_ddl_events('checkperm'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE public.checkperm (key integer) USING heap + REVOKE ALL ON public.checkperm FROM PUBLIC + GRANT INSERT ON public.checkperm TO postgres + GRANT SELECT ON public.checkperm TO postgres + GRANT UPDATE ON public.checkperm TO postgres + GRANT DELETE ON public.checkperm TO postgres + GRANT TRUNCATE ON public.checkperm TO postgres + GRANT REFERENCES ON public.checkperm TO postgres + GRANT TRIGGER ON public.checkperm TO postgres + GRANT INSERT ON public.checkperm TO full_access + GRANT SELECT ON public.checkperm TO full_access + GRANT UPDATE ON public.checkperm TO full_access + GRANT DELETE ON public.checkperm TO full_access + GRANT TRUNCATE ON public.checkperm TO full_access + GRANT REFERENCES ON public.checkperm TO full_access + GRANT TRIGGER ON public.checkperm TO full_access + GRANT SELECT ON public.checkperm TO PUBLIC + ALTER TABLE public.checkperm OWNER TO postgres +(18 rows) + +GRANT ALL ON checkperm TO full_access WITH GRANT OPTION; +SELECT * FROM master_get_table_ddl_events('checkperm'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE public.checkperm (key integer) USING heap + REVOKE ALL ON public.checkperm FROM PUBLIC + GRANT INSERT ON public.checkperm TO postgres + GRANT SELECT ON public.checkperm TO postgres + GRANT UPDATE ON public.checkperm TO postgres + GRANT DELETE ON public.checkperm TO postgres + GRANT TRUNCATE ON public.checkperm TO postgres + GRANT REFERENCES ON public.checkperm TO postgres + GRANT TRIGGER ON public.checkperm TO postgres + GRANT INSERT ON public.checkperm TO full_access WITH GRANT OPTION + GRANT SELECT ON public.checkperm TO full_access WITH GRANT OPTION + GRANT UPDATE ON public.checkperm TO full_access WITH GRANT OPTION + GRANT DELETE ON public.checkperm TO full_access WITH GRANT OPTION + GRANT TRUNCATE ON public.checkperm TO full_access WITH GRANT OPTION + GRANT REFERENCES ON public.checkperm TO full_access WITH GRANT OPTION + GRANT TRIGGER ON public.checkperm TO full_access WITH GRANT OPTION + GRANT SELECT ON public.checkperm TO PUBLIC + ALTER TABLE public.checkperm OWNER TO postgres +(18 rows) + +-- create table as superuser/postgres +CREATE TABLE trivial_postgres (id int); +SELECT create_distributed_table('trivial_postgres', 'id', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +GRANT ALL ON trivial_postgres TO full_access; +GRANT CREATE ON SCHEMA public TO full_access; +SET ROLE full_access; +CREATE TABLE trivial_full_access (id int); +SELECT create_distributed_table('trivial_full_access', 'id', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +RESET ROLE; +SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; + relname | rolname | relacl +--------------------------------------------------------------------- + trivial_full_access | full_access | + trivial_postgres | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} +(2 rows) + +SET citus.shard_replication_factor = 2; -- on all workers... +-- create shards as each user, verify ownership +SELECT master_create_empty_shard('trivial_postgres'); + master_create_empty_shard +--------------------------------------------------------------------- + 109080 +(1 row) + +SELECT master_create_empty_shard('trivial_full_access'); + master_create_empty_shard +--------------------------------------------------------------------- + 109081 +(1 row) + +SET ROLE full_access; +SELECT master_create_empty_shard('trivial_postgres'); + master_create_empty_shard +--------------------------------------------------------------------- + 109082 +(1 row) + +SELECT master_create_empty_shard('trivial_full_access'); + master_create_empty_shard +--------------------------------------------------------------------- + 109083 +(1 row) + +RESET ROLE; +SET ROLE full_access; +SELECT master_create_empty_shard('trivial_postgres'); + master_create_empty_shard +--------------------------------------------------------------------- + 109084 +(1 row) + +SELECT master_create_empty_shard('trivial_full_access'); + master_create_empty_shard +--------------------------------------------------------------------- + 109085 +(1 row) + +RESET ROLE; +\c - - - :worker_1_port +SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; + relname | rolname | relacl +--------------------------------------------------------------------- + trivial_full_access_109081 | full_access | + trivial_full_access_109083 | full_access | + trivial_full_access_109085 | full_access | + trivial_postgres_109080 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109082 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109084 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} +(6 rows) + +\c - - - :worker_2_port +SELECT relname, rolname, relacl FROM pg_class JOIN pg_roles ON (pg_roles.oid = pg_class.relowner) WHERE relname LIKE 'trivial%' ORDER BY relname; + relname | rolname | relacl +--------------------------------------------------------------------- + trivial_full_access_109081 | full_access | + trivial_full_access_109083 | full_access | + trivial_full_access_109085 | full_access | + trivial_postgres_109080 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109082 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} + trivial_postgres_109084 | postgres | {postgres=arwdDxt/postgres,full_access=arwdDxt/postgres} +(6 rows) + +\c - - - :master_port +-- ensure COPY into append tables works +CREATE TABLE stage_postgres(id) AS SELECT 2; +GRANT ALL ON stage_postgres TO full_access; +SET ROLE full_access; +CREATE TABLE stage_full_access(id) AS SELECT 1; +RESET ROLE; +SELECT master_create_empty_shard('trivial_postgres') AS shardid \gset +COPY trivial_postgres FROM STDIN WITH (append_to_shard :shardid); +SELECT master_create_empty_shard('trivial_full_access') AS shardid \gset +COPY trivial_full_access FROM STDIN WITH (append_to_shard :shardid); +SET ROLE full_access; +SELECT master_create_empty_shard('trivial_postgres') AS shardid \gset +COPY trivial_postgres FROM STDIN WITH (append_to_shard :shardid); +SELECT master_create_empty_shard('trivial_full_access') AS shardid \gset +COPY trivial_full_access FROM STDIN WITH (append_to_shard :shardid); +RESET ROLE; +SELECT * FROM trivial_postgres ORDER BY id; + id +--------------------------------------------------------------------- + 1 + 1 + 2 + 2 +(4 rows) + +SELECT * FROM trivial_full_access ORDER BY id; + id +--------------------------------------------------------------------- + 1 + 1 + 2 + 2 +(4 rows) + +SET ROLE full_access; +SELECT * FROM trivial_postgres ORDER BY id; + id +--------------------------------------------------------------------- + 1 + 1 + 2 + 2 +(4 rows) + +SELECT * FROM trivial_full_access ORDER BY id; + id +--------------------------------------------------------------------- + 1 + 1 + 2 + 2 +(4 rows) + +RESET ROLE; +-- verify column level grants are not supported +GRANT UPDATE (id) ON trivial_postgres TO read_access; +ERROR: grant/revoke on column list is currently unsupported +DROP TABLE trivial_full_access; +DROP TABLE trivial_postgres; +DROP TABLE stage_full_access; +DROP TABLE stage_postgres; +-- test GRANT/REVOKE on all tables in schema +CREATE SCHEMA multiuser_schema; +CREATE TABLE multiuser_schema.hash_table(a int, b int); +CREATE TABLE multiuser_schema.reference_table(a int, b int); +SET citus.shard_replication_factor TO 1; +SELECT create_distributed_table('multiuser_schema.hash_table', 'a', colocate_with => 'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- usage right must be granted to user +GRANT USAGE ON SCHEMA multiuser_schema TO read_access; +-- verify test user (read_access) does not have select privilege on both tables +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109090 | t | f + localhost | 57637 | 109092 | t | f + localhost | 57638 | 109091 | t | f + localhost | 57638 | 109093 | t | f +(4 rows) + +-- grant select +GRANT SELECT ON ALL TABLES IN SCHEMA multiuser_schema TO read_access; +-- verify select is granted +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109090 | t | t + localhost | 57637 | 109092 | t | t + localhost | 57638 | 109091 | t | t + localhost | 57638 | 109093 | t | t +(4 rows) + +-- distribute the second table +SELECT create_reference_table('multiuser_schema.reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- verify select is also granted +SELECT * FROM run_command_on_placements('multiuser_schema.reference_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57636 | 109094 | t | t + localhost | 57637 | 109094 | t | t + localhost | 57638 | 109094 | t | t +(3 rows) + +-- create another table in the schema, verify select is not granted +CREATE TABLE multiuser_schema.another_table(a int, b int); +SELECT create_distributed_table('multiuser_schema.another_table', 'a', colocate_with => 'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM run_command_on_placements('multiuser_schema.another_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109095 | t | f + localhost | 57637 | 109097 | t | f + localhost | 57638 | 109096 | t | f + localhost | 57638 | 109098 | t | f +(4 rows) + +-- grant select again, verify it is granted +GRANT SELECT ON ALL TABLES IN SCHEMA multiuser_schema TO read_access; +SELECT * FROM run_command_on_placements('multiuser_schema.another_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109095 | t | t + localhost | 57637 | 109097 | t | t + localhost | 57638 | 109096 | t | t + localhost | 57638 | 109098 | t | t +(4 rows) + +-- verify isolate tenant carries grants +SELECT isolate_tenant_to_new_shard('multiuser_schema.hash_table', 5, shard_transfer_mode => 'block_writes'); + isolate_tenant_to_new_shard +--------------------------------------------------------------------- + 109100 +(1 row) + +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109092 | t | t + localhost | 57637 | 109099 | t | t + localhost | 57637 | 109100 | t | t + localhost | 57637 | 109101 | t | t + localhost | 57638 | 109091 | t | t + localhost | 57638 | 109093 | t | t +(6 rows) + +-- revoke select +REVOKE SELECT ON ALL TABLES IN SCHEMA multiuser_schema FROM read_access; +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109092 | t | f + localhost | 57637 | 109099 | t | f + localhost | 57637 | 109100 | t | f + localhost | 57637 | 109101 | t | f + localhost | 57638 | 109091 | t | f + localhost | 57638 | 109093 | t | f +(6 rows) + +-- test multi-schema grants +CREATE SCHEMA multiuser_second_schema; +CREATE TABLE multiuser_second_schema.hash_table(a int, b int); +SELECT create_distributed_table('multiuser_second_schema.hash_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +GRANT ALL ON ALL TABLES IN SCHEMA multiuser_schema, multiuser_second_schema TO read_access; +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109092 | t | t + localhost | 57637 | 109099 | t | t + localhost | 57637 | 109100 | t | t + localhost | 57637 | 109101 | t | t + localhost | 57638 | 109091 | t | t + localhost | 57638 | 109093 | t | t +(6 rows) + +SELECT * FROM run_command_on_placements('multiuser_second_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109102 | t | t + localhost | 57637 | 109103 | t | t + localhost | 57637 | 109104 | t | t + localhost | 57637 | 109106 | t | t + localhost | 57638 | 109105 | t | t + localhost | 57638 | 109107 | t | t +(6 rows) + +-- revoke from multiple schemas, verify result +REVOKE SELECT ON ALL TABLES IN SCHEMA multiuser_schema, multiuser_second_schema FROM read_access; +SELECT * FROM run_command_on_placements('multiuser_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109092 | t | f + localhost | 57637 | 109099 | t | f + localhost | 57637 | 109100 | t | f + localhost | 57637 | 109101 | t | f + localhost | 57638 | 109091 | t | f + localhost | 57638 | 109093 | t | f +(6 rows) + +SELECT * FROM run_command_on_placements('multiuser_second_schema.hash_table', $$ select has_table_privilege('read_access', '%s', 'select') $$) +ORDER BY nodename, nodeport, shardid; + nodename | nodeport | shardid | success | result +--------------------------------------------------------------------- + localhost | 57637 | 109102 | t | f + localhost | 57637 | 109103 | t | f + localhost | 57637 | 109104 | t | f + localhost | 57637 | 109106 | t | f + localhost | 57638 | 109105 | t | f + localhost | 57638 | 109107 | t | f +(6 rows) + +DROP SCHEMA multiuser_schema CASCADE; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table multiuser_schema.hash_table +drop cascades to table multiuser_schema.reference_table +drop cascades to table multiuser_schema.reference_table_109094 +drop cascades to table multiuser_schema.another_table +DROP SCHEMA multiuser_second_schema CASCADE; +NOTICE: drop cascades to table multiuser_second_schema.hash_table diff --git a/src/test/regress/expected/pg17.out b/src/test/regress/expected/pg17.out index 563808787..aa4ccb64c 100644 --- a/src/test/regress/expected/pg17.out +++ b/src/test/regress/expected/pg17.out @@ -338,9 +338,6 @@ DEBUG: Router planner cannot handle multi-shard select queries RESET client_min_messages; RESET search_path; -RESET citus.next_shard_id; -RESET citus.shard_count; -RESET citus.shard_replication_factor; DROP SCHEMA pg17_corr_subq_folding CASCADE; NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table pg17_corr_subq_folding.test @@ -353,19 +350,19 @@ drop cascades to table pg17_corr_subq_folding.events -- PG17-specific tests go here. -- CREATE SCHEMA pg17; -SET search_path TO pg17; +SET search_path to pg17; -- Test specifying access method on partitioned tables. PG17 feature, added by: -- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=374c7a229 -- The following tests were failing tests in tableam but will pass on PG >= 17. -- There is some set-up duplication of tableam, and this test can be returned -- to tableam when 17 is the minimum supported PG version. SELECT public.run_command_on_coordinator_and_workers($Q$ - SET citus.enable_ddl_propagation TO off; - CREATE FUNCTION fake_am_handler(internal) - RETURNS table_am_handler - AS 'citus' - LANGUAGE C; - CREATE ACCESS METHOD fake_am TYPE TABLE HANDLER fake_am_handler; + SET citus.enable_ddl_propagation TO off; + CREATE FUNCTION fake_am_handler(internal) + RETURNS table_am_handler + AS 'citus' + LANGUAGE C; + CREATE ACCESS METHOD fake_am TYPE TABLE HANDLER fake_am_handler; $Q$); run_command_on_coordinator_and_workers --------------------------------------------------------------------- @@ -379,9 +376,9 @@ CREATE TABLE test_partitioned(id int, p int, val int) PARTITION BY RANGE (p) USING fake_am; -- Test that children inherit access method from parent CREATE TABLE test_partitioned_p1 PARTITION OF test_partitioned - FOR VALUES FROM (1) TO (10); + FOR VALUES FROM (1) TO (10); CREATE TABLE test_partitioned_p2 PARTITION OF test_partitioned - FOR VALUES FROM (11) TO (20); + FOR VALUES FROM (11) TO (20); INSERT INTO test_partitioned VALUES (1, 5, -1), (2, 15, -2); WARNING: fake_tuple_insert WARNING: fake_tuple_insert @@ -416,10 +413,93 @@ ORDER BY c.relname; test_partitioned_p2 | fake_am (2 rows) +-- Clean up DROP TABLE test_partitioned; ALTER EXTENSION citus DROP ACCESS METHOD fake_am; +SELECT public.run_command_on_coordinator_and_workers($Q$ + RESET citus.enable_ddl_propagation; +$Q$); + run_command_on_coordinator_and_workers +--------------------------------------------------------------------- + +(1 row) + -- End of testing specifying access method on partitioned tables. +-- MAINTAIN privilege tests +CREATE ROLE regress_maintain; +CREATE ROLE regress_no_maintain; +ALTER ROLE regress_maintain WITH login; +GRANT USAGE ON SCHEMA pg17 TO regress_maintain; +ALTER ROLE regress_no_maintain WITH login; +GRANT USAGE ON SCHEMA pg17 TO regress_no_maintain; +SET citus.shard_count TO 1; -- For consistent remote command logging +CREATE TABLE dist_test(a int, b int); +SELECT create_distributed_table('dist_test', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO dist_test SELECT i % 10, i FROM generate_series(1, 100) t(i); +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%maintain%'; +GRANT MAINTAIN ON dist_test TO regress_maintain; +NOTICE: issuing GRANT maintain ON dist_test TO regress_maintain +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT maintain ON dist_test TO regress_maintain +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT worker_apply_shard_ddl_command (20240023, 'pg17', 'GRANT maintain ON dist_test TO regress_maintain') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +RESET citus.grep_remote_commands; +SET ROLE regress_no_maintain; +-- Current role does not have MAINTAIN privileges on dist_test +ANALYZE dist_test; +WARNING: permission denied to analyze "dist_test", skipping it +NOTICE: issuing ANALYZE pg17.dist_test_20240023 +DETAIL: on server regress_no_maintain@localhost:xxxxx connectionId: xxxxxxx +VACUUM dist_test; +WARNING: permission denied to vacuum "dist_test", skipping it +NOTICE: issuing VACUUM pg17.dist_test_20240023 +DETAIL: on server regress_no_maintain@localhost:xxxxx connectionId: xxxxxxx +SET ROLE regress_maintain; +-- Current role has MAINTAIN privileges on dist_test +ANALYZE dist_test; +NOTICE: issuing ANALYZE pg17.dist_test_20240023 +DETAIL: on server regress_maintain@localhost:xxxxx connectionId: xxxxxxx +VACUUM dist_test; +NOTICE: issuing VACUUM pg17.dist_test_20240023 +DETAIL: on server regress_maintain@localhost:xxxxx connectionId: xxxxxxx +-- Take away regress_maintain's MAINTAIN privileges on dist_test +RESET ROLE; +SET citus.grep_remote_commands = '%maintain%'; +REVOKE MAINTAIN ON dist_test FROM regress_maintain; +NOTICE: issuing REVOKE maintain ON dist_test FROM regress_maintain +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing REVOKE maintain ON dist_test FROM regress_maintain +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT worker_apply_shard_ddl_command (20240023, 'pg17', 'REVOKE maintain ON dist_test FROM regress_maintain') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +RESET citus.grep_remote_commands; +SET ROLE regress_maintain; +-- Current role does not have MAINTAIN privileges on dist_test +ANALYZE dist_test; +WARNING: permission denied to analyze "dist_test", skipping it +NOTICE: issuing ANALYZE pg17.dist_test_20240023 +DETAIL: on server regress_maintain@localhost:xxxxx connectionId: xxxxxxx +VACUUM dist_test; +WARNING: permission denied to vacuum "dist_test", skipping it +NOTICE: issuing VACUUM pg17.dist_test_20240023 +DETAIL: on server regress_maintain@localhost:xxxxx connectionId: xxxxxxx +RESET ROLE; +-- End of MAINTAIN privilege tests +RESET citus.log_remote_commands; +RESET citus.next_shard_id; +RESET citus.shard_count; +RESET citus.shard_replication_factor; DROP SCHEMA pg17 CASCADE; -NOTICE: drop cascades to 2 other objects +NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to function fake_am_handler(internal) drop cascades to access method fake_am +drop cascades to table dist_test +DROP ROLE regress_maintain; +DROP ROLE regress_no_maintain; diff --git a/src/test/regress/expected/pg17_0.out b/src/test/regress/expected/pg17_0.out index 66dba2c29..09db03e4c 100644 --- a/src/test/regress/expected/pg17_0.out +++ b/src/test/regress/expected/pg17_0.out @@ -282,9 +282,6 @@ DEBUG: Router planner cannot handle multi-shard select queries RESET client_min_messages; RESET search_path; -RESET citus.next_shard_id; -RESET citus.shard_count; -RESET citus.shard_replication_factor; DROP SCHEMA pg17_corr_subq_folding CASCADE; NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table pg17_corr_subq_folding.test diff --git a/src/test/regress/sql/multi_multiuser_master_protocol.sql b/src/test/regress/sql/multi_multiuser_master_protocol.sql index 535500004..e13605fff 100644 --- a/src/test/regress/sql/multi_multiuser_master_protocol.sql +++ b/src/test/regress/sql/multi_multiuser_master_protocol.sql @@ -2,6 +2,12 @@ -- MULTI_MULTIUSER_MASTER_PROTOCOL -- +-- Test multi_multiuser_master_protocol has an alternative output file because +-- PG17's support for the MAINTAIN privilege: +-- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ecb0fd337 +-- means that calls of master_get_table_ddl_events() can show MAINTAIN and the +-- pg_class.relacl column may have 'm' for MAINTAIN + ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 109079; -- Tests that check the metadata returned by the master node. At the diff --git a/src/test/regress/sql/pg17.sql b/src/test/regress/sql/pg17.sql index e297a0c58..3c9a2541c 100644 --- a/src/test/regress/sql/pg17.sql +++ b/src/test/regress/sql/pg17.sql @@ -168,9 +168,6 @@ GROUP BY dept; RESET client_min_messages; RESET search_path; -RESET citus.next_shard_id; -RESET citus.shard_count; -RESET citus.shard_replication_factor; DROP SCHEMA pg17_corr_subq_folding CASCADE; \if :server_version_ge_17 @@ -181,7 +178,7 @@ DROP SCHEMA pg17_corr_subq_folding CASCADE; -- PG17-specific tests go here. -- CREATE SCHEMA pg17; -SET search_path TO pg17; +SET search_path to pg17; -- Test specifying access method on partitioned tables. PG17 feature, added by: -- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=374c7a229 @@ -190,12 +187,12 @@ SET search_path TO pg17; -- to tableam when 17 is the minimum supported PG version. SELECT public.run_command_on_coordinator_and_workers($Q$ - SET citus.enable_ddl_propagation TO off; - CREATE FUNCTION fake_am_handler(internal) - RETURNS table_am_handler - AS 'citus' - LANGUAGE C; - CREATE ACCESS METHOD fake_am TYPE TABLE HANDLER fake_am_handler; + SET citus.enable_ddl_propagation TO off; + CREATE FUNCTION fake_am_handler(internal) + RETURNS table_am_handler + AS 'citus' + LANGUAGE C; + CREATE ACCESS METHOD fake_am TYPE TABLE HANDLER fake_am_handler; $Q$); -- Since Citus assumes access methods are part of the extension, make fake_am @@ -207,9 +204,9 @@ PARTITION BY RANGE (p) USING fake_am; -- Test that children inherit access method from parent CREATE TABLE test_partitioned_p1 PARTITION OF test_partitioned - FOR VALUES FROM (1) TO (10); + FOR VALUES FROM (1) TO (10); CREATE TABLE test_partitioned_p2 PARTITION OF test_partitioned - FOR VALUES FROM (11) TO (20); + FOR VALUES FROM (11) TO (20); INSERT INTO test_partitioned VALUES (1, 5, -1), (2, 15, -2); INSERT INTO test_partitioned VALUES (3, 6, -6), (4, 16, -4); @@ -222,9 +219,66 @@ SELECT c.relname, am.amname FROM pg_class c, pg_am am WHERE c.relam = am.oid AND c.oid IN ('test_partitioned_p1'::regclass, 'test_partitioned_p2'::regclass) ORDER BY c.relname; +-- Clean up DROP TABLE test_partitioned; ALTER EXTENSION citus DROP ACCESS METHOD fake_am; +SELECT public.run_command_on_coordinator_and_workers($Q$ + RESET citus.enable_ddl_propagation; +$Q$); -- End of testing specifying access method on partitioned tables. +-- MAINTAIN privilege tests + +CREATE ROLE regress_maintain; +CREATE ROLE regress_no_maintain; + +ALTER ROLE regress_maintain WITH login; +GRANT USAGE ON SCHEMA pg17 TO regress_maintain; +ALTER ROLE regress_no_maintain WITH login; +GRANT USAGE ON SCHEMA pg17 TO regress_no_maintain; + +SET citus.shard_count TO 1; -- For consistent remote command logging +CREATE TABLE dist_test(a int, b int); +SELECT create_distributed_table('dist_test', 'a'); +INSERT INTO dist_test SELECT i % 10, i FROM generate_series(1, 100) t(i); + +SET citus.log_remote_commands TO on; + +SET citus.grep_remote_commands = '%maintain%'; +GRANT MAINTAIN ON dist_test TO regress_maintain; +RESET citus.grep_remote_commands; + +SET ROLE regress_no_maintain; +-- Current role does not have MAINTAIN privileges on dist_test +ANALYZE dist_test; +VACUUM dist_test; + +SET ROLE regress_maintain; +-- Current role has MAINTAIN privileges on dist_test +ANALYZE dist_test; +VACUUM dist_test; + +-- Take away regress_maintain's MAINTAIN privileges on dist_test +RESET ROLE; +SET citus.grep_remote_commands = '%maintain%'; +REVOKE MAINTAIN ON dist_test FROM regress_maintain; +RESET citus.grep_remote_commands; + +SET ROLE regress_maintain; +-- Current role does not have MAINTAIN privileges on dist_test +ANALYZE dist_test; +VACUUM dist_test; + +RESET ROLE; + +-- End of MAINTAIN privilege tests + +RESET citus.log_remote_commands; +RESET citus.next_shard_id; +RESET citus.shard_count; +RESET citus.shard_replication_factor; + DROP SCHEMA pg17 CASCADE; +DROP ROLE regress_maintain; +DROP ROLE regress_no_maintain;