This is an automated email from the ASF dual-hosted git repository. yjhjstz pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudberry.git
commit 60eb05436a23474ea3afbbebe211587f0c77ec4e Author: Huansong Fu <[email protected]> AuthorDate: Wed Oct 19 13:08:15 2022 -0700 Support ONLY keyword for GRANT/REVOKE Historically in GPDB6, when executing GRANT/REVOKE on a partition root, we will recurse into its child tables. We have decided to keep this behavior for 7X, but to also support the ONLY keyword so that users can choose to grant/revoke just the partition root, without recursion. The syntax is e.g.: GRANT SELECT ON ONLY foo_par TO role; Discussion: https://groups.google.com/a/greenplum.org/g/gpdb-dev/c/ImJrvQEECwA --- src/backend/catalog/aclchk.c | 6 +- src/backend/parser/gram.y | 27 ++++++- src/test/regress/expected/partition.out | 89 +++++++++++++++++++++++ src/test/regress/expected/partition_optimizer.out | 89 +++++++++++++++++++++++ src/test/regress/sql/partition.sql | 32 ++++++++ 5 files changed, 238 insertions(+), 5 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index d6a496aaf8..ccb5eeba9a 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -740,9 +740,11 @@ objectNamesToOids(ObjectType objtype, List *objnames) * GPDB: If we the object is a partitioned relation, also * recurse to the child partitions. It is different from * PostgreSQL, but it is how GRANT has historically worked on - * GPDB. + * GPDB. Unless it is indicated that we should not recurse + * (e.g. by specifying the 'ONLY' keyword), in which case + * don't bother finding the inheritors. */ - if (objtype == OBJECT_TABLE) + if (objtype == OBJECT_TABLE && relvar->inh) { HeapTuple tp; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 06540bd216..c93b839acd 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -463,7 +463,7 @@ static void check_expressions_in_partition_key(PartitionSpec *spec, core_yyscan_ sort_clause opt_sort_clause sortby_list index_params stats_params opt_include opt_c_include index_including_params name_list role_list from_clause from_list opt_array_bounds - qualified_name_list any_name any_name_list type_name_list + qualified_name_list qualified_name_list_with_only any_name any_name_list type_name_list any_operator expr_list attrs distinct_clause opt_distinct_clause target_list opt_target_list insert_column_list set_target_list @@ -10474,7 +10474,7 @@ privilege: SELECT opt_column_list * opt_table. You're going to get conflicts. */ privilege_target: - qualified_name_list + qualified_name_list_with_only { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); n->targtype = ACL_TARGET_OBJECT; @@ -10482,7 +10482,7 @@ privilege_target: n->objs = $1; $$ = n; } - | TABLE qualified_name_list + | TABLE qualified_name_list_with_only { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); n->targtype = ACL_TARGET_OBJECT; @@ -19062,6 +19062,27 @@ qualified_name_list: | qualified_name_list ',' qualified_name { $$ = lappend($1, $3); } ; +qualified_name_list_with_only: + qualified_name + { + $$ = list_make1($1); + } + | ONLY qualified_name + { + $2->inh = false; + $$ = list_make1($2); + } + | qualified_name_list ',' qualified_name + { + $$ = lappend($1, $3); + } + | qualified_name_list ',' ONLY qualified_name + { + $4->inh = false; + $$ = lappend($1, $4); + } + ; + /* * The production for a qualified relation name has to exactly match the * production for a qualified func_name, because in a FROM clause we cannot diff --git a/src/test/regress/expected/partition.out b/src/test/regress/expected/partition.out index 895897a4c7..207450c719 100755 --- a/src/test/regress/expected/partition.out +++ b/src/test/regress/expected/partition.out @@ -283,9 +283,98 @@ select has_table_privilege('part_role', 'bar_p'::regclass, 'select'); t (1 row) +-- the ONLY keyword will affect just the partition root for both grant/revoke +create role part_role2; +grant select on only foo_p to part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +grant select on foo_p to part_role2; +revoke select on only foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +revoke select on foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +create table foo_p2 (a int, b int) partition by range(a) (start(1) end(10) every(1)); +grant select on foo_p, only foo_p2 to part_role2; -- multiple tables in same statement +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p2'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p2_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +-- more cases +revoke all on foo_p from part_role2; +revoke all on foo_p2 from part_role2; +grant select on only public.foo_p to part_role2; -- with schema +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +grant update(b) on only foo_p2 to part_role2; -- column level priviledge +select relname, has_column_privilege('part_role2', oid, 'b', 'update') from pg_class +where relname = 'foo_p2' or relname = 'foo_p2_1_prt_6'; + relname | has_column_privilege +----------------+---------------------- + foo_p2 | t + foo_p2_1_prt_6 | f +(2 rows) + drop table foo_p; +drop table foo_p2; drop table bar_p; drop role part_role; +drop role part_role2; -- validation create table foo_p (i int) partition by range(i) (start(1) end(10) every(1)); diff --git a/src/test/regress/expected/partition_optimizer.out b/src/test/regress/expected/partition_optimizer.out index 805ea62439..ef4861238b 100755 --- a/src/test/regress/expected/partition_optimizer.out +++ b/src/test/regress/expected/partition_optimizer.out @@ -283,9 +283,98 @@ select has_table_privilege('part_role', 'bar_p'::regclass, 'select'); t (1 row) +-- the ONLY keyword will affect just the partition root for both grant/revoke +create role part_role2; +grant select on only foo_p to part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +grant select on foo_p to part_role2; +revoke select on only foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +revoke select on foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +create table foo_p2 (a int, b int) partition by range(a) (start(1) end(10) every(1)); +grant select on foo_p, only foo_p2 to part_role2; -- multiple tables in same statement +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p2'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p2_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +-- more cases +revoke all on foo_p from part_role2; +revoke all on foo_p2 from part_role2; +grant select on only public.foo_p to part_role2; -- with schema +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); + has_table_privilege +--------------------- + t +(1 row) + +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + has_table_privilege +--------------------- + f +(1 row) + +grant update(b) on only foo_p2 to part_role2; -- column level priviledge +select relname, has_column_privilege('part_role2', oid, 'b', 'update') from pg_class +where relname = 'foo_p2' or relname = 'foo_p2_1_prt_6'; + relname | has_column_privilege +----------------+---------------------- + foo_p2 | t + foo_p2_1_prt_6 | f +(2 rows) + drop table foo_p; +drop table foo_p2; drop table bar_p; drop role part_role; +drop role part_role2; -- validation create table foo_p (i int) partition by range(i) (start(1) end(10) every(1)); diff --git a/src/test/regress/sql/partition.sql b/src/test/regress/sql/partition.sql index cebff356e2..90dccbe00d 100644 --- a/src/test/regress/sql/partition.sql +++ b/src/test/regress/sql/partition.sql @@ -209,9 +209,41 @@ alter table foo_p exchange partition for(6) with table bar_p; select has_table_privilege('part_role', 'foo_p_1_prt_6'::regclass, 'select'); select has_table_privilege('part_role', 'bar_p'::regclass, 'select'); +-- the ONLY keyword will affect just the partition root for both grant/revoke +create role part_role2; +grant select on only foo_p to part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + +grant select on foo_p to part_role2; +revoke select on only foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); +revoke select on foo_p from part_role2; +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); + +create table foo_p2 (a int, b int) partition by range(a) (start(1) end(10) every(1)); +grant select on foo_p, only foo_p2 to part_role2; -- multiple tables in same statement +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p2'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p2_1_prt_6'::regclass, 'select'); + +-- more cases +revoke all on foo_p from part_role2; +revoke all on foo_p2 from part_role2; +grant select on only public.foo_p to part_role2; -- with schema +select has_table_privilege('part_role2', 'foo_p'::regclass, 'select'); +select has_table_privilege('part_role2', 'foo_p_1_prt_6'::regclass, 'select'); +grant update(b) on only foo_p2 to part_role2; -- column level priviledge +select relname, has_column_privilege('part_role2', oid, 'b', 'update') from pg_class +where relname = 'foo_p2' or relname = 'foo_p2_1_prt_6'; + drop table foo_p; +drop table foo_p2; drop table bar_p; drop role part_role; +drop role part_role2; -- validation create table foo_p (i int) partition by range(i) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
