diff --git a/src/backend/distributed/relay/relay_event_utility.c b/src/backend/distributed/relay/relay_event_utility.c index 8f9fe26b3..9bbea8498 100644 --- a/src/backend/distributed/relay/relay_event_utility.c +++ b/src/backend/distributed/relay/relay_event_utility.c @@ -243,6 +243,26 @@ RelayEventExtendNames(Node *parseTree, uint64 shardId) break; } + + case T_GrantStmt: + { + GrantStmt *grantStmt = (GrantStmt *) parseTree; + + if (grantStmt->targtype == ACL_TARGET_OBJECT && + grantStmt->objtype == ACL_OBJECT_RELATION) + { + ListCell *lc; + + foreach(lc, grantStmt->objects) + { + RangeVar *relation = (RangeVar *) lfirst(lc); + char **relationName = &(relation->relname); + AppendShardIdToName(relationName, shardId); + } + } + break; + } + case T_IndexStmt: { IndexStmt *indexStmt = (IndexStmt *) parseTree; diff --git a/src/backend/distributed/utils/citus_ruleutils.c b/src/backend/distributed/utils/citus_ruleutils.c index 4c1999ac2..706f9599a 100644 --- a/src/backend/distributed/utils/citus_ruleutils.c +++ b/src/backend/distributed/utils/citus_ruleutils.c @@ -17,6 +17,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_aggregate.h" +#include "catalog/pg_authid.h" #include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_opclass.h" @@ -54,6 +55,7 @@ static Oid get_extension_schema(Oid ext_oid); static void AppendOptionListToString(StringInfo stringData, List *options); +static const char * convert_aclright_to_string(int aclright); /* * pg_get_extensiondef_string finds the foreign data wrapper that corresponds to @@ -574,3 +576,184 @@ pg_get_indexclusterdef_string(Oid indexRelationId) return (buffer.data); } + + +/* + * pg_get_table_grants returns a list of sql statements which recreate the + * permissions for a specific table. + * + * This function is modeled after aclexplode(), don't change too heavily. + */ +List * +pg_get_table_grants(Oid relationId) +{ + /* *INDENT-OFF* */ + StringInfoData buffer; + Relation relation = NULL; + char *relationName = NULL; + List *defs = NIL; + HeapTuple classTuple = NULL; + Datum aclDatum = 0; + Acl *acl = NULL; + bool isNull = false; + int offtype = 0; + + relation = relation_open(relationId, AccessShareLock); + relationName = generate_relation_name(relationId, NIL); + + initStringInfo(&buffer); + + /* lookup all table level grants */ + classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId)); + if (!HeapTupleIsValid(classTuple)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation with OID %u does not exist", + relationId))); + } + + aclDatum = SysCacheGetAttr(RELOID, classTuple, Anum_pg_class_relacl, + &isNull); + + ReleaseSysCache(classTuple); + + if (!isNull) + { + int i = 0; + AclItem *aidat = NULL; + + + /* + * First revoke all default permissions, so we can start adding the + * exact permissions from the master. Note that we only do so if there + * are any actual grants; an empty grant set signals default + * permissions. + * + * Note: This doesn't work correctly if default permissions have been + * changed with ALTER DEFAULT PRIVILEGES - but that's hard to fix + * properly currently. + */ + appendStringInfo(&buffer, "REVOKE ALL ON %s FROM PUBLIC", + relationName); + defs = lappend(defs, pstrdup(buffer.data)); + resetStringInfo(&buffer); + + /* iterate through the acl datastructure, emit GRANTs */ + + acl = DatumGetAclP(aclDatum); + aidat = ACL_DAT(acl); + + offtype = -1; + i = 0; + while (i < ACL_NUM(acl)) + { + AclItem *aidata = NULL; + AclMode priv_bit = 0; + + offtype++; + + if (offtype == N_ACL_RIGHTS) + { + offtype = 0; + i++; + if (i >= ACL_NUM(acl)) /* done */ + { + break; + } + } + + aidata = &aidat[i]; + priv_bit = 1 << offtype; + + if (ACLITEM_GET_PRIVS(*aidata) & priv_bit) + { + const char *roleName = NULL; + const char *withGrant = ""; + + if (aidata->ai_grantee != 0) + { + HeapTuple htup; + + htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aidata->ai_grantee)); + if (HeapTupleIsValid(htup)) + { + Form_pg_authid authForm = ((Form_pg_authid) GETSTRUCT(htup)); + + roleName = quote_identifier(NameStr(authForm->rolname)); + + ReleaseSysCache(htup); + } + else + { + elog(ERROR, "cache lookup failed for role %u", aidata->ai_grantee); + } + } + else + { + roleName = "PUBLIC"; + } + + if ((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0) + { + withGrant = " WITH GRANT OPTION"; + } + + appendStringInfo(&buffer, "GRANT %s ON %s TO %s%s", + convert_aclright_to_string(priv_bit), + relationName, + roleName, + withGrant); + + defs = lappend(defs, pstrdup(buffer.data)); + + resetStringInfo(&buffer); + } + } + } + + resetStringInfo(&buffer); + + relation_close(relation, NoLock); + return defs; + /* *INDENT-ON* */ +} + + +/* copy of postgresql's function, which is static as well */ +static const char * +convert_aclright_to_string(int aclright) +{ + /* *INDENT-OFF* */ + switch (aclright) + { + case ACL_INSERT: + return "INSERT"; + case ACL_SELECT: + return "SELECT"; + case ACL_UPDATE: + return "UPDATE"; + case ACL_DELETE: + return "DELETE"; + case ACL_TRUNCATE: + return "TRUNCATE"; + case ACL_REFERENCES: + return "REFERENCES"; + case ACL_TRIGGER: + return "TRIGGER"; + case ACL_EXECUTE: + return "EXECUTE"; + case ACL_USAGE: + return "USAGE"; + case ACL_CREATE: + return "CREATE"; + case ACL_CREATE_TEMP: + return "TEMPORARY"; + case ACL_CONNECT: + return "CONNECT"; + default: + elog(ERROR, "unrecognized aclright: %d", aclright); + return NULL; + } + /* *INDENT-ON* */ +} diff --git a/src/include/distributed/citus_ruleutils.h b/src/include/distributed/citus_ruleutils.h index f56fc228a..dcee35280 100644 --- a/src/include/distributed/citus_ruleutils.h +++ b/src/include/distributed/citus_ruleutils.h @@ -21,6 +21,7 @@ extern char * pg_get_serverdef_string(Oid tableRelationId); extern char * pg_get_tableschemadef_string(Oid tableRelationId); extern char * pg_get_tablecolumnoptionsdef_string(Oid tableRelationId); extern char * pg_get_indexclusterdef_string(Oid indexRelationId); +extern List * pg_get_table_grants(Oid relationId); /* Function declarations for version dependent PostgreSQL ruleutils functions */ extern void pg_get_query_def(Query *query, StringInfo buffer);