diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index c7de5d874..402b63adc 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -44,7 +44,8 @@ static int ObjectAddressComparator(const void *a, const void *b); static void EnsureDependenciesExistOnAllNodes(const ObjectAddress *target); static void EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target, RequiredObjectSet requiredObjectSet); -static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency); +static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency, bool + fetchGrantStatements); static bool ShouldPropagateObject(const ObjectAddress *address); static char * DropTableIfExistsCommand(Oid relationId); @@ -164,7 +165,7 @@ EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target, ObjectAddress *object = NULL; foreach_ptr(object, objectsToBeCreated) { - List *dependencyCommands = GetDependencyCreateDDLCommands(object); + List *dependencyCommands = GetDependencyCreateDDLCommands(object, true); ddlCommands = list_concat(ddlCommands, dependencyCommands); /* create a new list with objects that actually created commands */ @@ -432,7 +433,7 @@ GetDistributableDependenciesForObject(const ObjectAddress *target) * in nodes, but we utilize logic it follows to choose the objects that could * be distributed */ - List *dependencyCommands = GetDependencyCreateDDLCommands(dependency); + List *dependencyCommands = GetDependencyCreateDDLCommands(dependency, true); /* create a new list with dependencies that actually created commands */ if (list_length(dependencyCommands) > 0) @@ -465,7 +466,7 @@ DropTableIfExistsCommand(Oid relationId) * commands to execute on a worker to create the object. */ static List * -GetDependencyCreateDDLCommands(const ObjectAddress *dependency) +GetDependencyCreateDDLCommands(const ObjectAddress *dependency, bool fetchGrantStatements) { switch (getObjectClass(dependency)) { @@ -605,7 +606,8 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) case OCLASS_ROLE: { - return GenerateCreateOrAlterRoleCommand(dependency->objectId); + return GenerateCreateOrAlterRoleCommand(dependency->objectId, + fetchGrantStatements); } case OCLASS_SCHEMA: @@ -680,15 +682,16 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) List * GetAllDependencyCreateDDLCommands(const List *dependencies) { - List *commands = NIL; + List *ddlCommands = NIL; ObjectAddress *dependency = NULL; foreach_ptr(dependency, dependencies) { - commands = list_concat(commands, GetDependencyCreateDDLCommands(dependency)); + ddlCommands = list_concat(ddlCommands, GetDependencyCreateDDLCommands(dependency, + false)); } - return commands; + return ddlCommands; } diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 7f5f697f2..30f3e7cda 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -56,6 +56,7 @@ #include "distributed/version_compat.h" #include "distributed/worker_transaction.h" + static const char * ExtractEncryptedPassword(Oid roleOid); static const char * CreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt); static const char * CreateAlterRoleSetIfExistsCommand(AlterRoleSetStmt *stmt); @@ -67,6 +68,8 @@ static DefElem * makeDefElemBool(char *name, bool value); static List * GenerateRoleOptionsList(HeapTuple tuple); static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options); static List * GenerateGrantRoleStmtsOfRole(Oid roleid); +static GrantRoleStmt * GetGrantRoleStmtFromAuthMemberRecord(Form_pg_auth_members + membership); static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename); static void EnsureSequentialModeForRoleDDL(void); @@ -512,7 +515,7 @@ GenerateRoleOptionsList(HeapTuple tuple) * the pg_authid table. */ List * -GenerateCreateOrAlterRoleCommand(Oid roleOid) +GenerateCreateOrAlterRoleCommand(Oid roleOid, bool fetchGrantStmts) { HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); @@ -562,11 +565,10 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) if (EnableCreateRolePropagation) { - List *grantRoleStmts = GenerateGrantRoleStmtsOfRole(roleOid); - Node *stmt = NULL; - foreach_ptr(stmt, grantRoleStmts) + if (fetchGrantStmts) { - completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); + completeRoleList = list_concat(completeRoleList, GenerateGrantRoleStmtsOfRole( + roleOid)); } /* @@ -577,7 +579,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) * SecLabel stmts to be run in the new node. */ List *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename); - stmt = NULL; + Node *stmt = NULL; foreach_ptr(stmt, secLabelOnRoleStmts) { completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); @@ -872,7 +874,9 @@ GenerateGrantRoleStmtsOfRole(Oid roleid) { Relation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock); HeapTuple tuple = NULL; - List *stmts = NIL; + + List *adminStmts = NIL; + List *otherStmts = NIL; ScanKeyData skey[1]; @@ -885,73 +889,148 @@ GenerateGrantRoleStmtsOfRole(Oid roleid) { Form_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple); - ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); - ObjectAddressSet(*roleAddress, AuthIdRelationId, membership->grantor); - if (!IsAnyObjectDistributed(list_make1(roleAddress))) + GrantRoleStmt *grantRoleStmt = GetGrantRoleStmtFromAuthMemberRecord(membership); + if (grantRoleStmt == NULL || IsReservedName(GetUserNameFromId(membership->roleid, + true))) { - /* we only need to propagate the grant if the grantor is distributed */ continue; } - GrantRoleStmt *grantRoleStmt = makeNode(GrantRoleStmt); - grantRoleStmt->is_grant = true; - - RoleSpec *grantedRole = makeNode(RoleSpec); - grantedRole->roletype = ROLESPEC_CSTRING; - grantedRole->location = -1; - grantedRole->rolename = GetUserNameFromId(membership->roleid, true); - grantRoleStmt->granted_roles = list_make1(grantedRole); - - RoleSpec *granteeRole = makeNode(RoleSpec); - granteeRole->roletype = ROLESPEC_CSTRING; - granteeRole->location = -1; - granteeRole->rolename = GetUserNameFromId(membership->member, true); - grantRoleStmt->grantee_roles = list_make1(granteeRole); - - RoleSpec *grantorRole = makeNode(RoleSpec); - grantorRole->roletype = ROLESPEC_CSTRING; - grantorRole->location = -1; - grantorRole->rolename = GetUserNameFromId(membership->grantor, false); - grantRoleStmt->grantor = grantorRole; - -#if PG_VERSION_NUM >= PG_VERSION_16 - - /* inherit option is always included */ - DefElem *inherit_opt; - if (membership->inherit_option) + if (membership->admin_option) { - inherit_opt = makeDefElem("inherit", (Node *) makeBoolean(true), -1); + adminStmts = lappend(adminStmts, grantRoleStmt); } else { - inherit_opt = makeDefElem("inherit", (Node *) makeBoolean(false), -1); + otherStmts = lappend(otherStmts, grantRoleStmt); } - grantRoleStmt->opt = list_make1(inherit_opt); - - /* admin option is false by default, only include true case */ - if (membership->admin_option) - { - DefElem *admin_opt = makeDefElem("admin", (Node *) makeBoolean(true), -1); - grantRoleStmt->opt = lappend(grantRoleStmt->opt, admin_opt); - } - - /* set option is true by default, only include false case */ - if (!membership->set_option) - { - DefElem *set_opt = makeDefElem("set", (Node *) makeBoolean(false), -1); - grantRoleStmt->opt = lappend(grantRoleStmt->opt, set_opt); - } -#else - grantRoleStmt->admin_opt = membership->admin_option; -#endif - - stmts = lappend(stmts, grantRoleStmt); } systable_endscan(scan); table_close(pgAuthMembers, AccessShareLock); - return stmts; + List *allStmts = list_concat(adminStmts, otherStmts); + List *commands = NIL; + Node *stmt = NULL; + foreach_ptr(stmt, allStmts) + { + commands = lappend(commands, DeparseTreeNode(stmt)); + } + + return commands; +} + + +List * +GenerateGrantRoleStmts() +{ + Relation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock); + HeapTuple tuple = NULL; + List *adminStmts = NIL; + List *otherStmts = NIL; + + SysScanDesc scan = systable_beginscan(pgAuthMembers, InvalidOid, false, + NULL, 0, NULL); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Form_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple); + + + GrantRoleStmt *grantRoleStmt = GetGrantRoleStmtFromAuthMemberRecord(membership); + if (grantRoleStmt == NULL || IsReservedName(GetUserNameFromId(membership->roleid, + true))) + { + continue; + } + + if (membership->admin_option) + { + adminStmts = lappend(adminStmts, grantRoleStmt); + } + else + { + otherStmts = lappend(otherStmts, grantRoleStmt); + } + } + + systable_endscan(scan); + table_close(pgAuthMembers, AccessShareLock); + + List *allStmts = list_concat(adminStmts, otherStmts); + + /*iterate through the list of adminStmts and otherStmts */ + /*and using DeparseTreeNode to convert the GrantRoleStmt to a string */ + /*and then add the string to the list of commands */ + List *commands = NIL; + Node *stmt = NULL; + foreach_ptr(stmt, allStmts) + { + commands = lappend(commands, DeparseTreeNode(stmt)); + } + + return commands; +} + + +static GrantRoleStmt * +GetGrantRoleStmtFromAuthMemberRecord(Form_pg_auth_members membership) +{ + ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*roleAddress, AuthIdRelationId, membership->grantor); + + GrantRoleStmt *grantRoleStmt = makeNode(GrantRoleStmt); + grantRoleStmt->is_grant = true; + + RoleSpec *grantedRole = makeNode(RoleSpec); + grantedRole->roletype = ROLESPEC_CSTRING; + grantedRole->location = -1; + grantedRole->rolename = GetUserNameFromId(membership->roleid, true); + grantRoleStmt->granted_roles = list_make1(grantedRole); + + RoleSpec *granteeRole = makeNode(RoleSpec); + granteeRole->roletype = ROLESPEC_CSTRING; + granteeRole->location = -1; + granteeRole->rolename = GetUserNameFromId(membership->member, true); + grantRoleStmt->grantee_roles = list_make1(granteeRole); + + RoleSpec *grantorRole = makeNode(RoleSpec); + grantorRole->roletype = ROLESPEC_CSTRING; + grantorRole->location = -1; + grantorRole->rolename = GetUserNameFromId(membership->grantor, false); + grantRoleStmt->grantor = grantorRole; + +#if PG_VERSION_NUM >= PG_VERSION_16 + + /* inherit option is always included */ + DefElem *inherit_opt; + if (membership->inherit_option) + { + inherit_opt = makeDefElem("inherit", (Node *) makeBoolean(true), -1); + } + else + { + inherit_opt = makeDefElem("inherit", (Node *) makeBoolean(false), -1); + } + grantRoleStmt->opt = list_make1(inherit_opt); + + /* admin option is false by default, only include true case */ + if (membership->admin_option) + { + DefElem *admin_opt = makeDefElem("admin", (Node *) makeBoolean(true), -1); + grantRoleStmt->opt = lappend(grantRoleStmt->opt, admin_opt); + } + + /* set option is true by default, only include false case */ + if (!membership->set_option) + { + DefElem *set_opt = makeDefElem("set", (Node *) makeBoolean(false), -1); + grantRoleStmt->opt = lappend(grantRoleStmt->opt, set_opt); + } +#else + grantRoleStmt->admin_opt = membership->admin_option; +#endif + return grantRoleStmt; } @@ -1202,7 +1281,7 @@ UnmarkRolesDistributed(List *roles) List * FilterDistributedRoles(List *roles) { - List *distributedRoles = NIL; + List *validRoles = NIL; Node *roleNode = NULL; foreach_ptr(roleNode, roles) { @@ -1218,12 +1297,46 @@ FilterDistributedRoles(List *roles) } ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); ObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid); - if (IsAnyObjectDistributed(list_make1(roleAddress))) + bool isSystemRole = IsReservedName(role->rolename); + if (IsAnyObjectDistributed(list_make1(roleAddress)) || isSystemRole) { - distributedRoles = lappend(distributedRoles, role); + validRoles = lappend(validRoles, role); } } - return distributedRoles; + return validRoles; +} + + +/* + * FilterDistributedRoles filters the list of AccessPrivs and returns the ones + * that are distributed. + */ +List * +FilterDistributedGrantedRoles(List *roles) +{ + List *validRoles = NIL; + Node *roleNode = NULL; + foreach_ptr(roleNode, roles) + { + AccessPriv *role = castNode(AccessPriv, roleNode); + Oid roleOid = get_role_oid(role->priv_name, false); + if (roleOid == InvalidOid) + { + /* + * Non-existing roles are ignored silently here. Postgres will + * handle to give an error or not for these roles. + */ + continue; + } + ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid); + bool isSystemRole = IsReservedName(role->priv_name); + if (IsAnyObjectDistributed(list_make1(roleAddress)) || isSystemRole) + { + validRoles = lappend(validRoles, role); + } + } + return validRoles; } @@ -1243,19 +1356,8 @@ PreprocessGrantRoleStmt(Node *node, const char *queryString, EnsurePropagationToCoordinator(); GrantRoleStmt *stmt = castNode(GrantRoleStmt, node); - List *allGranteeRoles = stmt->grantee_roles; - RoleSpec *grantor = stmt->grantor; - List *distributedGranteeRoles = FilterDistributedRoles(allGranteeRoles); - if (list_length(distributedGranteeRoles) <= 0) - { - return NIL; - } - - stmt->grantee_roles = distributedGranteeRoles; char *sql = DeparseTreeNode((Node *) stmt); - stmt->grantee_roles = allGranteeRoles; - stmt->grantor = grantor; List *commands = list_make3(DISABLE_DDL_PROPAGATION, sql, @@ -1265,6 +1367,54 @@ PreprocessGrantRoleStmt(Node *node, const char *queryString, } +DistributedRolesInGrantRoleStmt * +ExtractDistributedRolesInGrantRoleStmt(GrantRoleStmt *stmt) +{ + DistributedRolesInGrantRoleStmt *distributedRolesInGrantRoleStmt = palloc0( + sizeof(DistributedRolesInGrantRoleStmt)); + distributedRolesInGrantRoleStmt->distributedGrantees = FilterDistributedRoles( + stmt->grantee_roles); + distributedRolesInGrantRoleStmt->distributedGrantedRoles = + FilterDistributedGrantedRoles(stmt->granted_roles); + distributedRolesInGrantRoleStmt->grantor = stmt->grantor; + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = true; + + bool grantorMissingOk = false; + bool isGrantorDefined = distributedRolesInGrantRoleStmt->grantor != NULL && + get_rolespec_oid(distributedRolesInGrantRoleStmt->grantor, + grantorMissingOk) != InvalidOid; + bool isGrantorDistributed = IsAnyObjectDistributed(RoleSpecToObjectAddress( + distributedRolesInGrantRoleStmt + ->grantor, grantorMissingOk)); + bool skipDueToGrantor = isGrantorDefined && !isGrantorDistributed; + if (list_length(distributedRolesInGrantRoleStmt->distributedGrantees) <= 0) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since no grantees are distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + if (list_length(distributedRolesInGrantRoleStmt->distributedGrantedRoles) <= 0) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since no granted roles are distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + if (skipDueToGrantor) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since grantor is not distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + return distributedRolesInGrantRoleStmt; +} + + /* * PostprocessGrantRoleStmt actually creates the plan we need to execute for grant * role statement. diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 1b86b06f1..9a1bcc457 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -5167,6 +5167,11 @@ SendDependencyCreationCommands(MetadataSyncContext *context) List *ddlCommands = GetAllDependencyCreateDDLCommands(list_make1(dependency)); SendOrCollectCommandListToActivatedNodes(context, ddlCommands); } + + List *grantRoleCommands = GenerateGrantRoleStmts(); + SendOrCollectCommandListToActivatedNodes(context, grantRoleCommands); + + MemoryContextSwitchTo(oldContext); if (!MetadataSyncCollectsCommands(context)) diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 084308a8f..4274262ea 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -174,6 +174,14 @@ typedef enum TenantOperation TENANT_SET_SCHEMA, } TenantOperation; +typedef struct DistributedRolesInGrantRoleStmt +{ + List *distributedGrantees; + List *distributedGrantedRoles; + RoleSpec *grantor; + bool isGrantRoleStmtValid; +} DistributedRolesInGrantRoleStmt; + #define TOTAL_TENANT_OPERATION 5 extern const char *TenantOperationNames[TOTAL_TENANT_OPERATION]; @@ -519,7 +527,7 @@ extern List * PreprocessDropRoleStmt(Node *stmt, const char *queryString, extern List * PreprocessGrantRoleStmt(Node *stmt, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * PostprocessGrantRoleStmt(Node *stmt, const char *queryString); -extern List * GenerateCreateOrAlterRoleCommand(Oid roleOid); +extern List * GenerateCreateOrAlterRoleCommand(Oid roleOid, bool fetchGrantStatements); extern List * CreateRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess); @@ -527,7 +535,11 @@ extern List * RenameRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess); extern void UnmarkRolesDistributed(List *roles); +extern List * FilterDistributedGrantedRoles(List *roles); extern List * FilterDistributedRoles(List *roles); +extern DistributedRolesInGrantRoleStmt * ExtractDistributedRolesInGrantRoleStmt( + GrantRoleStmt *stmt); +extern List * GenerateGrantRoleStmts(void); /* schema.c - forward declarations */ extern List * PostprocessCreateSchemaStmt(Node *node, const char *queryString); diff --git a/src/test/regress/expected/create_role_propagation.out b/src/test/regress/expected/create_role_propagation.out index 4d594ddab..f497b2cf7 100644 --- a/src/test/regress/expected/create_role_propagation.out +++ b/src/test/regress/expected/create_role_propagation.out @@ -196,7 +196,6 @@ SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::t (1 row) \c - - - :master_port -create role test_admin_role; -- test grants with distributed and non-distributed roles SELECT master_remove_node('localhost', :worker_2_port); master_remove_node @@ -204,6 +203,7 @@ SELECT master_remove_node('localhost', :worker_2_port); (1 row) +create role test_admin_role; CREATE ROLE dist_role_1 SUPERUSER; CREATE ROLE dist_role_2; CREATE ROLE dist_role_3; @@ -225,6 +225,7 @@ SET citus.enable_create_role_propagation TO ON; grant dist_role_3,dist_role_1 to test_admin_role with admin option; SET ROLE dist_role_1; GRANT non_dist_role_1 TO non_dist_role_2; +ERROR: connection to the remote node dist_role_1@localhost:xxxxx failed with the following error: FATAL: role "dist_role_1" is not permitted to log in SET citus.enable_create_role_propagation TO OFF; grant dist_role_1 to non_dist_role_1 with admin option; SET ROLE non_dist_role_1; @@ -232,14 +233,35 @@ GRANT dist_role_1 TO dist_role_2 granted by non_dist_role_1; RESET ROLE; SET citus.enable_create_role_propagation TO ON; GRANT dist_role_3 TO non_dist_role_3 granted by test_admin_role; +ERROR: role "non_dist_role_3" does not exist +CONTEXT: while executing command on localhost:xxxxx GRANT non_dist_role_4 TO dist_role_4; GRANT dist_role_3 TO dist_role_4 granted by test_admin_role; +SELECT 1 FROM master_add_node('localhost', :worker_2_port); +WARNING: role "non_dist_role_1" does not exist +CONTEXT: while executing command on localhost:xxxxx +ERROR: failure on connection marked as essential: localhost:xxxxx +\c - - - :worker_2_port +create role non_dist_role_1; +\c - - - :master_port +--will be successful since non_dist_role_1 is created on worker_2 SELECT 1 FROM master_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + dist_role_3 + dist_role_4 + non_dist_role_1 + non_dist_role_4 +(6 rows) + SELECT result FROM run_command_on_all_nodes( $$ SELECT json_agg(q.* ORDER BY member) FROM ( @@ -248,14 +270,13 @@ SELECT result FROM run_command_on_all_nodes( ) q; $$ ); - result + result --------------------------------------------------------------------- - [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + - {"member":"non_dist_role_3","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] - [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] - [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] (3 rows) @@ -269,10 +290,9 @@ SELECT result FROM run_command_on_all_nodes( ) q; $$ ); - result + result --------------------------------------------------------------------- - [{"member":"non_dist_role_3","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + - {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] (3 rows) @@ -283,11 +303,9 @@ SELECT roleid::regrole::text AS role, member::regrole::text, (grantor::regrole:: dist_role_1 | dist_role_2 | t | f dist_role_1 | non_dist_role_1 | t | t dist_role_1 | test_admin_role | t | t - dist_role_3 | non_dist_role_3 | t | f dist_role_3 | test_admin_role | t | t - non_dist_role_1 | non_dist_role_2 | t | f non_dist_role_4 | dist_role_4 | t | f -(7 rows) +(5 rows) SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_%' ORDER BY 1; objid @@ -296,10 +314,14 @@ SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid':: dist_role_2 dist_role_3 dist_role_4 + non_dist_role_1 non_dist_role_4 -(5 rows) +(6 rows) REVOKE dist_role_3 from non_dist_role_3 granted by test_admin_role cascade; +WARNING: role "non_dist_role_3" has not been granted membership in role "dist_role_3" by role "test_admin_role" +ERROR: role "non_dist_role_3" does not exist +CONTEXT: while executing command on localhost:xxxxx SELECT result FROM run_command_on_all_nodes( $$ SELECT json_agg(q.* ORDER BY member) FROM ( @@ -332,15 +354,18 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_%' ORDER BY 1; dist_role_2 dist_role_3 dist_role_4 + non_dist_role_1 non_dist_role_4 -(5 rows) +(6 rows) \c - - - :worker_2_port SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - non_dist_role_4 | dist_role_4 | postgres | f -(1 row) + dist_role_1 | dist_role_2 | non_dist_role_1 | f + dist_role_1 | non_dist_role_1 | postgres | t + non_dist_role_4 | dist_role_4 | postgres | f +(3 rows) SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_%' ORDER BY 1; rolname @@ -349,8 +374,9 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_%' ORDER BY 1; dist_role_2 dist_role_3 dist_role_4 + non_dist_role_1 non_dist_role_4 -(5 rows) +(6 rows) \c - - - :master_port DROP ROLE dist_role_3, non_dist_role_3, dist_role_4, non_dist_role_4; @@ -402,6 +428,12 @@ SELECT master_remove_node('localhost', :worker_2_port); (1 row) +GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; +ERROR: role "nondist_mixed_2" does not exist +CONTEXT: while executing command on localhost:xxxxx +\c - - - :worker_1_port +create role nondist_mixed_2; +\c - - - :master_port GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; SELECT 1 FROM master_add_node('localhost', :worker_2_port); ?column? @@ -431,19 +463,23 @@ SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid':: dist_mixed_3 dist_mixed_4 nondist_mixed_1 -(5 rows) + nondist_mixed_2 +(6 rows) \c - - - :worker_1_port SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - dist_mixed_1 | dist_mixed_3 | postgres | f - dist_mixed_1 | dist_mixed_4 | postgres | f - dist_mixed_2 | dist_mixed_3 | postgres | f - dist_mixed_2 | dist_mixed_4 | postgres | f - nondist_mixed_1 | dist_mixed_3 | postgres | f - nondist_mixed_1 | dist_mixed_4 | postgres | f -(6 rows) + dist_mixed_1 | dist_mixed_3 | postgres | f + dist_mixed_1 | dist_mixed_4 | postgres | f + dist_mixed_1 | nondist_mixed_2 | postgres | f + dist_mixed_2 | dist_mixed_3 | postgres | f + dist_mixed_2 | dist_mixed_4 | postgres | f + dist_mixed_2 | nondist_mixed_2 | postgres | f + nondist_mixed_1 | dist_mixed_3 | postgres | f + nondist_mixed_1 | dist_mixed_4 | postgres | f + nondist_mixed_1 | nondist_mixed_2 | postgres | f +(9 rows) SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; rolname @@ -453,19 +489,23 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; dist_mixed_3 dist_mixed_4 nondist_mixed_1 -(5 rows) + nondist_mixed_2 +(6 rows) \c - - - :worker_2_port SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - dist_mixed_1 | dist_mixed_3 | postgres | f - dist_mixed_1 | dist_mixed_4 | postgres | f - dist_mixed_2 | dist_mixed_3 | postgres | f - dist_mixed_2 | dist_mixed_4 | postgres | f - nondist_mixed_1 | dist_mixed_3 | postgres | f - nondist_mixed_1 | dist_mixed_4 | postgres | f -(6 rows) + dist_mixed_1 | dist_mixed_3 | postgres | f + dist_mixed_1 | dist_mixed_4 | postgres | f + dist_mixed_1 | nondist_mixed_2 | postgres | f + dist_mixed_2 | dist_mixed_3 | postgres | f + dist_mixed_2 | dist_mixed_4 | postgres | f + dist_mixed_2 | nondist_mixed_2 | postgres | f + nondist_mixed_1 | dist_mixed_3 | postgres | f + nondist_mixed_1 | dist_mixed_4 | postgres | f + nondist_mixed_1 | nondist_mixed_2 | postgres | f +(9 rows) SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; rolname @@ -475,17 +515,27 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; dist_mixed_3 dist_mixed_4 nondist_mixed_1 -(5 rows) + nondist_mixed_2 +(6 rows) \c - - - :master_port +set citus.log_remote_commands to on; +set citus.grep_remote_commands to '%DROP%'; DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2; +NOTICE: issuing DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set citus.grep_remote_commands to '%DROP%'; +reset citus.log_remote_commands; -- test drop multiple roles with non-distributed roles SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist%' ORDER BY 1; - objid + objid --------------------------------------------------------------------- dist_role_1 dist_role_2 -(2 rows) + non_dist_role_1 +(3 rows) SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; rolname @@ -498,11 +548,12 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; \c - - - :worker_1_port SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; - rolname + rolname --------------------------------------------------------------------- dist_role_1 dist_role_2 -(2 rows) + non_dist_role_1 +(3 rows) \c - - - :master_port DROP ROLE dist_role_1, non_dist_role_1, dist_role_2, non_dist_role_2; @@ -522,6 +573,12 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; --------------------------------------------------------------------- (0 rows) +\c - - - :worker_2_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + \c - - - :master_port -- test alter part of create or alter role SELECT master_remove_node('localhost', :worker_2_port); @@ -571,7 +628,11 @@ HINT: Connect to other nodes directly to manually create all necessary users an SET citus.enable_create_role_propagation TO ON; CREATE ROLE dist_cascade; GRANT nondist_cascade_1 TO nondist_cascade_2; +ERROR: role "nondist_cascade_2" does not exist +CONTEXT: while executing command on localhost:xxxxx GRANT nondist_cascade_2 TO nondist_cascade_3; +ERROR: role "nondist_cascade_3" does not exist +CONTEXT: while executing command on localhost:xxxxx SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%cascade%' ORDER BY 1; objid --------------------------------------------------------------------- @@ -579,11 +640,9 @@ SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid':: (1 row) SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - nondist_cascade_1 | nondist_cascade_2 | postgres | f - nondist_cascade_2 | nondist_cascade_3 | postgres | f -(2 rows) +(0 rows) \c - - - :worker_1_port SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; @@ -614,55 +673,43 @@ SELECT 1 FROM master_add_node('localhost', :worker_2_port); SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%cascade%' ORDER BY 1; objid --------------------------------------------------------------------- - nondist_cascade_1 - nondist_cascade_2 nondist_cascade_3 dist_cascade -(4 rows) +(2 rows) SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - nondist_cascade_1 | nondist_cascade_2 | postgres | f - nondist_cascade_2 | nondist_cascade_3 | postgres | f - nondist_cascade_3 | dist_cascade | postgres | f -(3 rows) + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) \c - - - :worker_1_port SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; rolname --------------------------------------------------------------------- dist_cascade - nondist_cascade_1 - nondist_cascade_2 nondist_cascade_3 -(4 rows) +(2 rows) SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - nondist_cascade_1 | nondist_cascade_2 | postgres | f - nondist_cascade_2 | nondist_cascade_3 | postgres | f - nondist_cascade_3 | dist_cascade | postgres | f -(3 rows) + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) \c - - - :worker_2_port SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; rolname --------------------------------------------------------------------- dist_cascade - nondist_cascade_1 - nondist_cascade_2 nondist_cascade_3 -(4 rows) +(2 rows) SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; - role | member | grantor | admin_option + role | member | grantor | admin_option --------------------------------------------------------------------- - nondist_cascade_1 | nondist_cascade_2 | postgres | f - nondist_cascade_2 | nondist_cascade_3 | postgres | f - nondist_cascade_3 | dist_cascade | postgres | f -(3 rows) + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) \c - - - :master_port DROP ROLE create_role, create_role_2, create_group, create_group_2, create_user, create_user_2, create_role_with_nothing, create_role_sysid, "create_role'edge", "create_role""edge"; diff --git a/src/test/regress/expected/create_role_propagation_0.out b/src/test/regress/expected/create_role_propagation_0.out new file mode 100644 index 000000000..9a70ca88c --- /dev/null +++ b/src/test/regress/expected/create_role_propagation_0.out @@ -0,0 +1,800 @@ +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil +--------------------------------------------------------------------- +(0 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +\c - - - :worker_1_port +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolpassword, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil +--------------------------------------------------------------------- +(0 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +\c - - - :master_port +CREATE ROLE create_role; +CREATE ROLE create_role_2; +CREATE USER create_user; +CREATE USER create_user_2; +CREATE GROUP create_group; +CREATE GROUP create_group_2; +-- show that create role fails if sysid option is given as non-int +CREATE ROLE create_role_sysid SYSID "123"; +ERROR: syntax error at or near ""123"" +-- show that create role accepts sysid option as int +CREATE ROLE create_role_sysid SYSID 123; +NOTICE: SYSID can no longer be specified +SELECT master_remove_node('localhost', :worker_2_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + +CREATE ROLE create_role_with_everything SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 105 PASSWORD 'strong_password123^' VALID UNTIL '2045-05-05 00:00:00.00+00' IN ROLE create_role, create_group ROLE create_user, create_group_2 ADMIN create_role_2, create_user_2; +CREATE ROLE create_role_with_nothing NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT 3 PASSWORD 'weakpassword' VALID UNTIL '2015-05-05 00:00:00.00+00'; +-- show that creating role from worker node is allowed +\c - - - :worker_1_port +CREATE ROLE role_on_worker; +DROP ROLE role_on_worker; +\c - - - :master_port +-- edge case role names +CREATE ROLE "create_role'edge"; +CREATE ROLE "create_role""edge"; +-- test grant role +GRANT create_group TO create_role; +GRANT create_group TO create_role_2 WITH ADMIN OPTION; +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, (rolpassword != '') as pass_not_empty, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | pass_not_empty | rolvaliduntil +--------------------------------------------------------------------- + create_group | f | t | f | f | f | f | f | -1 | | + create_group_2 | f | t | f | f | f | f | f | -1 | | + create_role | f | t | f | f | f | f | f | -1 | | + create_role"edge | f | t | f | f | f | f | f | -1 | | + create_role'edge | f | t | f | f | f | f | f | -1 | | + create_role_2 | f | t | f | f | f | f | f | -1 | | + create_role_sysid | f | t | f | f | f | f | f | -1 | | + create_role_with_everything | t | t | t | t | t | t | t | 105 | t | Thu May 04 17:00:00 2045 PDT + create_role_with_nothing | f | f | f | f | f | f | f | 3 | t | Mon May 04 17:00:00 2015 PDT + create_user | f | t | f | f | t | f | f | -1 | | + create_user_2 | f | t | f | f | t | f | f | -1 | | +(11 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + create_group | create_role | postgres | f + create_group | create_role_2 | postgres | t + create_group | create_role_with_everything | postgres | f + create_role | create_role_with_everything | postgres | f + create_role_with_everything | create_group_2 | postgres | f + create_role_with_everything | create_role_2 | postgres | t + create_role_with_everything | create_user | postgres | f + create_role_with_everything | create_user_2 | postgres | t +(8 rows) + +\c - - - :worker_1_port +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, (rolpassword != '') as pass_not_empty, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | pass_not_empty | rolvaliduntil +--------------------------------------------------------------------- + create_group | f | t | f | f | f | f | f | -1 | | + create_group_2 | f | t | f | f | f | f | f | -1 | | + create_role | f | t | f | f | f | f | f | -1 | | + create_role"edge | f | t | f | f | f | f | f | -1 | | + create_role'edge | f | t | f | f | f | f | f | -1 | | + create_role_2 | f | t | f | f | f | f | f | -1 | | + create_role_sysid | f | t | f | f | f | f | f | -1 | | + create_role_with_everything | t | t | t | t | t | t | t | 105 | t | Thu May 04 17:00:00 2045 PDT + create_role_with_nothing | f | f | f | f | f | f | f | 3 | t | Mon May 04 17:00:00 2015 PDT + create_user | f | t | f | f | t | f | f | -1 | | + create_user_2 | f | t | f | f | t | f | f | -1 | | +(11 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + create_group | create_role | postgres | f + create_group | create_role_2 | postgres | t + create_group | create_role_with_everything | postgres | f + create_role | create_role_with_everything | postgres | f + create_role_with_everything | create_group_2 | postgres | f + create_role_with_everything | create_role_2 | postgres | t + create_role_with_everything | create_user | postgres | f + create_role_with_everything | create_user_2 | postgres | t +(8 rows) + +\c - - - :master_port +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +\c - - - :worker_2_port +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, (rolpassword != '') as pass_not_empty, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | pass_not_empty | rolvaliduntil +--------------------------------------------------------------------- + create_group | f | t | f | f | f | f | f | -1 | | infinity + create_group_2 | f | t | f | f | f | f | f | -1 | | infinity + create_role | f | t | f | f | f | f | f | -1 | | infinity + create_role"edge | f | t | f | f | f | f | f | -1 | | infinity + create_role'edge | f | t | f | f | f | f | f | -1 | | infinity + create_role_2 | f | t | f | f | f | f | f | -1 | | infinity + create_role_sysid | f | t | f | f | f | f | f | -1 | | infinity + create_role_with_everything | t | t | t | t | t | t | t | 105 | t | Thu May 04 17:00:00 2045 PDT + create_role_with_nothing | f | f | f | f | f | f | f | 3 | t | Mon May 04 17:00:00 2015 PDT + create_user | f | t | f | f | t | f | f | -1 | | infinity + create_user_2 | f | t | f | f | t | f | f | -1 | | infinity +(11 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + create_group | create_role | postgres | f + create_group | create_role_2 | postgres | t + create_group | create_role_with_everything | postgres | f + create_role | create_role_with_everything | postgres | f + create_role_with_everything | create_group_2 | postgres | f + create_role_with_everything | create_role_2 | postgres | t + create_role_with_everything | create_user | postgres | f + create_role_with_everything | create_user_2 | postgres | t +(8 rows) + +\c - - - :master_port +DROP ROLE create_role_with_everything; +REVOKE create_group FROM create_role; +REVOKE ADMIN OPTION FOR create_group FROM create_role_2; +\c - - - :master_port +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, (rolpassword != '') as pass_not_empty, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | pass_not_empty | rolvaliduntil +--------------------------------------------------------------------- + create_group | f | t | f | f | f | f | f | -1 | | + create_group_2 | f | t | f | f | f | f | f | -1 | | + create_role | f | t | f | f | f | f | f | -1 | | + create_role"edge | f | t | f | f | f | f | f | -1 | | + create_role'edge | f | t | f | f | f | f | f | -1 | | + create_role_2 | f | t | f | f | f | f | f | -1 | | + create_role_sysid | f | t | f | f | f | f | f | -1 | | + create_role_with_nothing | f | f | f | f | f | f | f | 3 | t | Mon May 04 17:00:00 2015 PDT + create_user | f | t | f | f | t | f | f | -1 | | + create_user_2 | f | t | f | f | t | f | f | -1 | | +(10 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + create_group | create_role_2 | postgres | f +(1 row) + +\c - - - :worker_1_port +SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, (rolpassword != '') as pass_not_empty, rolvaliduntil FROM pg_authid WHERE rolname LIKE 'create\_%' ORDER BY rolname; + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | pass_not_empty | rolvaliduntil +--------------------------------------------------------------------- + create_group | f | t | f | f | f | f | f | -1 | | + create_group_2 | f | t | f | f | f | f | f | -1 | | + create_role | f | t | f | f | f | f | f | -1 | | + create_role"edge | f | t | f | f | f | f | f | -1 | | + create_role'edge | f | t | f | f | f | f | f | -1 | | + create_role_2 | f | t | f | f | f | f | f | -1 | | + create_role_sysid | f | t | f | f | f | f | f | -1 | | + create_role_with_nothing | f | f | f | f | f | f | f | 3 | t | Mon May 04 17:00:00 2015 PDT + create_user | f | t | f | f | t | f | f | -1 | | + create_user_2 | f | t | f | f | t | f | f | -1 | | +(10 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE 'create\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + create_group | create_role_2 | postgres | f +(1 row) + +\c - - - :master_port +-- test grants with distributed and non-distributed roles +SELECT master_remove_node('localhost', :worker_2_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + +create role test_admin_role; +CREATE ROLE dist_role_1 SUPERUSER; +CREATE ROLE dist_role_2; +CREATE ROLE dist_role_3; +CREATE ROLE dist_role_4; +SET citus.enable_create_role_propagation TO OFF; +CREATE ROLE non_dist_role_1 SUPERUSER; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE non_dist_role_2; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE non_dist_role_3; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE non_dist_role_4; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +SET citus.enable_create_role_propagation TO ON; +grant dist_role_3,dist_role_1 to test_admin_role with admin option; +SET ROLE dist_role_1; +GRANT non_dist_role_1 TO non_dist_role_2; +ERROR: connection to the remote node dist_role_1@localhost:xxxxx failed with the following error: FATAL: role "dist_role_1" is not permitted to log in +SET citus.enable_create_role_propagation TO OFF; +grant dist_role_1 to non_dist_role_1 with admin option; +SET ROLE non_dist_role_1; +GRANT dist_role_1 TO dist_role_2 granted by non_dist_role_1; +RESET ROLE; +SET citus.enable_create_role_propagation TO ON; +GRANT dist_role_3 TO non_dist_role_3 granted by test_admin_role; +ERROR: role "non_dist_role_3" does not exist +CONTEXT: while executing command on localhost:xxxxx +GRANT non_dist_role_4 TO dist_role_4; +GRANT dist_role_3 TO dist_role_4 granted by test_admin_role; +SELECT 1 FROM master_add_node('localhost', :worker_2_port); +WARNING: role "non_dist_role_1" does not exist +CONTEXT: while executing command on localhost:xxxxx +ERROR: failure on connection marked as essential: localhost:xxxxx +\c - - - :worker_2_port +create role non_dist_role_1; +\c - - - :master_port +--will be successful since non_dist_role_1 is created on worker_2 +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + dist_role_3 + dist_role_4 + non_dist_role_1 + non_dist_role_4 +(6 rows) + +SELECT result FROM run_command_on_all_nodes( + $$ + SELECT json_agg(q.* ORDER BY member) FROM ( + SELECT member::regrole::text, roleid::regrole::text AS role, grantor::regrole::text, admin_option + FROM pg_auth_members WHERE roleid::regrole::text = 'dist_role_3' + ) q; + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role_4","role":"dist_role_3","grantor":"test_admin_role","admin_option":false}, + + {"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] +(3 rows) + +REVOKE dist_role_3 from dist_role_4 granted by test_admin_role cascade; +SELECT result FROM run_command_on_all_nodes( + $$ + SELECT json_agg(q.* ORDER BY member) FROM ( + SELECT member::regrole::text, roleid::regrole::text AS role, grantor::regrole::text, admin_option + FROM pg_auth_members WHERE roleid::regrole::text = 'dist_role_3' + order by member::regrole::text + ) q; + $$ +); + result +--------------------------------------------------------------------- + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] +(3 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, (grantor::regrole::text IN ('postgres', 'non_dist_role_1', 'dist_role_1','test_admin_role')) AS grantor, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + dist_role_1 | dist_role_2 | t | f + dist_role_1 | non_dist_role_1 | t | t + dist_role_1 | test_admin_role | t | t + dist_role_3 | test_admin_role | t | t + non_dist_role_4 | dist_role_4 | t | f +(5 rows) + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + dist_role_3 + dist_role_4 + non_dist_role_1 + non_dist_role_4 +(6 rows) + +REVOKE dist_role_3 from non_dist_role_3 granted by test_admin_role cascade; +WARNING: role "non_dist_role_3" is not a member of role "dist_role_3" +ERROR: role "non_dist_role_3" does not exist +CONTEXT: while executing command on localhost:xxxxx +SELECT result FROM run_command_on_all_nodes( + $$ + SELECT json_agg(q.* ORDER BY member) FROM ( + SELECT member::regrole::text, roleid::regrole::text AS role, grantor::regrole::text, admin_option + FROM pg_auth_members WHERE roleid::regrole::text = 'dist_role_3' + order by member::regrole::text + ) q; + $$ +); + result +--------------------------------------------------------------------- + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] + [{"member":"test_admin_role","role":"dist_role_3","grantor":"postgres","admin_option":true}] +(3 rows) + +revoke dist_role_3,dist_role_1 from test_admin_role cascade; +drop role test_admin_role; +\c - - - :worker_1_port +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + non_dist_role_4 | dist_role_4 | postgres | f +(1 row) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + dist_role_3 + dist_role_4 + non_dist_role_1 + non_dist_role_4 +(6 rows) + +\c - - - :worker_2_port +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + dist_role_1 | dist_role_2 | non_dist_role_1 | f + dist_role_1 | non_dist_role_1 | dist_role_1 | t + non_dist_role_4 | dist_role_4 | postgres | f +(3 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + dist_role_3 + dist_role_4 + non_dist_role_1 + non_dist_role_4 +(6 rows) + +\c - - - :master_port +DROP ROLE dist_role_3, non_dist_role_3, dist_role_4, non_dist_role_4; +-- test grant with multiple mixed roles +CREATE ROLE dist_mixed_1; +CREATE ROLE dist_mixed_2; +CREATE ROLE dist_mixed_3; +CREATE ROLE dist_mixed_4; +SET citus.enable_create_role_propagation TO OFF; +CREATE ROLE nondist_mixed_1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE nondist_mixed_2; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_mixed_1 + dist_mixed_2 + dist_mixed_3 + dist_mixed_4 +(4 rows) + +\c - - - :worker_1_port +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_mixed_1 + dist_mixed_2 + dist_mixed_3 + dist_mixed_4 +(4 rows) + +\c - - - :master_port +SELECT master_remove_node('localhost', :worker_2_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + +GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; +ERROR: role "nondist_mixed_2" does not exist +CONTEXT: while executing command on localhost:xxxxx +\c - - - :worker_1_port +create role nondist_mixed_2; +\c - - - :master_port +GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + dist_mixed_1 | dist_mixed_3 | postgres | f + dist_mixed_1 | dist_mixed_4 | postgres | f + dist_mixed_1 | nondist_mixed_2 | postgres | f + dist_mixed_2 | dist_mixed_3 | postgres | f + dist_mixed_2 | dist_mixed_4 | postgres | f + dist_mixed_2 | nondist_mixed_2 | postgres | f + nondist_mixed_1 | dist_mixed_3 | postgres | f + nondist_mixed_1 | dist_mixed_4 | postgres | f + nondist_mixed_1 | nondist_mixed_2 | postgres | f +(9 rows) + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_mixed_1 + dist_mixed_2 + dist_mixed_3 + dist_mixed_4 + nondist_mixed_1 + nondist_mixed_2 +(6 rows) + +\c - - - :worker_1_port +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + dist_mixed_1 | dist_mixed_3 | postgres | f + dist_mixed_1 | dist_mixed_4 | postgres | f + dist_mixed_1 | nondist_mixed_2 | postgres | f + dist_mixed_2 | dist_mixed_3 | postgres | f + dist_mixed_2 | dist_mixed_4 | postgres | f + dist_mixed_2 | nondist_mixed_2 | postgres | f + nondist_mixed_1 | dist_mixed_3 | postgres | f + nondist_mixed_1 | dist_mixed_4 | postgres | f + nondist_mixed_1 | nondist_mixed_2 | postgres | f +(9 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_mixed_1 + dist_mixed_2 + dist_mixed_3 + dist_mixed_4 + nondist_mixed_1 + nondist_mixed_2 +(6 rows) + +\c - - - :worker_2_port +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + dist_mixed_1 | dist_mixed_3 | postgres | f + dist_mixed_1 | dist_mixed_4 | postgres | f + dist_mixed_1 | nondist_mixed_2 | postgres | f + dist_mixed_2 | dist_mixed_3 | postgres | f + dist_mixed_2 | dist_mixed_4 | postgres | f + dist_mixed_2 | nondist_mixed_2 | postgres | f + nondist_mixed_1 | dist_mixed_3 | postgres | f + nondist_mixed_1 | dist_mixed_4 | postgres | f + nondist_mixed_1 | nondist_mixed_2 | postgres | f +(9 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_mixed_1 + dist_mixed_2 + dist_mixed_3 + dist_mixed_4 + nondist_mixed_1 + nondist_mixed_2 +(6 rows) + +\c - - - :master_port +set citus.log_remote_commands to on; +set citus.grep_remote_commands to '%DROP%'; +DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2; +NOTICE: issuing DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set citus.grep_remote_commands to '%DROP%'; +reset citus.log_remote_commands; +-- test drop multiple roles with non-distributed roles +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + non_dist_role_1 +(3 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + non_dist_role_1 + non_dist_role_2 +(4 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_role_1 + dist_role_2 + non_dist_role_1 +(3 rows) + +\c - - - :master_port +DROP ROLE dist_role_1, non_dist_role_1, dist_role_2, non_dist_role_2; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist%' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c - - - :worker_2_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c - - - :master_port +-- test alter part of create or alter role +SELECT master_remove_node('localhost', :worker_2_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + +DROP ROLE create_role, create_role_2; +\c - - - :worker_2_port +SELECT rolname, rolcanlogin FROM pg_authid WHERE rolname = 'create_role' OR rolname = 'create_role_2' ORDER BY rolname; + rolname | rolcanlogin +--------------------------------------------------------------------- + create_role | f + create_role_2 | f +(2 rows) + +\c - - - :master_port +CREATE ROLE create_role LOGIN; +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CREATE ROLE create_role_2 LOGIN; +\c - - - :worker_2_port +SELECT rolname, rolcanlogin FROM pg_authid WHERE rolname = 'create_role' OR rolname = 'create_role_2' ORDER BY rolname; + rolname | rolcanlogin +--------------------------------------------------------------------- + create_role | t + create_role_2 | t +(2 rows) + +\c - - - :master_port +-- test cascading grants +SET citus.enable_create_role_propagation TO OFF; +CREATE ROLE nondist_cascade_1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE nondist_cascade_2; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +CREATE ROLE nondist_cascade_3; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +SET citus.enable_create_role_propagation TO ON; +CREATE ROLE dist_cascade; +GRANT nondist_cascade_1 TO nondist_cascade_2; +ERROR: role "nondist_cascade_2" does not exist +CONTEXT: while executing command on localhost:xxxxx +GRANT nondist_cascade_2 TO nondist_cascade_3; +ERROR: role "nondist_cascade_3" does not exist +CONTEXT: while executing command on localhost:xxxxx +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%cascade%' ORDER BY 1; + objid +--------------------------------------------------------------------- + dist_cascade +(1 row) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_cascade +(1 row) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +\c - - - :master_port +SELECT master_remove_node('localhost', :worker_2_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + +GRANT nondist_cascade_3 TO dist_cascade; +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%cascade%' ORDER BY 1; + objid +--------------------------------------------------------------------- + nondist_cascade_3 + dist_cascade +(2 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_cascade + nondist_cascade_3 +(2 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) + +\c - - - :worker_2_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%cascade%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + dist_cascade + nondist_cascade_3 +(2 rows) + +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%cascade%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- + nondist_cascade_3 | dist_cascade | postgres | f +(1 row) + +\c - - - :master_port +DROP ROLE create_role, create_role_2, create_group, create_group_2, create_user, create_user_2, create_role_with_nothing, create_role_sysid, "create_role'edge", "create_role""edge"; +-- test grant non-existing roles +CREATE ROLE existing_role_1; +CREATE ROLE existing_role_2; +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%existing%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +GRANT existing_role_1, nonexisting_role_1 TO existing_role_2, nonexisting_role_2; +ERROR: role "nonexisting_role_2" does not exist +SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%existing%' ORDER BY 1, 2; + role | member | grantor | admin_option +--------------------------------------------------------------------- +(0 rows) + +-- test drop non-existing roles +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%existing%' ORDER BY 1; + objid +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +\c - - - :master_port +DROP ROLE existing_role_1, existing_role_2, nonexisting_role_1, nonexisting_role_2; +ERROR: role "nonexisting_role_1" does not exist +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%existing%' ORDER BY 1; + objid +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- + existing_role_1 + existing_role_2 +(2 rows) + +\c - - - :master_port +DROP ROLE IF EXISTS existing_role_1, existing_role_2, nonexisting_role_1, nonexisting_role_2; +NOTICE: role "nonexisting_role_1" does not exist, skipping +NOTICE: role "nonexisting_role_2" does not exist, skipping +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%existing%' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c - - - :worker_1_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c - - - :master_port +DROP ROLE nondist_cascade_1, nondist_cascade_2, nondist_cascade_3, dist_cascade; diff --git a/src/test/regress/expected/granted_by_support.out b/src/test/regress/expected/granted_by_support.out new file mode 100644 index 000000000..9c76c88fe --- /dev/null +++ b/src/test/regress/expected/granted_by_support.out @@ -0,0 +1,194 @@ +-- Active: 1700033167033@@localhost@9700@gurkanindibay@public +--In below tests, complex role hierarchy is created and then granted by support is tested. +--- Test 1: Tests from main database +select 1 from citus_remove_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +reset citus.enable_create_role_propagation; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +grant dist_role2 to non_dist_role1 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role1 ; +reset citus.enable_create_role_propagation; +grant dist_role2 to "dist_role5'_test" granted by non_dist_role1;--will fail since non_dist_role1 does not exist on worker_1 +ERROR: role "non_dist_role1" does not exist +CONTEXT: while executing command on localhost:xxxxx +\c - - - :master_port +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +--will fail since non_dist_role2 does not exist in worker_1 +grant dist_role2 to non_dist_role2 with admin option; +ERROR: role "non_dist_role2" does not exist +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +ERROR: role "non_dist_role2" does not exist +grant non_dist_role2 to "dist_role5'_test"; +ERROR: role "non_dist_role2" does not exist +\c - - - :worker_1_port +create role non_dist_role2; +\c - - - :master_port +--will be successful since non_dist_role has been created on worker_1 +grant dist_role2 to non_dist_role2 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +grant non_dist_role2 to "dist_role5'_test"; +grant dist_role4 to "dist_role5'_test" with admin option; +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','non_dist_role1') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true}] +(2 rows) + +--below command propagates the non_dist_role1 since non_dist_role1 is already granted to dist_role1 +--and citus sees granted roles as a dependency and citus propagates the dependent roles +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- + non_dist_role1 +(1 row) + +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 +ERROR: role "dist_role4" is a member of role "dist_role3" +--Below command will not be successful since non_dist_role1 is propagated with the dependency resolution above +--however, ADMIN OPTION is not propagated for non_dist_role1 to worker 1 because the citus.enable_create_role_propagation is off +grant non_dist_role1 to dist_role4 granted by dist_role1; +ERROR: permission denied to grant privileges as role "dist_role1" +DETAIL: The grantor must have the ADMIN option on role "non_dist_role1". +CONTEXT: while executing command on localhost:xxxxx +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" +ERROR: role "dist_role5'_test" is a member of role "dist_role3" +set citus.enable_create_role_propagation to off; +create role non_dist_role_for_mds; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +grant dist_role3 to non_dist_role_for_mds with admin option; +grant non_dist_role_for_mds to dist_role1 with admin option; +grant dist_role3 to dist_role4 with admin option GRANTED BY non_dist_role_for_mds; +reset citus.enable_create_role_propagation; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role_for_mds' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"', 'non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"non_dist_role_for_mds","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true}] +(2 rows) + +set citus.enable_create_role_propagation to off; +create role non_dist_role_mds_fail; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +grant dist_role2 to non_dist_role_mds_fail with admin option; +grant dist_role2 to non_dist_role_for_mds GRANTED BY non_dist_role_mds_fail; +reset citus.enable_create_role_propagation; +--will fail since non_dist_role_for_mds is not in dependency resolution +select 1 from citus_add_node ('localhost',:worker_2_port); +WARNING: role "non_dist_role_mds_fail" does not exist +CONTEXT: while executing command on localhost:xxxxx +ERROR: failure on connection marked as essential: localhost:xxxxx +--this grant statement will add non_dist_role_mds_fail to dist_role3 dependencies +grant non_dist_role_mds_fail to dist_role3; +--will be successful since non_dist_role_mds_fail is in dependency resolution of dist_role3 +-- and will be created in metadata sync phase +select 1 from citus_add_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"non_dist_role_for_mds","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role2","grantor":"non_dist_role_mds_fail","admin_option":false},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"non_dist_role_for_mds","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role2","grantor":"non_dist_role_mds_fail","admin_option":false},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] +(3 rows) + +--clean all resources +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1,non_dist_role2,non_dist_role_for_mds,non_dist_role_mds_fail; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +reset citus.enable_create_role_propagation; +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + + + +(3 rows) + diff --git a/src/test/regress/expected/granted_by_support_0.out b/src/test/regress/expected/granted_by_support_0.out new file mode 100644 index 000000000..e850214a8 --- /dev/null +++ b/src/test/regress/expected/granted_by_support_0.out @@ -0,0 +1,193 @@ +-- Active: 1700033167033@@localhost@9700@gurkanindibay@public +--In below tests, complex role hierarchy is created and then granted by support is tested. +--- Test 1: Tests from main database +select 1 from citus_remove_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +reset citus.enable_create_role_propagation; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +grant dist_role2 to non_dist_role1 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role1 ; +reset citus.enable_create_role_propagation; +grant dist_role2 to "dist_role5'_test" granted by non_dist_role1;--will fail since non_dist_role1 does not exist on worker_1 +ERROR: role "non_dist_role1" does not exist +CONTEXT: while executing command on localhost:xxxxx +\c - - - :master_port +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +--will fail since non_dist_role2 does not exist in worker_1 +grant dist_role2 to non_dist_role2 with admin option; +ERROR: role "non_dist_role2" does not exist +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +ERROR: role "non_dist_role2" does not exist +grant non_dist_role2 to "dist_role5'_test"; +ERROR: role "non_dist_role2" does not exist +\c - - - :worker_1_port +create role non_dist_role2; +\c - - - :master_port +--will be successful since non_dist_role has been created on worker_1 +grant dist_role2 to non_dist_role2 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +NOTICE: role "dist_role4" is already a member of role "dist_role2" +grant non_dist_role2 to "dist_role5'_test"; +grant dist_role4 to "dist_role5'_test" with admin option; +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','non_dist_role1') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true}] +(2 rows) + +--below command propagates the non_dist_role1 since non_dist_role1 is already granted to dist_role1 +--and citus sees granted roles as a dependency and citus propagates the dependent roles +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- + non_dist_role1 +(1 row) + +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 +ERROR: role "dist_role4" is a member of role "dist_role3" +--Below command will not be successful since non_dist_role1 is propagated with the dependency resolution above +--however, ADMIN OPTION is not propagated for non_dist_role1 to worker 1 because the citus.enable_create_role_propagation is off +grant non_dist_role1 to dist_role4 granted by dist_role1; +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" +ERROR: role "dist_role5'_test" is a member of role "dist_role3" +set citus.enable_create_role_propagation to off; +create role non_dist_role_for_mds; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +grant dist_role3 to non_dist_role_for_mds with admin option; +grant non_dist_role_for_mds to dist_role1 with admin option; +grant dist_role3 to dist_role4 with admin option GRANTED BY non_dist_role_for_mds; +NOTICE: role "dist_role4" is already a member of role "dist_role3" +reset citus.enable_create_role_propagation; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role_for_mds' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"', 'non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"non_dist_role1","grantor":"dist_role1","admin_option":false},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"non_dist_role1","grantor":"dist_role1","admin_option":false},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true}] +(2 rows) + +set citus.enable_create_role_propagation to off; +create role non_dist_role_mds_fail; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +grant dist_role2 to non_dist_role_mds_fail with admin option; +grant dist_role2 to non_dist_role_for_mds GRANTED BY non_dist_role_mds_fail; +reset citus.enable_create_role_propagation; +--will fail since non_dist_role_for_mds is not in dependency resolution +select 1 from citus_add_node ('localhost',:worker_2_port); +WARNING: role "non_dist_role_mds_fail" does not exist +CONTEXT: while executing command on localhost:xxxxx +ERROR: failure on connection marked as essential: localhost:xxxxx +--this grant statement will add non_dist_role_mds_fail to dist_role3 dependencies +grant non_dist_role_mds_fail to dist_role3; +--will be successful since non_dist_role_mds_fail is in dependency resolution of dist_role3 +-- and will be created in metadata sync phase +select 1 from citus_add_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"non_dist_role1","grantor":"dist_role1","admin_option":false},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role2","grantor":"non_dist_role_mds_fail","admin_option":false},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role2","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"non_dist_role1","grantor":"dist_role1","admin_option":false},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"non_dist_role_for_mds","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role3","role":"non_dist_role_mds_fail","grantor":"postgres","admin_option":false},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true},{"member":"dist_role4","role":"non_dist_role1","grantor":"dist_role1","admin_option":false},{"member":"non_dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role2","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"non_dist_role_for_mds","role":"dist_role2","grantor":"non_dist_role_mds_fail","admin_option":false},{"member":"non_dist_role_for_mds","role":"dist_role3","grantor":"postgres","admin_option":true}] +(3 rows) + +--clean all resources +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1,non_dist_role2,non_dist_role_for_mds,non_dist_role_mds_fail; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + objid +--------------------------------------------------------------------- +(0 rows) + +reset citus.enable_create_role_propagation; +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + + + +(3 rows) + diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 546c0a832..ec7faa098 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -1012,14 +1012,14 @@ WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; -- Set GUCs to log remote commands and filter on REVOKE commands SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%REVOKE%'; - -- test REVOKES as well - GRANT role1 TO role2; - REVOKE SET OPTION FOR role1 FROM role2; +-- test REVOKES as well +GRANT role1 TO role2; +REVOKE SET OPTION FOR role1 FROM role2; NOTICE: issuing REVOKE SET OPTION FOR role1 FROM role2 RESTRICT; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing REVOKE SET OPTION FOR role1 FROM role2 RESTRICT; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx - REVOKE INHERIT OPTION FOR role1 FROM role2; +REVOKE INHERIT OPTION FOR role1 FROM role2; NOTICE: issuing REVOKE INHERIT OPTION FOR role1 FROM role2 RESTRICT; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing REVOKE INHERIT OPTION FOR role1 FROM role2 RESTRICT; @@ -1033,14 +1033,16 @@ CREATE ROLE role5; RESET citus.enable_ddl_propagation; -- by default, admin option is false, inherit is true, set is true GRANT role3 TO role4; +ERROR: role "role4" does not exist +CONTEXT: while executing command on localhost:xxxxx GRANT role3 TO role5 WITH ADMIN TRUE, INHERIT FALSE, SET FALSE; +ERROR: role "role5" does not exist +CONTEXT: while executing command on localhost:xxxxx SELECT roleid::regrole::text AS role, member::regrole::text, admin_option, inherit_option, set_option FROM pg_auth_members WHERE roleid::regrole::text = 'role3' ORDER BY 1, 2; - role | member | admin_option | inherit_option | set_option + role | member | admin_option | inherit_option | set_option --------------------------------------------------------------------- - role3 | role4 | f | t | t - role3 | role5 | t | f | f -(2 rows) +(0 rows) DROP ROLE role3, role4, role5; -- Test that everything works fine for roles that are propagated @@ -1120,7 +1122,7 @@ DROP ROLE role6, role7, role8, role9, role10, role11, role12, -- when adding a new node. -- First, we need to remove the node: SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); -?column? + ?column? --------------------------------------------------------------------- 1 (1 row) diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index cfff00942..8dd5cfbc9 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -62,6 +62,7 @@ test: alter_database_propagation test: citus_shards test: reassign_owned +test: granted_by_support # ---------- # multi_citus_tools tests utility functions written for citus tools diff --git a/src/test/regress/sql/create_role_propagation.sql b/src/test/regress/sql/create_role_propagation.sql index bd2951b17..d5cdb8ec9 100644 --- a/src/test/regress/sql/create_role_propagation.sql +++ b/src/test/regress/sql/create_role_propagation.sql @@ -75,12 +75,12 @@ SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::t \c - - - :master_port -create role test_admin_role; - -- test grants with distributed and non-distributed roles SELECT master_remove_node('localhost', :worker_2_port); +create role test_admin_role; + CREATE ROLE dist_role_1 SUPERUSER; CREATE ROLE dist_role_2; CREATE ROLE dist_role_3; @@ -123,6 +123,17 @@ GRANT dist_role_3 TO dist_role_4 granted by test_admin_role; SELECT 1 FROM master_add_node('localhost', :worker_2_port); +\c - - - :worker_2_port +create role non_dist_role_1; + +\c - - - :master_port +--will be successful since non_dist_role_1 is created on worker_2 +SELECT 1 FROM master_add_node('localhost', :worker_2_port); + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text LIKE '%dist\_%' ORDER BY 1; + + + SELECT result FROM run_command_on_all_nodes( $$ SELECT json_agg(q.* ORDER BY member) FROM ( @@ -194,6 +205,12 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; SELECT master_remove_node('localhost', :worker_2_port); GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; + +\c - - - :worker_1_port +create role nondist_mixed_2; + +\c - - - :master_port +GRANT dist_mixed_1, dist_mixed_2, nondist_mixed_1 TO dist_mixed_3, dist_mixed_4, nondist_mixed_2; SELECT 1 FROM master_add_node('localhost', :worker_2_port); SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::text, admin_option FROM pg_auth_members WHERE roleid::regrole::text LIKE '%dist\_mixed%' ORDER BY 1, 2; @@ -206,7 +223,12 @@ SELECT roleid::regrole::text AS role, member::regrole::text, grantor::regrole::t SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist\_mixed%' ORDER BY 1; \c - - - :master_port +set citus.log_remote_commands to on; +set citus.grep_remote_commands to '%DROP%'; DROP ROLE dist_mixed_1, dist_mixed_2, dist_mixed_3, dist_mixed_4, nondist_mixed_1, nondist_mixed_2; +set citus.grep_remote_commands to '%DROP%'; +reset citus.log_remote_commands; + -- test drop multiple roles with non-distributed roles @@ -224,6 +246,9 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; \c - - - :worker_1_port SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; + +\c - - - :worker_2_port +SELECT rolname FROM pg_authid WHERE rolname LIKE '%dist%' ORDER BY 1; \c - - - :master_port -- test alter part of create or alter role @@ -321,3 +346,4 @@ SELECT rolname FROM pg_authid WHERE rolname LIKE '%existing%' ORDER BY 1; \c - - - :master_port DROP ROLE nondist_cascade_1, nondist_cascade_2, nondist_cascade_3, dist_cascade; + diff --git a/src/test/regress/sql/granted_by_support.sql b/src/test/regress/sql/granted_by_support.sql new file mode 100644 index 000000000..e0d742a3e --- /dev/null +++ b/src/test/regress/sql/granted_by_support.sql @@ -0,0 +1,168 @@ +-- Active: 1700033167033@@localhost@9700@gurkanindibay@public +--In below tests, complex role hierarchy is created and then granted by support is tested. + +--- Test 1: Tests from main database +select 1 from citus_remove_node ('localhost',:worker_2_port); +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +reset citus.enable_create_role_propagation; +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; + +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; + +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +grant dist_role2 to non_dist_role1 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role1 ; +reset citus.enable_create_role_propagation; + +grant dist_role2 to "dist_role5'_test" granted by non_dist_role1;--will fail since non_dist_role1 does not exist on worker_1 + + +\c - - - :master_port +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; + + +--will fail since non_dist_role2 does not exist in worker_1 +grant dist_role2 to non_dist_role2 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +grant non_dist_role2 to "dist_role5'_test"; + + +\c - - - :worker_1_port +create role non_dist_role2; + +\c - - - :master_port +--will be successful since non_dist_role has been created on worker_1 +grant dist_role2 to non_dist_role2 with admin option; +grant dist_role2 to dist_role4 granted by non_dist_role2 ; +grant non_dist_role2 to "dist_role5'_test"; + + +grant dist_role4 to "dist_role5'_test" with admin option; + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','non_dist_role1') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + +--below command propagates the non_dist_role1 since non_dist_role1 is already granted to dist_role1 +--and citus sees granted roles as a dependency and citus propagates the dependent roles + +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; + +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 + +--Below command will not be successful since non_dist_role1 is propagated with the dependency resolution above +--however, ADMIN OPTION is not propagated for non_dist_role1 to worker 1 because the citus.enable_create_role_propagation is off +grant non_dist_role1 to dist_role4 granted by dist_role1; + +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" + + + + +set citus.enable_create_role_propagation to off; +create role non_dist_role_for_mds; + +grant dist_role3 to non_dist_role_for_mds with admin option; +grant non_dist_role_for_mds to dist_role1 with admin option; + +grant dist_role3 to dist_role4 with admin option GRANTED BY non_dist_role_for_mds; +reset citus.enable_create_role_propagation; + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role_for_mds' ORDER BY 1; + + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"', 'non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + + +set citus.enable_create_role_propagation to off; +create role non_dist_role_mds_fail; + +grant dist_role2 to non_dist_role_mds_fail with admin option; +grant dist_role2 to non_dist_role_for_mds GRANTED BY non_dist_role_mds_fail; + + +reset citus.enable_create_role_propagation; + +--will fail since non_dist_role_for_mds is not in dependency resolution +select 1 from citus_add_node ('localhost',:worker_2_port); + +--this grant statement will add non_dist_role_mds_fail to dist_role3 dependencies +grant non_dist_role_mds_fail to dist_role3; + +--will be successful since non_dist_role_mds_fail is in dependency resolution of dist_role3 +-- and will be created in metadata sync phase +select 1 from citus_add_node ('localhost',:worker_2_port); + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + + +--clean all resources +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1,non_dist_role2,non_dist_role_for_mds,non_dist_role_mds_fail; + +SELECT objid::regrole FROM pg_catalog.pg_dist_object WHERE classid='pg_authid'::regclass::oid AND objid::regrole::text= 'non_dist_role1' ORDER BY 1; +reset citus.enable_create_role_propagation; + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"','non_dist_role_for_mds','non_dist_role1','non_dist_role2') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + + +