From c45b94e88a98f251c8e1c3c7774c8caf13ee1a03 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 25 Feb 2016 18:40:11 -0800 Subject: [PATCH] Add ReplicateGrantStmt(). This is the basis for coordinating GRANT/REVOKE across nodes. --- .../distributed/executor/multi_utility.c | 154 ++++++++++++++++++ src/include/distributed/multi_utility.h | 1 + 2 files changed, 155 insertions(+) diff --git a/src/backend/distributed/executor/multi_utility.c b/src/backend/distributed/executor/multi_utility.c index 731bf773d..da65e3ef5 100644 --- a/src/backend/distributed/executor/multi_utility.c +++ b/src/backend/distributed/executor/multi_utility.c @@ -16,6 +16,7 @@ #include "catalog/namespace.h" #include "commands/defrem.h" #include "commands/tablecmds.h" +#include "distributed/citus_ruleutils.h" #include "distributed/master_protocol.h" #include "distributed/metadata_cache.h" #include "distributed/multi_copy.h" @@ -1291,3 +1292,156 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) return attnums; /* *INDENT-ON* */ } + + +/* + * ReplicateGrantStmt replicates GRANT/REVOKE command to worker nodes if the + * the statement affects distributed tables. + * + * NB: So far column level privileges are not supported. + */ +void +ReplicateGrantStmt(Node *parsetree) +{ + GrantStmt *grantStmt = (GrantStmt *) parsetree; + StringInfoData privsString; + StringInfoData granteesString; + StringInfoData targetString; + StringInfoData ddlString; + ListCell *granteeCell = NULL; + ListCell *objectCell = NULL; + ListCell *privilegeCell = NULL; + bool isFirst = true; + + initStringInfo(&privsString); + initStringInfo(&granteesString); + initStringInfo(&targetString); + initStringInfo(&ddlString); + + /* + * So far only table level grants are supported. Most other types of + * grants aren't interesting anyway. + */ + if (grantStmt->targtype != ACL_TARGET_OBJECT || + grantStmt->objtype != ACL_OBJECT_RELATION) + { + return; + } + + /* deparse the privileges */ + if (grantStmt->privileges == NIL) + { + appendStringInfo(&privsString, "ALL"); + } + else + { + isFirst = true; + foreach(privilegeCell, grantStmt->privileges) + { + AccessPriv *priv = lfirst(privilegeCell); + + if (!isFirst) + { + appendStringInfoString(&privsString, ", "); + } + isFirst = false; + + Assert(priv->cols == NIL); + Assert(priv->priv_name != NULL); + + appendStringInfo(&privsString, "%s", priv->priv_name); + } + } + + /* deparse the privileges */ + isFirst = true; + foreach(granteeCell, grantStmt->grantees) + { +#if (PG_VERSION_NUM >= 90500) + RoleSpec *spec = lfirst(granteeCell); +#else + PrivGrantee *spec = lfirst(granteeCell); +#endif + + if (!isFirst) + { + appendStringInfoString(&granteesString, ", "); + } + isFirst = false; + +#if (PG_VERSION_NUM >= 90500) + if (spec->roletype == ROLESPEC_CSTRING) + { + appendStringInfoString(&granteesString, quote_identifier(spec->rolename)); + } + else if (spec->roletype == ROLESPEC_CURRENT_USER) + { + appendStringInfoString(&granteesString, "CURRENT_USER"); + } + else if (spec->roletype == ROLESPEC_SESSION_USER) + { + appendStringInfoString(&granteesString, "SESSION_USER"); + } + else if (spec->roletype == ROLESPEC_PUBLIC) + { + appendStringInfoString(&granteesString, "PUBLIC"); + } +#else + if (spec->rolname) + { + appendStringInfoString(&granteesString, quote_identifier(spec->rolname)); + } + else + { + appendStringInfoString(&granteesString, "PUBLIC"); + } +#endif + } + + /* + * Deparse the target objects, and issue the deparsed statements to + * workers, if applicable. That's so we easily can replicate statements + * only to distributed relations. + */ + isFirst = true; + foreach(objectCell, grantStmt->objects) + { + RangeVar *relvar = (RangeVar *) lfirst(objectCell); + Oid relOid = RangeVarGetRelid(relvar, NoLock, false); + const char *grantOption = ""; + + if (!IsDistributedTable(relOid)) + { + continue; + } + + resetStringInfo(&targetString); + appendStringInfo(&targetString, "%s", generate_relation_name(relOid, NIL)); + + if (grantStmt->is_grant) + { + if (grantStmt->grant_option) + { + grantOption = " WITH GRANT OPTION"; + } + + appendStringInfo(&ddlString, "GRANT %s ON %s TO %s%s", + privsString.data, targetString.data, granteesString.data, + grantOption); + } + else + { + if (grantStmt->grant_option) + { + grantOption = "GRANT OPTION FOR "; + } + + appendStringInfo(&ddlString, "REVOKE %s%s ON %s FROM %s", + grantOption, privsString.data, targetString.data, + granteesString.data); + } + + ExecuteDistributedDDLCommand(relOid, ddlString.data); + resetStringInfo(&ddlString); + } +} diff --git a/src/include/distributed/multi_utility.h b/src/include/distributed/multi_utility.h index 45f84b858..63b5a2e52 100644 --- a/src/include/distributed/multi_utility.h +++ b/src/include/distributed/multi_utility.h @@ -16,6 +16,7 @@ extern void multi_ProcessUtility(Node *parsetree, const char *queryString, ProcessUtilityContext context, ParamListInfo params, DestReceiver *dest, char *completionTag); +extern void ReplicateGrantStmt(Node *parsetree); #endif /* MULTI_UTILITY_H */