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]

Reply via email to