Hi All, This is a "proof-of-concept" patch for a new model around role attributes and fine grained permissions meant to alleviate the current over dependence on superuser.
This is not yet complete and only serves as a proof-of-concept at this point, but I wanted to share it in the hopes of receiving comments, suggestions and general feedback. The general gist of this patch is as follows: * New system catalog "pg_permission" that relates role id's to permissions. * New syntax. - GRANT <permission> TO <role> - REVOKE <permission> FROM <role> where, <permission> is one of an enumerated value, such as "CREATE ROLE" or "CREATE DATABASE". * Refactor CREATEDB and NOCREATEDB role attribute to "CREATE DATABASE" permission set by GRANT or REVOKE. * Refactor CREATEROLE and NOCREATEROLE role attribute to "CREATE ROLE" permission set by GRANT or REVOKE. Again, this is meant to serve as a proof-of-concept. It is not comprehensive and only demonstrates how this might work with a few already defined permissions. I have attached the current patch based on master. Any comments or feedback would be greatly appreciated. Thanks, Adam -- Adam Brightwell - adam.brightw...@crunchydatasolutions.com Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile new file mode 100644 index a974bd5..8e4ad25 *** a/src/backend/catalog/Makefile --- b/src/backend/catalog/Makefile *************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr *** 39,45 **** pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ ! pg_foreign_table.h \ pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \ toasting.h indexing.h \ ) --- 39,45 ---- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ ! pg_foreign_table.h pg_permission.h \ pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \ toasting.h indexing.h \ ) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c new file mode 100644 index d9745ca..9f4b5df *** a/src/backend/catalog/aclchk.c --- b/src/backend/catalog/aclchk.c *************** *** 42,53 **** --- 42,55 ---- #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" + #include "catalog/pg_permission.h" #include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" #include "commands/dbcommands.h" + #include "commands/permission.h" #include "commands/proclang.h" #include "commands/tablespace.h" #include "foreign/foreign.h" *************** bool *** 5065,5082 **** has_createrole_privilege(Oid roleid) { bool result = false; - HeapTuple utup; /* Superusers bypass all permission checking. */ if (superuser_arg(roleid)) return true; ! utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); ! if (HeapTupleIsValid(utup)) ! { ! result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole; ! ReleaseSysCache(utup); ! } return result; } --- 5067,5079 ---- has_createrole_privilege(Oid roleid) { bool result = false; /* Superusers bypass all permission checking. */ if (superuser_arg(roleid)) return true; ! result = HasPermission(roleid, PERM_CREATE_ROLE); ! return result; } diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile new file mode 100644 index 22f116b..b98212a *** a/src/backend/commands/Makefile --- b/src/backend/commands/Makefile *************** OBJS = aggregatecmds.o alter.o analyze.o *** 17,23 **** dbcommands.o define.o discard.o dropcmds.o \ event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \ ! portalcmds.o prepare.o proclang.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \ variable.o view.o --- 17,23 ---- dbcommands.o define.o discard.o dropcmds.o \ event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \ ! permission.o portalcmds.o prepare.o proclang.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \ variable.o view.o diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c new file mode 100644 index f480be8..ecba8a5 *** a/src/backend/commands/dbcommands.c --- b/src/backend/commands/dbcommands.c *************** *** 36,45 **** --- 36,47 ---- #include "catalog/pg_authid.h" #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" + #include "catalog/pg_permission.h" #include "catalog/pg_tablespace.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/defrem.h" + #include "commands/permission.h" #include "commands/seclabel.h" #include "commands/tablespace.h" #include "mb/pg_wchar.h" *************** static bool *** 1789,1806 **** have_createdb_privilege(void) { bool result = false; - HeapTuple utup; /* Superusers can always do everything */ if (superuser()) return true; ! utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId())); ! if (HeapTupleIsValid(utup)) ! { ! result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb; ! ReleaseSysCache(utup); ! } return result; } --- 1791,1803 ---- have_createdb_privilege(void) { bool result = false; /* Superusers can always do everything */ if (superuser()) return true; ! result = HasPermission(GetUserId(), PERM_CREATE_DATABASE); ! return result; } diff --git a/src/backend/commands/permission.c b/src/backend/commands/permission.c new file mode 100644 index ...977b7d7 *** a/src/backend/commands/permission.c --- b/src/backend/commands/permission.c *************** *** 0 **** --- 1,254 ---- + /*------------------------------------------------------------------------- + * + * permission.c + * Commands for manipulating permissions. + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California* + * + * src/backend/commands/permission.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/genam.h" + #include "access/heapam.h" + #include "access/htup_details.h" + #include "access/sysattr.h" + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" + #include "commands/permission.h" + #include "nodes/pg_list.h" + #include "parser/parse_node.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/rel.h" + #include "utils/syscache.h" + + static void AddPermissionToRole(const char *role_name, Oid role_id, + List *permissions); + static void RemovePermissionFromRole(const char *role_name, Oid role_id, + List *permissions); + + void + RemovePermissionById(Oid permission_id) + { + Relation catalog; + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple tuple; + + /* Find permission. */ + catalog = heap_open(PermissionRelationId, RowExclusiveLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(permission_id)); + + sscan = systable_beginscan(catalog, PermissionRelationId, true, + NULL, 1,&skey); + + tuple = systable_getnext(sscan); + + /* If permission exists, then delete it. */ + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for permission %u", permission_id); + + simple_heap_delete(catalog, &tuple->t_self); + + /* Invalidate Cache */ + // CacheInvalidateRelcache(catalog); + + /* Clean up */ + heap_close(catalog, RowExclusiveLock); + } + + /* + * Grant/Revoke permissions to/from roles. + */ + void + GrantPermission(GrantPermissionStmt *stmt) + { + Oid role_id; + char *role_name; + ListCell *item; + + foreach(item, stmt->roles) + { + role_name = strVal(lfirst(item)); + role_id = get_role_oid(role_name, false); + + if (stmt->is_grant) + AddPermissionToRole(role_name, role_id, stmt->permissions); + else + RemovePermissionFromRole(role_name, role_id, stmt->permissions); + } + } + + /* + * HasPermission + * check if a user/role has a permission, true if role permission, otherwise + * false. + * + * role_id - the role to check. + * permission - the permission to check. + */ + bool + HasPermission(Oid role_id, Permission permission) + { + bool result = false; + HeapTuple tuple; + + tuple = SearchSysCache2(PERMROLEIDPERMID, ObjectIdGetDatum(role_id), + Int32GetDatum(permission)); + + if (HeapTupleIsValid(tuple)) + { + result = true; + ReleaseSysCache(tuple); + } + + return result; + } + + /* + * AddPermissionToRole - Add given permissions to the specified role + * + * role_name: name of role to add permissions to, only used for error messages. + * role_id: OID of the role to add permissions to. + * permissions: list of permissions to add to the role. + * + */ + static void + AddPermissionToRole(const char *role_name, Oid role_id, List *permissions) + { + Relation pg_permission_rel; + Oid permission_id; + ScanKeyData skeys[2]; + SysScanDesc sscan; + HeapTuple tuple; + Datum new_record[Natts_pg_permission]; + bool new_record_nulls[Natts_pg_permission]; + ObjectAddress role_dependency; + ObjectAddress permission_dependee; + ListCell *item; + + pg_permission_rel = heap_open(PermissionRelationId, RowExclusiveLock); + + foreach(item, permissions) + { + int permission = lfirst_int(item); + + /* Determine if permission is already set. */ + ScanKeyInit(&skeys[0], + Anum_pg_permission_permroleid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(role_id)); + + ScanKeyInit(&skeys[1], + Anum_pg_permission_permpermission, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(permission)); + + sscan = systable_beginscan(pg_permission_rel, PermissionRoleIdPermIndexId, + true, NULL, 2, skeys); + + tuple = systable_getnext(sscan); + + if (!HeapTupleIsValid(tuple)) + { + memset(new_record, 0, sizeof(new_record)); + memset(new_record_nulls, 0, sizeof(new_record_nulls)); + + new_record[Anum_pg_permission_permroleid - 1] + = ObjectIdGetDatum(role_id); + new_record[Anum_pg_permission_permpermission - 1] + = Int32GetDatum(permission); + + tuple = heap_form_tuple(RelationGetDescr(pg_permission_rel), + new_record, new_record_nulls); + + permission_id = simple_heap_insert(pg_permission_rel, tuple); + + CatalogUpdateIndexes(pg_permission_rel, tuple); + + /* Record dependencies on role */ + role_dependency.classId = AuthIdRelationId; + role_dependency.objectId = role_id; + role_dependency.objectSubId = 0; + + permission_dependee.classId = PermissionRelationId; + permission_dependee.objectId = permission_id; + permission_dependee.objectSubId = 0; + + recordDependencyOn(&permission_dependee, &role_dependency, DEPENDENCY_AUTO); + + heap_freetuple(tuple); + } + else + elog(NOTICE, "Permission already set for %s.", role_name); + + systable_endscan(sscan); + } + + /* Clean up. */ + heap_close(pg_permission_rel, RowExclusiveLock); + } + + /* + * RemovePermissionFromRole - Remove given permissions from the specified role + * + * role_name: name of role to add permissions to, only used for error messages. + * role_id: OID of the role to add permissions to. + * permissions: list of permissions to add to the role. + * + */ + static void + RemovePermissionFromRole(const char *role_name, Oid role_id, List *permissions) + { + Relation pg_permission_rel; + ScanKeyData skeys[2]; + SysScanDesc sscan; + HeapTuple tuple; + ListCell *item; + + pg_permission_rel = heap_open(PermissionRelationId, RowExclusiveLock); + + foreach(item, permissions) + { + int permission = lfirst_int(item); + + /* Determine if permission is already set. */ + ScanKeyInit(&skeys[0], + Anum_pg_permission_permroleid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(role_id)); + + ScanKeyInit(&skeys[1], + Anum_pg_permission_permpermission, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(permission)); + + sscan = systable_beginscan(pg_permission_rel, PermissionRoleIdPermIndexId, + true, NULL, 2, skeys); + + tuple = systable_getnext(sscan); + + /* If the permission exists, remove it. */ + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "Permission is not set for %s.", role_name); + + simple_heap_delete(pg_permission_rel, &tuple->t_self); + + CatalogUpdateIndexes(pg_permission_rel, tuple); + + systable_endscan(sscan); + } + + /* Clean up. */ + heap_close(pg_permission_rel, RowExclusiveLock); + } + diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c new file mode 100644 index 3088578..ed7c17b *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copyGrantRoleStmt(const GrantRoleStmt * *** 2701,2706 **** --- 2701,2718 ---- return newnode; } + static GrantPermissionStmt * + _copyGrantPermissionStmt(const GrantPermissionStmt *from) + { + GrantPermissionStmt *newnode = makeNode(GrantPermissionStmt); + + COPY_NODE_FIELD(roles); + COPY_NODE_FIELD(permissions); + COPY_SCALAR_FIELD(is_grant); + + return newnode; + } + static AlterDefaultPrivilegesStmt * _copyAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *from) { *************** copyObject(const void *from) *** 4291,4296 **** --- 4303,4311 ---- case T_GrantRoleStmt: retval = _copyGrantRoleStmt(from); break; + case T_GrantPermissionStmt: + retval = _copyGrantPermissionStmt(from); + break; case T_AlterDefaultPrivilegesStmt: retval = _copyAlterDefaultPrivilegesStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c new file mode 100644 index 1b07db6..e261bbf *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** _equalGrantRoleStmt(const GrantRoleStmt *** 1045,1050 **** --- 1045,1060 ---- } static bool + _equalGrantPermissionStmt(const GrantPermissionStmt *a, const GrantPermissionStmt *b) + { + COMPARE_NODE_FIELD(roles); + COMPARE_NODE_FIELD(permissions); + COMPARE_SCALAR_FIELD(is_grant); + + return true; + } + + static bool _equalAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *a, const AlterDefaultPrivilegesStmt *b) { COMPARE_NODE_FIELD(options); *************** equal(const void *a, const void *b) *** 2755,2760 **** --- 2765,2773 ---- case T_GrantRoleStmt: retval = _equalGrantRoleStmt(a, b); break; + case T_GrantPermissionStmt: + retval = _equalGrantPermissionStmt(a, b); + break; case T_AlterDefaultPrivilegesStmt: retval = _equalAlterDefaultPrivilegesStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y new file mode 100644 index a113809..d3b6d99 *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 51,56 **** --- 51,57 ---- #include "catalog/index.h" #include "catalog/namespace.h" + #include "catalog/pg_permission.h" #include "catalog/pg_trigger.h" #include "commands/defrem.h" #include "commands/trigger.h" *************** static Node *makeRecursiveViewSelect(cha *** 322,327 **** --- 323,330 ---- %type <str> iso_level opt_encoding %type <node> grantee %type <list> grantee_list + %type <ival> permission + %type <list> permission_list %type <accesspriv> privilege %type <list> privileges privilege_list %type <privtarget> privilege_target *************** GrantRoleStmt: *** 6072,6077 **** --- 6075,6088 ---- n->grantor = $6; $$ = (Node*)n; } + | GRANT permission_list TO role_list + { + GrantPermissionStmt *n = makeNode(GrantPermissionStmt); + n->is_grant = true; + n->permissions = $2; + n->roles = $4; + $$ = (Node*)n; + } ; RevokeRoleStmt: *************** RevokeRoleStmt: *** 6095,6100 **** --- 6106,6119 ---- n->behavior = $9; $$ = (Node*)n; } + | REVOKE permission_list FROM role_list + { + GrantPermissionStmt *n = makeNode(GrantPermissionStmt); + n->is_grant = false; + n->permissions = $2; + n->roles = $4; + $$ = (Node*)n; + } ; opt_grant_admin_option: WITH ADMIN OPTION { $$ = TRUE; } *************** opt_granted_by: GRANTED BY RoleId { *** 6105,6110 **** --- 6124,6138 ---- | /*EMPTY*/ { $$ = NULL; } ; + permission_list: permission { $$ = list_make1_int($1); } + | permission_list ',' permission { $$ = lappend_int($1, $3); } + ; + + permission: CREATE DATABASE { $$ = PERM_CREATE_DATABASE; } + | CREATE ROLE { $$ = PERM_CREATE_ROLE; } + | /*EMPTY*/ { $$ = PERM_INVALID; } + ; + /***************************************************************************** * * ALTER DEFAULT PRIVILEGES statement diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c new file mode 100644 index 07e0b98..58cc8ee *** a/src/backend/tcop/utility.c --- b/src/backend/tcop/utility.c *************** *** 39,44 **** --- 39,45 ---- #include "commands/extension.h" #include "commands/matview.h" #include "commands/lockcmds.h" + #include "commands/permission.h" #include "commands/portalcmds.h" #include "commands/prepare.h" #include "commands/proclang.h" *************** check_xact_readonly(Node *parsetree) *** 183,188 **** --- 184,190 ---- case T_DropRoleStmt: case T_GrantStmt: case T_GrantRoleStmt: + case T_GrantPermissionStmt: case T_AlterDefaultPrivilegesStmt: case T_TruncateStmt: case T_DropOwnedStmt: *************** standard_ProcessUtility(Node *parsetree, *** 561,566 **** --- 563,572 ---- GrantRole((GrantRoleStmt *) parsetree); break; + case T_GrantPermissionStmt: + GrantPermission((GrantPermissionStmt *) parsetree); + break; + case T_CreatedbStmt: /* no event triggers for global objects */ PreventTransactionChain(isTopLevel, "CREATE DATABASE"); *************** CreateCommandTag(Node *parsetree) *** 2002,2007 **** --- 2008,2021 ---- } break; + case T_GrantPermissionStmt: + { + GrantPermissionStmt *stmt = (GrantPermissionStmt *) parsetree; + + tag = (stmt->is_grant) ? "GRANT PERMISSION" : "REVOKE PERMISSION"; + } + break; + case T_GrantRoleStmt: { GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree; diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c new file mode 100644 index 94d951c..2d5c7d4 *** a/src/backend/utils/cache/syscache.c --- b/src/backend/utils/cache/syscache.c *************** *** 47,52 **** --- 47,53 ---- #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" + #include "catalog/pg_permission.h" #include "catalog/pg_proc.h" #include "catalog/pg_range.h" #include "catalog/pg_rewrite.h" *************** static const struct cachedesc cacheinfo[ *** 565,570 **** --- 566,582 ---- }, 8 }, + {PermissionRelationId, /* PERMROLEIDPERMID */ + PermissionRoleIdPermIndexId, + 2, + { + Anum_pg_permission_permroleid, + Anum_pg_permission_permpermission, + 0, + 0, + }, + 8 + }, {ProcedureRelationId, /* PROCNAMEARGSNSP */ ProcedureNameArgsNspIndexId, 3, diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h new file mode 100644 index 59576fd..60e110d *** a/src/include/catalog/indexing.h --- b/src/include/catalog/indexing.h *************** DECLARE_UNIQUE_INDEX(pg_extension_name_i *** 299,304 **** --- 299,310 ---- DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops)); #define RangeTypidIndexId 3542 + DECLARE_UNIQUE_INDEX(pg_permission_oid_index, 6001, on pg_permission using btree(oid oid_ops)); + #define PermissionOidIndexId 6001 + + DECLARE_UNIQUE_INDEX(pg_permission_roleid_index, 6002, on pg_permission using btree(permroleid oid_ops, permpermission int4_ops)); + #define PermissionRoleIdPermIndexId 6002 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES diff --git a/src/include/catalog/pg_permission.h b/src/include/catalog/pg_permission.h new file mode 100644 index ...eee670b *** a/src/include/catalog/pg_permission.h --- b/src/include/catalog/pg_permission.h *************** *** 0 **** --- 1,51 ---- + /* + * pg_permission.h + * definition of the system catalog for role permissions (pg_permission) + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + #ifndef PG_PERMISSION_H + #define PG_PERMISSION_H + + #include "catalog/genbki.h" + + /* ---------------- + * pg_permission definition. cpp turns this into + * typedef struct FormData_pg_permission + * ---------------- + */ + #define PermissionRelationId 6000 + + CATALOG(pg_permission,6000) + { + Oid permroleid; + int32 permpermission; + } FormData_pg_permission; + + /* ---------------- + * Form_pg_permission corresponds to a pointer to a tuple with + * the format of pg_permission relation. + * ---------------- + */ + typedef FormData_pg_permission *Form_pg_permission; + + /* + * ---------------- + * compiler constants for pg_permission + * ---------------- + */ + #define Natts_pg_permission 2 + #define Anum_pg_permission_permroleid 1 + #define Anum_pg_permission_permpermission 2 + + typedef enum Permission + { + PERM_INVALID = -1, /* Invalid Permission */ + PERM_CREATE_DATABASE = 0, /* CREATE DATABASE */ + PERM_CREATE_ROLE /* CREATE ROLE */ + } Permission; + + #endif /* PG_PERMISSION_H */ + diff --git a/src/include/commands/permission.h b/src/include/commands/permission.h new file mode 100644 index ...0595e8c *** a/src/include/commands/permission.h --- b/src/include/commands/permission.h *************** *** 0 **** --- 1,27 ---- + /*------------------------------------------------------------------------- + * + * permission.h + * prototypes for permission.c. + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/permission.h + * + *------------------------------------------------------------------------- + */ + + #ifndef PERMISSION_H + #define PERMISSION_H + + #include "catalog/pg_permission.h" + #include "nodes/parsenodes.h" + + extern void RemovePermissionById(Oid permission_id); + + extern void GrantPermission(GrantPermissionStmt *stmt); + + extern bool HasPermission(Oid role_id, Permission permission); + + #endif /* PERMISSION_H */ + diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h new file mode 100644 index 067c768..6b56981 *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** typedef enum NodeTag *** 278,283 **** --- 278,284 ---- T_AlterDomainStmt, T_SetOperationStmt, T_GrantStmt, + T_GrantPermissionStmt, T_GrantRoleStmt, T_AlterDefaultPrivilegesStmt, T_ClosePortalStmt, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h new file mode 100644 index 8364bef..2b20388 *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** typedef struct GrantRoleStmt *** 1476,1481 **** --- 1476,1493 ---- } GrantRoleStmt; /* ---------------------- + * Grant/Revoke Permission Statement + * ---------------------- + */ + typedef struct GrantPermissionStmt + { + NodeTag type; + List *permissions; /* list of permissions to be granted/revoked */ + List *roles; /* list of roles to granted/revoked permission */ + bool is_grant; /* true = GRANT, false = REVOKE */ + } GrantPermissionStmt; + + /* ---------------------- * Alter Default Privileges Statement * ---------------------- */ diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h new file mode 100644 index f97229f..23e4d8a *** a/src/include/utils/syscache.h --- b/src/include/utils/syscache.h *************** enum SysCacheIdentifier *** 72,77 **** --- 72,78 ---- OPEROID, OPFAMILYAMNAMENSP, OPFAMILYOID, + PERMROLEIDPERMID, PROCNAMEARGSNSP, PROCOID, RANGETYPE,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers