From: Nathan Bossart Date: Wed, 7 Dec 2022 11:20:01 -0800 Subject: [PATCH v1 1/2] add grantable privileges for cluster, refresh matview, and reindex --- doc/src/sgml/ddl.sgml | 62 ++++++++++-- doc/src/sgml/func.sgml | 5 +- .../sgml/ref/alter_default_privileges.sgml | 4 +- doc/src/sgml/ref/cluster.sgml | 6 +- doc/src/sgml/ref/grant.sgml | 5 +- .../sgml/ref/refresh_materialized_view.sgml | 3 +- doc/src/sgml/ref/reindex.sgml | 6 +- doc/src/sgml/ref/revoke.sgml | 2 +- src/backend/catalog/aclchk.c | 12 +++ src/backend/commands/cluster.c | 19 ++-- src/backend/commands/indexcmds.c | 31 +++--- src/backend/commands/matview.c | 3 +- src/backend/commands/tablecmds.c | 17 ++-- src/backend/utils/adt/acl.c | 24 +++++ src/bin/pg_dump/dumputils.c | 3 + src/bin/pg_dump/t/002_pg_dump.pl | 2 +- src/bin/psql/tab-complete.c | 4 +- src/include/commands/tablecmds.h | 4 +- src/include/nodes/parsenodes.h | 5 +- src/include/utils/acl.h | 7 +- src/test/regress/expected/create_index.out | 4 +- src/test/regress/expected/dependency.out | 22 ++--- src/test/regress/expected/privileges.out | 96 ++++++++++++++----- src/test/regress/expected/rowsecurity.out | 34 +++---- src/test/regress/sql/dependency.sql | 2 +- src/test/regress/sql/privileges.sql | 61 ++++++++++++ 26 files changed, 333 insertions(+), 110 deletions(-) diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 38618de01c..eec49debfe 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -1692,8 +1692,9 @@ ALTER TABLE products RENAME TO items; TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE, USAGE, SET, - ALTER SYSTEM, VACUUM, and - ANALYZE. + ALTER SYSTEM, VACUUM, + ANALYZE, CLUSTER, + REFRESH, and REINDEX. The privileges applicable to a particular object vary depending on the object's type (table, function, etc.). More detail about the meanings of these privileges appears below. @@ -2001,6 +2002,34 @@ REVOKE ALL ON accounts FROM PUBLIC; + + + CLUSTER + + + Allows CLUSTER on a relation. + + + + + + REFRESH + + + Allows REFRESH MATERIALIZED VIEW on a materialized + view. + + + + + + REINDEX + + + Allows REINDEX on a relation and its indexes. + + + The privileges required by other commands are listed on the @@ -2160,6 +2189,21 @@ REVOKE ALL ON accounts FROM PUBLIC; z TABLE + + CLUSTER + S + TABLE + + + REFRESH + f + TABLE + + + REINDEX + n + TABLE + @@ -2250,7 +2294,7 @@ REVOKE ALL ON accounts FROM PUBLIC; TABLE (and table-like objects) - arwdDxtvz + arwdDxtvzSfn none \dp @@ -2308,12 +2352,12 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw; would show: => \dp mytable - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------+---------+-------+-------------------------+-----------------------+---------- - public | mytable | table | miriam=arwdDxtvz/miriam+| col1: +| - | | | =r/miriam +| miriam_rw=rw/miriam | - | | | admin=arw/miriam | | + Access privileges + Schema | Name | Type | Access privileges | Column privileges | Policies +--------+---------+-------+----------------------------+-----------------------+---------- + public | mytable | table | miriam=arwdDxtvzSfn/miriam+| col1: +| + | | | =r/miriam +| miriam_rw=rw/miriam | + | | | admin=arw/miriam | | (1 row) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index e57ffce971..06d54e47ba 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -22995,8 +22995,9 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); are SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, - TRIGGER, VACUUM and - ANALYZE. + TRIGGER, VACUUM, + ANALYZE, CLUSTER, + REFRESH, and REINDEX. diff --git a/doc/src/sgml/ref/alter_default_privileges.sgml b/doc/src/sgml/ref/alter_default_privileges.sgml index 0da295daff..9153359edc 100644 --- a/doc/src/sgml/ref/alter_default_privileges.sgml +++ b/doc/src/sgml/ref/alter_default_privileges.sgml @@ -28,7 +28,7 @@ ALTER DEFAULT PRIVILEGES where abbreviated_grant_or_revoke is one of: -GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE } +GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE | CLUSTER | REFRESH | REINDEX } [, ...] | ALL [ PRIVILEGES ] } ON TABLES TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] @@ -51,7 +51,7 @@ GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] } TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] REVOKE [ GRANT OPTION FOR ] - { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE } + { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE | CLUSTER | REFRESH | REINDEX } [, ...] | ALL [ PRIVILEGES ] } ON TABLES FROM { [ GROUP ] role_name | PUBLIC } [, ...] diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml index c37f4236f1..24eccab251 100644 --- a/doc/src/sgml/ref/cluster.sgml +++ b/doc/src/sgml/ref/cluster.sgml @@ -69,9 +69,9 @@ CLUSTER [VERBOSE] CLUSTER without any parameter reclusters all the previously-clustered tables in the current database that the calling user - owns, or all such tables if called by a superuser. This - form of CLUSTER cannot be executed inside a transaction - block. + owns or has the CLUSTER privilege for, or all such tables + if called by a superuser. This form of CLUSTER cannot be + executed inside a transaction block. diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index c3c585be7e..fff8cffb07 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE } +GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE | CLUSTER | REFRESH | REINDEX } [, ...] | ALL [ PRIVILEGES ] } ON { [ TABLE ] table_name [, ...] | ALL TABLES IN SCHEMA schema_name [, ...] } @@ -195,6 +195,9 @@ GRANT role_name [, ...] TO ALTER SYSTEM VACUUM ANALYZE + CLUSTER + REFRESH + REINDEX Specific types of privileges, as defined in . diff --git a/doc/src/sgml/ref/refresh_materialized_view.sgml b/doc/src/sgml/ref/refresh_materialized_view.sgml index 675d6090f3..90b3de5274 100644 --- a/doc/src/sgml/ref/refresh_materialized_view.sgml +++ b/doc/src/sgml/ref/refresh_materialized_view.sgml @@ -32,7 +32,8 @@ REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] name REFRESH MATERIALIZED VIEW completely replaces the contents of a materialized view. To execute this command you must be the - owner of the materialized view. The old contents are discarded. If + owner of the materialized view or have the REFRESH + privilege on the materialized view. The old contents are discarded. If WITH DATA is specified (or defaults) the backing query is executed to provide the new data, and the materialized view is left in a scannable state. If WITH NO DATA is specified no new diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml index fcbda88149..560e536b3c 100644 --- a/doc/src/sgml/ref/reindex.sgml +++ b/doc/src/sgml/ref/reindex.sgml @@ -293,14 +293,16 @@ REINDEX [ ( option [, ...] ) ] { DA Reindexing a single index or table requires being the owner of that - index or table. Reindexing a schema or database requires being the + index or table or having the REINDEX privilege on the + table. Reindexing a schema or database requires being the owner of that schema or database. Note specifically that it's thus possible for non-superusers to rebuild indexes of tables owned by other users. However, as a special exception, when REINDEX DATABASE, REINDEX SCHEMA or REINDEX SYSTEM is issued by a non-superuser, indexes on shared catalogs will be skipped unless the user owns the - catalog (which typically won't be the case). Of course, superusers + catalog (which typically won't be the case) or has the + REINDEX privilege on the catalog. Of course, superusers can always reindex anything. diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index e28d192fd3..7da9ece998 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -22,7 +22,7 @@ PostgreSQL documentation REVOKE [ GRANT OPTION FOR ] - { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE } + { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE | CLUSTER | REFRESH | REINDEX } [, ...] | ALL [ PRIVILEGES ] } ON { [ TABLE ] table_name [, ...] | ALL TABLES IN SCHEMA schema_name [, ...] } diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index bd967eaa78..b0b44b3fb2 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -3424,6 +3424,12 @@ string_to_privilege(const char *privname) return ACL_VACUUM; if (strcmp(privname, "analyze") == 0) return ACL_ANALYZE; + if (strcmp(privname, "cluster") == 0) + return ACL_CLUSTER; + if (strcmp(privname, "refresh") == 0) + return ACL_REFRESH; + if (strcmp(privname, "reindex") == 0) + return ACL_REINDEX; if (strcmp(privname, "rule") == 0) return 0; /* ignore old RULE privileges */ ereport(ERROR, @@ -3469,6 +3475,12 @@ privilege_to_string(AclMode privilege) return "VACUUM"; case ACL_ANALYZE: return "ANALYZE"; + case ACL_CLUSTER: + return "CLUSTER"; + case ACL_REFRESH: + return "REFRESH"; + case ACL_REINDEX: + return "REINDEX"; default: elog(ERROR, "unrecognized privilege: %d", (int) privilege); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 07e091bb87..c39a9cd221 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -138,6 +138,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) { /* This is the single-relation case. */ Oid tableOid; + AclMode acl = ACL_CLUSTER; /* * Find, lock, and check permissions on the table. We obtain @@ -147,7 +148,8 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) tableOid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, 0, - RangeVarCallbackOwnsTable, NULL); + RangeVarCallbackForTablePrivs, + &acl); rel = table_open(tableOid, NoLock); /* @@ -364,8 +366,9 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params) */ if (recheck) { - /* Check that the user still owns the relation */ - if (!object_ownercheck(RelationRelationId, tableOid, save_userid)) + /* Check that the user still has privileges for the relation */ + if (!object_ownercheck(RelationRelationId, tableOid, save_userid) && + pg_class_aclcheck(tableOid, save_userid, ACL_CLUSTER) != ACLCHECK_OK) { relation_close(OldHeap, AccessExclusiveLock); goto out; @@ -1612,7 +1615,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, /* - * Get a list of tables that the current user owns and + * Get a list of tables that the current user has privileges on and * have indisclustered set. Return the list in a List * of RelToCluster * (stored in the specified memory context), each one giving the tableOid * and the indexOid on which the table is already clustered. @@ -1629,8 +1632,8 @@ get_tables_to_cluster(MemoryContext cluster_context) List *rtcs = NIL; /* - * Get all indexes that have indisclustered set and are owned by - * appropriate user. + * Get all indexes that have indisclustered set and that the current user + * has the appropriate privileges for. */ indRelation = table_open(IndexRelationId, AccessShareLock); ScanKeyInit(&entry, @@ -1644,7 +1647,8 @@ get_tables_to_cluster(MemoryContext cluster_context) index = (Form_pg_index) GETSTRUCT(indexTuple); - if (!object_ownercheck(RelationRelationId, index->indrelid, GetUserId())) + if (!object_ownercheck(RelationRelationId, index->indrelid, GetUserId()) && + pg_class_aclcheck(index->indrelid, GetUserId(), ACL_CLUSTER) != ACLCHECK_OK) continue; /* Use a permanent memory context for the result list */ @@ -1694,6 +1698,7 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) /* Silently skip partitions which the user has no access to. */ if (!object_ownercheck(RelationRelationId, relid, GetUserId()) && + pg_class_aclcheck(relid, GetUserId(), ACL_CLUSTER) != ACLCHECK_OK && (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) || IsSharedRelation(relid))) continue; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index b5b860c3ab..e30e2ef844 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -2754,6 +2754,7 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, char relkind; struct ReindexIndexCallbackState *state = arg; LOCKMODE table_lockmode; + Oid table_oid; /* * Lock level here should match table lock in reindex_index() for @@ -2793,14 +2794,17 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, errmsg("\"%s\" is not an index", relation->relname))); /* Check permissions */ - if (!object_ownercheck(RelationRelationId, relId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, relation->relname); + table_oid = IndexGetRelation(relId, true); + if (!object_ownercheck(RelationRelationId, relId, GetUserId()) && + OidIsValid(table_oid) && + pg_class_aclcheck(table_oid, GetUserId(), ACL_REINDEX) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for \"%s\"", relation->relname))); /* Lock heap before index to avoid deadlock. */ if (relId != oldRelId) { - Oid table_oid = IndexGetRelation(relId, true); - /* * If the OID isn't valid, it means the index was concurrently * dropped, which is not a problem for us; just return normally. @@ -2822,6 +2826,7 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel) { Oid heapOid; bool result; + AclMode acl = ACL_REINDEX; /* * The lock level used here should match reindex_relation(). @@ -2835,7 +2840,7 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel) (params->options & REINDEXOPT_CONCURRENTLY) != 0 ? ShareUpdateExclusiveLock : ShareLock, 0, - RangeVarCallbackOwnsTable, NULL); + RangeVarCallbackForTablePrivs, &acl); if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE) ReindexPartitions(heapOid, params, isTopLevel); @@ -3001,15 +3006,17 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind, continue; /* - * The table can be reindexed if the user is superuser, the table - * owner, or the database/schema owner (but in the latter case, only - * if it's not a shared relation). object_ownercheck includes the - * superuser case, and depending on objectKind we already know that - * the user has permission to run REINDEX on this database or schema - * per the permission checks at the beginning of this routine. + * The table can be reindexed if the user has been granted REINDEX on + * the table or the user is a superuser, the table owner, or the + * database/schema owner (but in the latter case, only if it's not a + * shared relation). object_ownercheck includes the superuser case, + * and depending on objectKind we already know that the user has + * permission to run REINDEX on this database or schema per the + * permission checks at the beginning of this routine. */ if (classtuple->relisshared && - !object_ownercheck(RelationRelationId, relid, GetUserId())) + !object_ownercheck(RelationRelationId, relid, GetUserId()) && + pg_class_aclcheck(relid, GetUserId(), ACL_REINDEX) != ACLCHECK_OK) continue; /* diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 9ac0383459..c3d1d3d321 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -155,6 +155,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, int save_sec_context; int save_nestlevel; ObjectAddress address; + AclMode acl = ACL_REFRESH; /* Determine strength of lock needed. */ concurrent = stmt->concurrent; @@ -165,7 +166,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, */ matviewOid = RangeVarGetRelidExtended(stmt->relation, lockmode, 0, - RangeVarCallbackOwnsTable, NULL); + RangeVarCallbackForTablePrivs, &acl); matviewRel = table_open(matviewOid, NoLock); relowner = matviewRel->rd_rel->relowner; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ee88e87d76..bcc11b8017 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16930,13 +16930,13 @@ AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, * This is intended as a callback for RangeVarGetRelidExtended(). It allows * the relation to be locked only if (1) it's a plain or partitioned table, * materialized view, or TOAST table and (2) the current user is the owner (or - * the superuser). This meets the permission-checking needs of CLUSTER, - * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it - * can be used by all. + * the superuser) or has been granted any of the privileges specified in *acl. + * This meets the permission-checking needs of CLUSTER, REINDEX TABLE, and + * REFRESH MATERIALIZED VIEW; we expose it here so that it can be used by all. */ void -RangeVarCallbackOwnsTable(const RangeVar *relation, - Oid relId, Oid oldRelId, void *arg) +RangeVarCallbackForTablePrivs(const RangeVar *relation, + Oid relId, Oid oldRelId, void *acl) { char relkind; @@ -16959,8 +16959,11 @@ RangeVarCallbackOwnsTable(const RangeVar *relation, errmsg("\"%s\" is not a table or materialized view", relation->relname))); /* Check permissions */ - if (!object_ownercheck(RelationRelationId, relId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname); + if (!object_ownercheck(RelationRelationId, relId, GetUserId()) && + pg_class_aclcheck(relId, GetUserId(), *((AclMode *) acl)) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for \"%s\"", relation->relname))); } /* diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index ed1b6a41cf..022de60f93 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -327,6 +327,15 @@ aclparse(const char *s, AclItem *aip) case ACL_ANALYZE_CHR: read = ACL_ANALYZE; break; + case ACL_CLUSTER_CHR: + read = ACL_CLUSTER; + break; + case ACL_REFRESH_CHR: + read = ACL_REFRESH; + break; + case ACL_REINDEX_CHR: + read = ACL_REINDEX; + break; case 'R': /* ignore old RULE privileges */ read = 0; break; @@ -1603,6 +1612,9 @@ makeaclitem(PG_FUNCTION_ARGS) {"ALTER SYSTEM", ACL_ALTER_SYSTEM}, {"VACUUM", ACL_VACUUM}, {"ANALYZE", ACL_ANALYZE}, + {"CLUSTER", ACL_CLUSTER}, + {"REFRESH", ACL_REFRESH}, + {"REINDEX", ACL_REINDEX}, {"RULE", 0}, /* ignore old RULE privileges */ {NULL, 0} }; @@ -1715,6 +1727,12 @@ convert_aclright_to_string(int aclright) return "VACUUM"; case ACL_ANALYZE: return "ANALYZE"; + case ACL_CLUSTER: + return "CLUSTER"; + case ACL_REFRESH: + return "REFRESH"; + case ACL_REINDEX: + return "REINDEX"; default: elog(ERROR, "unrecognized aclright: %d", aclright); return NULL; @@ -2028,6 +2046,12 @@ convert_table_priv_string(text *priv_type_text) {"VACUUM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_VACUUM)}, {"ANALYZE", ACL_ANALYZE}, {"ANALYZE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ANALYZE)}, + {"CLUSTER", ACL_CLUSTER}, + {"CLUSTER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CLUSTER)}, + {"REFRESH", ACL_REFRESH}, + {"REFRESH WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFRESH)}, + {"REINDEX", ACL_REINDEX}, + {"REINDEX WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REINDEX)}, {"RULE", 0}, /* ignore old RULE privileges */ {"RULE WITH GRANT OPTION", 0}, {NULL, 0} diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 9311417f18..f6f94913f4 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -459,6 +459,9 @@ do { \ CONVERT_PRIV('D', "TRUNCATE"); CONVERT_PRIV('v', "VACUUM"); CONVERT_PRIV('z', "ANALYZE"); + CONVERT_PRIV('S', "CLUSTER"); + CONVERT_PRIV('f', "REFRESH"); + CONVERT_PRIV('n', "REINDEX"); } } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 6656222363..85e4654922 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -618,7 +618,7 @@ my %tests = ( \QREVOKE ALL ON TABLES FROM regress_dump_test_role;\E\n \QALTER DEFAULT PRIVILEGES \E \QFOR ROLE regress_dump_test_role \E - \QGRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,VACUUM,ANALYZE,UPDATE ON TABLES TO regress_dump_test_role;\E + \QGRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,VACUUM,ANALYZE,CLUSTER,REFRESH,REINDEX,UPDATE ON TABLES TO regress_dump_test_role;\E /xm, like => { %full_runs, section_post_data => 1, }, unlike => { no_privs => 1, }, diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 89e7317c23..a096e92b0d 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1147,7 +1147,7 @@ static const SchemaQuery Query_for_trigger_of_table = { #define Privilege_options_of_grant_and_revoke \ "SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", \ "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE", "SET", "ALTER SYSTEM", \ -"VACUUM", "ANALYZE", "ALL" +"VACUUM", "ANALYZE", "CLUSTER", "REFRESH", "REINDEX", "ALL" /* * These object types were introduced later than our support cutoff of @@ -3783,7 +3783,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "CREATE", "EXECUTE", "USAGE", "VACUUM", "ANALYZE", - "ALL"); + "CLUSTER", "REFRESH", "REINDEX", "ALL"); else if (TailMatches("GRANT")) COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles, Privilege_options_of_grant_and_revoke); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 03f14d6be1..88fafb8798 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -95,8 +95,8 @@ extern void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid); -extern void RangeVarCallbackOwnsTable(const RangeVar *relation, - Oid relId, Oid oldRelId, void *arg); +extern void RangeVarCallbackForTablePrivs(const RangeVar *relation, + Oid relId, Oid oldRelId, void *acl); extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6a6d3293e4..52fa7b2d04 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -97,7 +97,10 @@ typedef uint64 AclMode; /* a bitmask of privilege bits */ #define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */ #define ACL_VACUUM (1<<14) /* for relations */ #define ACL_ANALYZE (1<<15) /* for relations */ -#define N_ACL_RIGHTS 16 /* 1 plus the last 1< 44); \dp - Access privileges - Schema | Name | Type | Access privileges | Column privileges | Policies ---------------------+----------+-------+-----------------------------------------------+-------------------+-------------------------------------------- - regress_rls_schema | category | table | regress_rls_alice=arwdDxtvz/regress_rls_alice+| | - | | | =arwdDxtvz/regress_rls_alice | | - regress_rls_schema | document | table | regress_rls_alice=arwdDxtvz/regress_rls_alice+| | p1: + - | | | =arwdDxtvz/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv + - | | | | | FROM uaccount + - | | | | | WHERE (uaccount.pguser = CURRENT_USER)))+ - | | | | | p2r (RESTRICTIVE): + - | | | | | (u): ((cid <> 44) AND (cid < 50)) + - | | | | | to: regress_rls_dave + - | | | | | p1r (RESTRICTIVE): + - | | | | | (u): (cid <> 44) + - | | | | | to: regress_rls_dave - regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxtvz/regress_rls_alice+| | - | | | =r/regress_rls_alice | | + Access privileges + Schema | Name | Type | Access privileges | Column privileges | Policies +--------------------+----------+-------+--------------------------------------------------+-------------------+-------------------------------------------- + regress_rls_schema | category | table | regress_rls_alice=arwdDxtvzSfn/regress_rls_alice+| | + | | | =arwdDxtvzSfn/regress_rls_alice | | + regress_rls_schema | document | table | regress_rls_alice=arwdDxtvzSfn/regress_rls_alice+| | p1: + + | | | =arwdDxtvzSfn/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv + + | | | | | FROM uaccount + + | | | | | WHERE (uaccount.pguser = CURRENT_USER)))+ + | | | | | p2r (RESTRICTIVE): + + | | | | | (u): ((cid <> 44) AND (cid < 50)) + + | | | | | to: regress_rls_dave + + | | | | | p1r (RESTRICTIVE): + + | | | | | (u): (cid <> 44) + + | | | | | to: regress_rls_dave + regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxtvzSfn/regress_rls_alice+| | + | | | =r/regress_rls_alice | | (3 rows) \d document diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql index 99b905a938..7c5457db9e 100644 --- a/src/test/regress/sql/dependency.sql +++ b/src/test/regress/sql/dependency.sql @@ -21,7 +21,7 @@ REVOKE SELECT ON deptest FROM GROUP regress_dep_group; DROP GROUP regress_dep_group; -- can't drop the user if we revoke the privileges partially -REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, VACUUM, ANALYZE ON deptest FROM regress_dep_user; +REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, VACUUM, ANALYZE, CLUSTER, REFRESH, REINDEX ON deptest FROM regress_dep_user; DROP USER regress_dep_user; -- now we are OK to drop him diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 1bcaaba4eb..98eb7f05be 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -1916,3 +1916,64 @@ DROP ROLE regress_both; DROP ROLE regress_only_vacuum_all; DROP ROLE regress_only_analyze_all; DROP ROLE regress_both_all; + +-- CLUSTER +CREATE ROLE regress_no_cluster; +CREATE ROLE regress_cluster; + +CREATE TABLE cluster_test (a INT); +CREATE INDEX ON cluster_test (a); +GRANT CLUSTER ON cluster_test TO regress_cluster; + +SET ROLE regress_no_cluster; +CLUSTER cluster_test USING cluster_test_a_idx; +RESET ROLE; + +SET ROLE regress_cluster; +CLUSTER cluster_test USING cluster_test_a_idx; +RESET ROLE; + +DROP TABLE cluster_test; +DROP ROLE regress_no_cluster; +DROP ROLE regress_cluster; + +-- REFRESH MATERIALIZED VIEW +CREATE ROLE regress_no_refresh; +CREATE ROLE regress_refresh; + +CREATE MATERIALIZED VIEW refresh_test AS SELECT 1; +GRANT REFRESH ON refresh_test TO regress_refresh; + +SET ROLE regress_no_refresh; +REFRESH MATERIALIZED VIEW refresh_test; +RESET ROLE; + +SET ROLE regress_refresh; +REFRESH MATERIALIZED VIEW refresh_test; +RESET ROLE; + +DROP MATERIALIZED VIEW refresh_test; +DROP ROLE regress_no_refresh; +DROP ROLE regress_refresh; + +-- REINDEX +CREATE ROLE regress_no_reindex; +CREATE ROLE regress_reindex; + +CREATE TABLE reindex_test (a INT); +CREATE INDEX ON reindex_test (a); +GRANT REINDEX ON reindex_test TO regress_reindex; + +SET ROLE regress_no_reindex; +REINDEX TABLE reindex_test; +REINDEX INDEX reindex_test_a_idx; +RESET ROLE; + +SET ROLE regress_reindex; +REINDEX TABLE reindex_test; +REINDEX INDEX reindex_test_a_idx; +RESET ROLE; + +DROP TABLE reindex_test; +DROP ROLE regress_no_reindex; +DROP ROLE regress_reindex; -- 2.25.1 --zYM0uCDKw75PZbzx Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0002-add-predefined-roles-for-cluster-refresh-matview-.patch"