From 88dccf993057f7d79716c15c190591614fd3af46 Mon Sep 17 00:00:00 2001 From: Ajin Cherian Date: Tue, 20 Sep 2022 10:37:21 -0400 Subject: [PATCH v22 5/5] Support DDL of GRANT Command. Granting the following global objects are not supported: 1) Foreign data wrapper 2) parameter - ex: grant all on parameter wal_buffers TO test; 3) tablespace - ex: grant create on tablespace tbs1 to test; 4) database - ex: grant all on database postgres to test; 5) role - ex: grant test to test1; -- no event trigger for global objects --- src/backend/catalog/aclchk.c | 4 + src/backend/commands/ddl_deparse.c | 250 +++++++++++++++++++++++++++++++++ src/backend/commands/event_trigger.c | 3 +- src/backend/commands/publicationcmds.c | 3 +- src/include/utils/aclchk_internal.h | 1 + 5 files changed, 259 insertions(+), 2 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index aa5a2ed..7dc190d 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -385,7 +385,11 @@ ExecuteGrantStmt(GrantStmt *stmt) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("grantor must be current user"))); + + istmt.grantor_uid = grantor; } + else + istmt.grantor_uid = InvalidOid; /* * Turn the regular GrantStmt into the InternalGrant form. diff --git a/src/backend/commands/ddl_deparse.c b/src/backend/commands/ddl_deparse.c index f0d3909..2727dec 100755 --- a/src/backend/commands/ddl_deparse.c +++ b/src/backend/commands/ddl_deparse.c @@ -32,15 +32,19 @@ #include "catalog/namespace.h" #include "catalog/pg_am.h" #include "catalog/pg_attribute.h" +#include "catalog/pg_authid.h" #include "catalog/pg_cast.h" #include "catalog/pg_class.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_depend.h" +#include "catalog/pg_foreign_data_wrapper.h" +#include "catalog/pg_foreign_server.h" #include "catalog/pg_extension.h" #include "catalog/pg_inherits.h" #include "catalog/pg_language.h" +#include "catalog/pg_largeobject.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" @@ -766,6 +770,39 @@ new_objtree_for_rolespec(RoleSpec *spec) } /* + * Helper routine for %{}R objects, with role specified by OID. (ACL_ID_PUBLIC + * means to use "public"). + */ +static ObjTree * +new_objtree_for_role_id(Oid roleoid) +{ + ObjTree *role; + + role = new_objtree(""); + + if (roleoid != ACL_ID_PUBLIC) + { + HeapTuple roltup; + char *rolename; + + roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleoid)); + if (!HeapTupleIsValid(roltup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("role with OID %u does not exist", roleoid))); + + rolename = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname); + append_string_object(role, "%{rolename}I", pstrdup(rolename)); + + ReleaseSysCache(roltup); + } + else + append_string_object(role, "%{rolename}I", "public"); + + return role; +} + +/* * A helper routine to setup %{}T elements. */ static ObjTree * @@ -1711,6 +1748,216 @@ deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree, } /* + * deparse an GRANT command. + */ +static ObjTree * +deparse_GrantStmt(CollectedCommand *cmd) +{ + InternalGrant *istmt; + ObjTree *grantStmt; + char *fmt; + char *objtype; + List *list; + ListCell *cell; + Oid classId; + ObjTree *tmp; + + istmt = cmd->d.grant.istmt; + + /* + * If there are no objects from "ALL ... IN SCHEMA" to be granted, then + * we need not do anything. + */ + if (istmt->objects == NIL) + return NULL; + + switch (istmt->objtype) + { + case OBJECT_COLUMN: + case OBJECT_TABLE: + objtype = "TABLE"; + classId = RelationRelationId; + break; + case OBJECT_SEQUENCE: + objtype = "SEQUENCE"; + classId = RelationRelationId; + break; + case OBJECT_DOMAIN: + objtype = "DOMAIN"; + classId = TypeRelationId; + break; + case OBJECT_FDW: + objtype = "FOREIGN DATA WRAPPER"; + classId = ForeignDataWrapperRelationId; + break; + case OBJECT_FOREIGN_SERVER: + objtype = "FOREIGN SERVER"; + classId = ForeignServerRelationId; + break; + case OBJECT_FUNCTION: + objtype = "FUNCTION"; + classId = ProcedureRelationId; + break; + case OBJECT_PROCEDURE: + objtype = "PROCEDURE"; + classId = ProcedureRelationId; + break; + case OBJECT_ROUTINE: + objtype = "ROUTINE"; + classId = ProcedureRelationId; + break; + case OBJECT_LANGUAGE: + objtype = "LANGUAGE"; + classId = LanguageRelationId; + break; + case OBJECT_LARGEOBJECT: + objtype = "LARGE OBJECT"; + classId = LargeObjectRelationId; + break; + case OBJECT_SCHEMA: + objtype = "SCHEMA"; + classId = NamespaceRelationId; + break; + case OBJECT_TYPE: + objtype = "TYPE"; + classId = TypeRelationId; + break; + case OBJECT_DATABASE: + case OBJECT_TABLESPACE: + objtype = ""; + classId = InvalidOid; + elog(ERROR, "global objects not supported"); + break; + default: + elog(ERROR, "invalid OBJECT value %d", istmt->objtype); + } + + fmt = psprintf("GRANT"); + + grantStmt = new_objtree_VA(fmt, 0); + + /* build list of privileges to grant */ + if (istmt->all_privs) + { + tmp = new_objtree_VA("ALL PRIVILEGES", 0); + list = list_make1(new_object_object(tmp)); + } + else + { + list = NIL; + + if (istmt->privileges & ACL_INSERT) + list = lappend(list, new_string_object("INSERT")); + if (istmt->privileges & ACL_SELECT) + list = lappend(list, new_string_object("SELECT")); + if (istmt->privileges & ACL_UPDATE) + list = lappend(list, new_string_object("UPDATE")); + if (istmt->privileges & ACL_DELETE) + list = lappend(list, new_string_object("DELETE")); + if (istmt->privileges & ACL_TRUNCATE) + list = lappend(list, new_string_object("TRUNCATE")); + if (istmt->privileges & ACL_REFERENCES) + list = lappend(list, new_string_object("REFERENCES")); + if (istmt->privileges & ACL_TRIGGER) + list = lappend(list, new_string_object("TRIGGER")); + if (istmt->privileges & ACL_EXECUTE) + list = lappend(list, new_string_object("EXECUTE")); + if (istmt->privileges & ACL_USAGE) + list = lappend(list, new_string_object("USAGE")); + if (istmt->privileges & ACL_CREATE) + list = lappend(list, new_string_object("CREATE")); + if (istmt->privileges & ACL_CREATE_TEMP) + list = lappend(list, new_string_object("TEMPORARY")); + if (istmt->privileges & ACL_CONNECT) + list = lappend(list, new_string_object("CONNECT")); + + if (istmt->col_privs != NIL) + { + ListCell *ocell; + + foreach(ocell, istmt->col_privs) + { + AccessPriv *priv = lfirst(ocell); + List *cols = NIL; + + tmp = new_objtree(""); + foreach(cell, priv->cols) + { + String *colname = lfirst_node(String, cell); + + cols = lappend(cols, + new_string_object(strVal(colname))); + } + append_array_object(tmp, "(%{cols:, }s)", cols); + + if (priv->priv_name == NULL) + append_string_object(grantStmt, "%{priv}s", "ALL PRIVILEGES"); + else + append_string_object(grantStmt, "%{priv}s", priv->priv_name); + + list = lappend(list, new_object_object(tmp)); + } + } + } + append_array_object(grantStmt, "%{privileges:, }s ON", list); + + append_string_object(grantStmt, "%{objtype}s", objtype); + + /* target objects. We use object identities here */ + list = NIL; + foreach(cell, istmt->objects) + { + Oid objid = lfirst_oid(cell); + ObjectAddress addr; + + addr.classId = classId; + addr.objectId = objid; + addr.objectSubId = 0; + + tmp = new_objtree_VA("%{identity}s", 1, + "identity", ObjTypeString, + getObjectIdentity(&addr, false)); + + list = lappend(list, new_object_object(tmp)); + } + append_array_object(grantStmt, "%{privtarget:, }s TO", list); + + /* list of grantees */ + list = NIL; + foreach(cell, istmt->grantees) + { + Oid grantee = lfirst_oid(cell); + + tmp = new_objtree_for_role_id(grantee); + list = lappend(list, new_object_object(tmp)); + } + + append_array_object(grantStmt, "%{grantees:, }s", list); + + /* the wording of the grant option is variable ... */ + append_string_object(grantStmt, "%{grant_option}s", + istmt->grant_option ? "WITH GRANT OPTION" : ""); + + if (istmt->grantor_uid) + { + HeapTuple roltup; + char *rolename; + + roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(istmt->grantor_uid)); + if (!HeapTupleIsValid(roltup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("role with OID %u does not exist", istmt->grantor_uid))); + + rolename = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname); + append_string_object(grantStmt, "GRANTED BY %{rolename}s", rolename); + ReleaseSysCache(roltup); + } + + return grantStmt; +} + +/* * Deparse an AlterOperatorStmt (ALTER OPERATOR ... SET ...). */ static ObjTree * @@ -6847,6 +7094,9 @@ deparse_utility_command(CollectedCommand *cmd, bool verbose_mode) case SCT_AlterTable: tree = deparse_AlterTableStmt(cmd); break; + case SCT_Grant: + tree = deparse_GrantStmt(cmd); + break; case SCT_CreateTableAs: tree = deparse_CreateTableAsStmt(cmd); break; diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 37f27dc..861584c 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -2474,7 +2474,8 @@ publication_deparse_ddl_command_end(PG_FUNCTION_ARGS) { RenameStmt *renameStmt = (RenameStmt *)cmd->parsetree; - if (renameStmt->relationType != OBJECT_TYPE && + if (renameStmt && + renameStmt->relationType != OBJECT_TYPE && renameStmt->relationType != OBJECT_TABLE) continue; } diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 215e526..9c79496 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -999,7 +999,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt) CMDTAG_DROP_SCHEMA, CMDTAG_CREATE_INDEX, CMDTAG_DROP_INDEX, - CMDTAG_ALTER_INDEX + CMDTAG_ALTER_INDEX, + CMDTAG_GRANT }; CommandTag init_commands[] = { diff --git a/src/include/utils/aclchk_internal.h b/src/include/utils/aclchk_internal.h index 361284d..4e020ef 100644 --- a/src/include/utils/aclchk_internal.h +++ b/src/include/utils/aclchk_internal.h @@ -39,6 +39,7 @@ typedef struct List *grantees; bool grant_option; DropBehavior behavior; + Oid grantor_uid; } InternalGrant; -- 1.8.3.1