diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index f29998b6ebe..f37ec668cb7 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -83,12 +83,15 @@ typedef struct FixedParallelState /* Fixed-size state that workers must restore. */ Oid database_id; Oid authenticated_user_id; - Oid current_user_id; + Oid session_user_id; Oid outer_user_id; + Oid current_user_id; Oid temp_namespace_id; Oid temp_toast_namespace_id; int sec_context; - bool is_superuser; + bool authenticated_user_is_superuser; + bool session_user_is_superuser; + bool role_is_superuser; PGPROC *parallel_leader_pgproc; pid_t parallel_leader_pid; BackendId parallel_leader_backend_id; @@ -330,9 +333,12 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_allocate(pcxt->toc, sizeof(FixedParallelState)); fps->database_id = MyDatabaseId; fps->authenticated_user_id = GetAuthenticatedUserId(); + fps->session_user_id = GetSessionUserId(); fps->outer_user_id = GetCurrentRoleId(); - fps->is_superuser = session_auth_is_superuser; GetUserIdAndSecContext(&fps->current_user_id, &fps->sec_context); + fps->authenticated_user_is_superuser = GetAuthenticatedUserIsSuperuser(); + fps->session_user_is_superuser = GetSessionUserIsSuperuser(); + fps->role_is_superuser = session_auth_is_superuser; GetTempNamespaceState(&fps->temp_namespace_id, &fps->temp_toast_namespace_id); fps->parallel_leader_pgproc = MyProc; @@ -1396,6 +1402,18 @@ ParallelWorkerMain(Datum main_arg) entrypt = LookupParallelWorkerFunction(library_name, function_name); + /* + * Restore current session authorization and role id. No verification + * happens here, we just blindly adopt the leader's state. Note that this + * has to happen before InitPostgres, since InitializeSessionUserId will + * not set these variables. + */ + SetAuthenticatedUserId(fps->authenticated_user_id, + fps->authenticated_user_is_superuser); + SetSessionAuthorization(fps->session_user_id, + fps->session_user_is_superuser); + SetCurrentRoleId(fps->outer_user_id, fps->role_is_superuser); + /* Restore database connection. */ BackgroundWorkerInitializeConnectionByOid(fps->database_id, fps->authenticated_user_id, @@ -1461,13 +1479,13 @@ ParallelWorkerMain(Datum main_arg) InvalidateSystemCaches(); /* - * Restore current role id. Skip verifying whether session user is - * allowed to become this role and blindly restore the leader's state for - * current role. + * Restore current user ID and security context. No verification happens + * here, we just blindly adopt the leader's state. We can't do this till + * after restoring GUCs, else we'll get complaints about restoring + * session_authorization and role. (In effect, we're assuming that all + * the restored values are okay to set, even if we are now inside a + * restricted context.) */ - SetCurrentRoleId(fps->outer_user_id, fps->is_superuser); - - /* Restore user ID and security context. */ SetUserIdAndSecContext(fps->current_user_id, fps->sec_context); /* Restore temp-namespace state to ensure search path matches leader's. */ diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index e5ddcda0b4a..f105e006c3f 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -753,41 +753,79 @@ check_session_authorization(char **newval, void **extra, GucSource source) if (*newval == NULL) return true; - if (!IsTransactionState()) + if (InitializingParallelWorker) { /* - * Can't do catalog lookups, so fail. The result of this is that - * session_authorization cannot be set in postgresql.conf, which seems - * like a good thing anyway, so we don't work hard to avoid it. + * In parallel worker initialization, we want to copy the leader's + * state even if it no longer matches the catalogs. ParallelWorkerMain + * already installed the correct role OID and superuser state. */ - return false; + roleid = GetSessionUserId(); + is_superuser = GetSessionUserIsSuperuser(); } - - /* Look up the username */ - roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); - if (!HeapTupleIsValid(roleTup)) + else { + if (!IsTransactionState()) + { + /* + * Can't do catalog lookups, so fail. The result of this is that + * session_authorization cannot be set in postgresql.conf, which + * seems like a good thing anyway, so we don't work hard to avoid + * it. + */ + return false; + } + /* * When source == PGC_S_TEST, we don't throw a hard error for a - * nonexistent user name, only a NOTICE. See comments in guc.h. + * nonexistent user name or insufficient privileges, only a NOTICE. + * See comments in guc.h. */ - if (source == PGC_S_TEST) + + /* Look up the username */ + roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); + if (!HeapTupleIsValid(roleTup)) { - ereport(NOTICE, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role \"%s\" does not exist", *newval))); - return true; + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("role \"%s\" does not exist", *newval))); + return true; + } + GUC_check_errmsg("role \"%s\" does not exist", *newval); + return false; + } + + roleform = (Form_pg_authid) GETSTRUCT(roleTup); + roleid = roleform->oid; + is_superuser = roleform->rolsuper; + + ReleaseSysCache(roleTup); + + /* + * Only superusers may SET SESSION AUTHORIZATION a role other than + * itself. Note that in case of multiple SETs in a single session, the + * original authenticated user's superuserness is what matters. + */ + if (roleid != GetAuthenticatedUserId() && + !GetAuthenticatedUserIsSuperuser()) + { + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission will be denied to set session authorization \"%s\"", + *newval))); + return true; + } + GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE); + GUC_check_errmsg("permission denied to set session authorization \"%s\"", + *newval); + return false; } - GUC_check_errmsg("role \"%s\" does not exist", *newval); - return false; } - roleform = (Form_pg_authid) GETSTRUCT(roleTup); - roleid = roleform->oid; - is_superuser = roleform->rolsuper; - - ReleaseSysCache(roleTup); - /* Set up "extra" struct for assign_session_authorization to use */ myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra)); if (!myextra) @@ -836,6 +874,16 @@ check_role(char **newval, void **extra, GucSource source) roleid = InvalidOid; is_superuser = false; } + else if (InitializingParallelWorker) + { + /* + * In parallel worker initialization, we want to copy the leader's + * state even if it no longer matches the catalogs. ParallelWorkerMain + * already installed the correct role OID and superuser state. + */ + roleid = GetCurrentRoleId(); + is_superuser = session_auth_is_superuser; + } else { if (!IsTransactionState()) @@ -875,13 +923,8 @@ check_role(char **newval, void **extra, GucSource source) ReleaseSysCache(roleTup); - /* - * Verify that session user is allowed to become this role, but skip - * this in parallel mode, where we must blindly recreate the parallel - * leader's state. - */ - if (!InitializingParallelWorker && - !is_member_of_role(GetSessionUserId(), roleid)) + /* Verify that session user is allowed to become this role */ + if (!is_member_of_role(GetSessionUserId(), roleid)) { if (source == PGC_S_TEST) { diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 8997193ca1c..3fc27d7255d 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -29,6 +29,7 @@ #include #include "access/htup_details.h" +#include "access/parallel.h" #include "catalog/pg_authid.h" #include "common/file_perm.h" #include "libpq/libpq.h" @@ -508,7 +509,7 @@ GetOuterUserId(void) static void -SetOuterUserId(Oid userid) +SetOuterUserId(Oid userid, bool is_superuser) { AssertState(SecurityRestrictionContext == 0); AssertArg(OidIsValid(userid)); @@ -516,6 +517,11 @@ SetOuterUserId(Oid userid) /* We force the effective user ID to match, too */ CurrentUserId = userid; + + /* Also update the is_superuser GUC to match OuterUserId's property */ + SetConfigOption("is_superuser", + is_superuser ? "on" : "off", + PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); } @@ -529,6 +535,12 @@ GetSessionUserId(void) return SessionUserId; } +bool +GetSessionUserIsSuperuser(void) +{ + Assert(OidIsValid(SessionUserId)); + return SessionUserIsSuperuser; +} static void SetSessionUserId(Oid userid, bool is_superuser) @@ -537,15 +549,11 @@ SetSessionUserId(Oid userid, bool is_superuser) AssertArg(OidIsValid(userid)); SessionUserId = userid; SessionUserIsSuperuser = is_superuser; - SetRoleIsActive = false; - - /* We force the effective user IDs to match, too */ - OuterUserId = userid; - CurrentUserId = userid; } /* - * GetAuthenticatedUserId - get the authenticated user ID + * GetAuthenticatedUserId/SetAuthenticatedUserId - get/set the authenticated + * user ID */ Oid GetAuthenticatedUserId(void) @@ -554,6 +562,32 @@ GetAuthenticatedUserId(void) return AuthenticatedUserId; } +/* + * Return whether the authenticated user was superuser at connection start. + */ +bool +GetAuthenticatedUserIsSuperuser(void) +{ + Assert(OidIsValid(AuthenticatedUserId)); + return AuthenticatedUserIsSuperuser; +} + +void +SetAuthenticatedUserId(Oid userid, bool is_superuser) +{ + Assert(OidIsValid(userid)); + + /* call only once */ + Assert(!OidIsValid(AuthenticatedUserId)); + + AuthenticatedUserId = userid; + AuthenticatedUserIsSuperuser = is_superuser; + + /* Also mark our PGPROC entry with the authenticated user id */ + /* (We assume this is an atomic store so no lock is needed) */ + MyProc->roleId = userid; +} + /* * GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID @@ -699,6 +733,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid) HeapTuple roleTup; Form_pg_authid rform; char *rname; + bool is_superuser; /* * Don't do scans if we're bootstrapping, none of the system catalogs @@ -706,9 +741,6 @@ InitializeSessionUserId(const char *rolename, Oid roleid) */ AssertState(!IsBootstrapProcessingMode()); - /* call only once */ - AssertState(!OidIsValid(AuthenticatedUserId)); - /* * Make sure syscache entries are flushed for recent catalog changes. This * allows us to find roles that were created on-the-fly during @@ -716,36 +748,52 @@ InitializeSessionUserId(const char *rolename, Oid roleid) */ AcceptInvalidationMessages(); + /* + * Look up the role, either by name if that's given or by OID if not. + * Normally we have to fail if we don't find it, but in parallel workers + * just return without doing anything: all the critical work has been done + * already. The upshot of that is that if the role has been deleted, we + * will not enforce its rolconnlimit against parallel workers anymore. + */ if (rolename != NULL) { roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(rolename)); if (!HeapTupleIsValid(roleTup)) + { + if (InitializingParallelWorker) + return; ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role \"%s\" does not exist", rolename))); + } } else { roleTup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (!HeapTupleIsValid(roleTup)) + { + if (InitializingParallelWorker) + return; ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role with OID %u does not exist", roleid))); + } } rform = (Form_pg_authid) GETSTRUCT(roleTup); roleid = rform->oid; rname = NameStr(rform->rolname); + is_superuser = rform->rolsuper; - AuthenticatedUserId = roleid; - AuthenticatedUserIsSuperuser = rform->rolsuper; + /* In a parallel worker, ParallelWorkerMain already set these variables */ + if (!InitializingParallelWorker) + { + SetAuthenticatedUserId(roleid, is_superuser); - /* This sets OuterUserId/CurrentUserId too */ - SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); - - /* Also mark our PGPROC entry with the authenticated user id */ - /* (We assume this is an atomic store so no lock is needed) */ - MyProc->roleId = roleid; + /* Set SessionUserId and related variables via the GUC mechanisms */ + SetConfigOption("session_authorization", rname, + PGC_BACKEND, PGC_S_OVERRIDE); + } /* * These next checks are not enforced when in standalone mode, so that @@ -774,7 +822,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid) * just document that the connection limit is approximate. */ if (rform->rolconnlimit >= 0 && - !AuthenticatedUserIsSuperuser && + !is_superuser && CountUserBackends(roleid) > rform->rolconnlimit) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), @@ -782,13 +830,6 @@ InitializeSessionUserId(const char *rolename, Oid roleid) rname))); } - /* Record username and superuser status as GUC settings too */ - SetConfigOption("session_authorization", rname, - PGC_BACKEND, PGC_S_OVERRIDE); - SetConfigOption("is_superuser", - AuthenticatedUserIsSuperuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); - ReleaseSysCache(roleTup); } @@ -811,48 +852,39 @@ InitializeSessionUserIdStandalone(void) AuthenticatedUserId = BOOTSTRAP_SUPERUSERID; AuthenticatedUserIsSuperuser = true; - SetSessionUserId(BOOTSTRAP_SUPERUSERID, true); - /* - * XXX This should set SetConfigOption("session_authorization"), too. - * Since we don't, C code will get NULL, and current_setting() will get an - * empty string. + * XXX Ideally we'd do this via SetConfigOption("session_authorization"), + * but we lack the role name needed to do that, and we can't fetch it + * because one reason for this special case is to be able to start up even + * if something's happened to the BOOTSTRAP_SUPERUSERID's pg_authid row. + * Since we don't set the GUC itself, C code will see the value as NULL, + * and current_setting() will report an empty string within this session. */ - SetConfigOption("is_superuser", "on", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + SetSessionAuthorization(BOOTSTRAP_SUPERUSERID, true); + + /* We could do SetConfigOption("role"), but let's be consistent */ + SetCurrentRoleId(InvalidOid, false); } /* * Change session auth ID while running * - * Only a superuser may set auth ID to something other than himself. Note - * that in case of multiple SETs in a single session, the original userid's - * superuserness is what matters. But we set the GUC variable is_superuser - * to indicate whether the *current* session userid is a superuser. - * - * Note: this is not an especially clean place to do the permission check. - * It's OK because the check does not require catalog access and can't - * fail during an end-of-transaction GUC reversion, but we may someday - * have to push it up into assign_session_authorization. + * The SQL standard says that SET SESSION AUTHORIZATION implies SET ROLE NONE. + * We mechanize that at higher levels not here, because this is the GUC + * assign hook for "session_authorization", and it must be commutative with + * SetCurrentRoleId (the hook for "role") because guc.c provides no guarantees + * which will run first during cases such as transaction rollback. Therefore, + * we update derived state (OuterUserId/CurrentUserId/is_superuser) only if + * !SetRoleIsActive. */ void SetSessionAuthorization(Oid userid, bool is_superuser) { - /* Must have authenticated already, else can't make permission check */ - AssertState(OidIsValid(AuthenticatedUserId)); - - if (userid != AuthenticatedUserId && - !AuthenticatedUserIsSuperuser) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set session authorization"))); - SetSessionUserId(userid, is_superuser); - SetConfigOption("is_superuser", - is_superuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + if (!SetRoleIsActive) + SetOuterUserId(userid, is_superuser); } /* @@ -888,28 +920,25 @@ SetCurrentRoleId(Oid roleid, bool is_superuser) /* * Get correct info if it's SET ROLE NONE * - * If SessionUserId hasn't been set yet, just do nothing --- the eventual - * SetSessionUserId call will fix everything. This is needed since we - * will get called during GUC initialization. + * If SessionUserId hasn't been set yet, do nothing beyond updating + * SetRoleIsActive --- the eventual SetSessionAuthorization call will + * update the derived state. This is needed since we will get called + * during GUC initialization. */ if (!OidIsValid(roleid)) { + SetRoleIsActive = false; + if (!OidIsValid(SessionUserId)) return; roleid = SessionUserId; is_superuser = SessionUserIsSuperuser; - - SetRoleIsActive = false; } else SetRoleIsActive = true; - SetOuterUserId(roleid); - - SetConfigOption("is_superuser", - is_superuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + SetOuterUserId(roleid, is_superuser); } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 835986288a2..79599a2c10f 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -8187,6 +8187,9 @@ set_config_option_ext(const char *name, const char *value, case PGC_STRING: { struct config_string *conf = (struct config_string *) record; + GucContext orig_context = context; + GucSource orig_source = source; + Oid orig_srole = srole; #define newval (newval_union.stringval) @@ -8272,6 +8275,36 @@ set_config_option_ext(const char *name, const char *value, conf->gen.source = source; conf->gen.scontext = context; conf->gen.srole = srole; + + /* + * Ugly hack: during SET session_authorization, forcibly + * do SET ROLE NONE with the same context/source/etc, so + * that the effects will have identical lifespan. This is + * required by the SQL spec, and it's not possible to do + * it within the variable's check hook or assign hook + * because our APIs for those don't pass enough info. + * However, don't do it if is_reload: in that case we + * expect that if "role" isn't supposed to be default, it + * has been or will be set by a separate reload action. + * + * A fine point: for RESET session_authorization, we do + * "RESET role" not "SET ROLE NONE" (by passing down NULL + * rather than "none" for the value). This would have the + * same effects in typical cases, but if the reset value + * of "role" is not "none" it seems better to revert to + * that. + */ + if (!is_reload && + strcmp(conf->gen.name, "session_authorization") == 0) + (void) set_config_option_ext("role", + value ? "none" : NULL, + orig_context, + orig_source, + orig_srole, + action, + true, + elevel, + false); } if (makeDefault) @@ -10927,12 +10960,6 @@ can_skip_gucvar(struct config_generic *gconf) * mechanisms (if indeed they aren't compile-time constants). So we may * always skip these. * - * Role must be handled specially because its current value can be an - * invalid value (for instance, if someone dropped the role since we set - * it). So if we tried to serialize it normally, we might get a failure. - * We skip it here, and use another mechanism to ensure the worker has the - * right value. - * * For all other GUCs, we skip if the GUC has its compiled-in default * value (i.e., source == PGC_S_DEFAULT). On the leader side, this means * we don't send GUCs that have their default values, which typically @@ -10941,8 +10968,8 @@ can_skip_gucvar(struct config_generic *gconf) * comments in RestoreGUCState for more info. */ return gconf->context == PGC_POSTMASTER || - gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT || - strcmp(gconf->name, "role") == 0; + gconf->context == PGC_INTERNAL || + gconf->source == PGC_S_DEFAULT; } /* diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 3233278b340..22fe2c994e1 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -351,7 +351,10 @@ extern char *GetUserNameFromId(Oid roleid, bool noerr); extern Oid GetUserId(void); extern Oid GetOuterUserId(void); extern Oid GetSessionUserId(void); +extern bool GetSessionUserIsSuperuser(void); extern Oid GetAuthenticatedUserId(void); +extern bool GetAuthenticatedUserIsSuperuser(void); +extern void SetAuthenticatedUserId(Oid userid, bool is_superuser); extern void GetUserIdAndSecContext(Oid *userid, int *sec_context); extern void SetUserIdAndSecContext(Oid userid, int sec_context); extern bool InLocalUserIdChange(void); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 8213be3d216..6d58833af23 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -50,6 +50,73 @@ SET SESSION AUTHORIZATION regress_priv_user8; SET ROLE pg_read_all_settings; RESET ROLE; RESET SESSION AUTHORIZATION; +-- test interaction of SET SESSION AUTHORIZATION and SET ROLE, +-- as well as propagation of these settings to parallel workers +GRANT regress_priv_user9 TO regress_priv_user8; +SET SESSION AUTHORIZATION regress_priv_user8; +SET ROLE regress_priv_user9; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +--------------------+--------------------+--------------------+-------------------- + regress_priv_user8 | regress_priv_user9 | regress_priv_user9 | regress_priv_user9 +(1 row) + +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +--------------------+--------------------+--------------------+-------------------- + regress_priv_user8 | regress_priv_user9 | regress_priv_user9 | regress_priv_user9 +(1 row) + +BEGIN; +SET SESSION AUTHORIZATION regress_priv_user10; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +---------------------+---------------------+---------------------+------ + regress_priv_user10 | regress_priv_user10 | regress_priv_user10 | none +(1 row) + +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +---------------------+---------------------+---------------------+------ + regress_priv_user10 | regress_priv_user10 | regress_priv_user10 | none +(1 row) + +ROLLBACK; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +--------------------+--------------------+--------------------+-------------------- + regress_priv_user8 | regress_priv_user9 | regress_priv_user9 | regress_priv_user9 +(1 row) + +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + session_user | current_role | current_user | role +--------------------+--------------------+--------------------+-------------------- + regress_priv_user8 | regress_priv_user9 | regress_priv_user9 | regress_priv_user9 +(1 row) + +RESET SESSION AUTHORIZATION; +-- session_user at this point is installation-dependent +SET force_parallel_mode = 0; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; + c_r_ok | c_u_ok | role +--------+--------+------ + t | t | none +(1 row) + +SET force_parallel_mode = 1; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; + c_r_ok | c_u_ok | role +--------+--------+------ + t | t | none +(1 row) + +RESET force_parallel_mode; REVOKE pg_read_all_settings FROM regress_priv_user8; DROP USER regress_priv_user10; DROP USER regress_priv_user9; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 55b5df584bb..fe657be533e 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -60,6 +60,39 @@ SET ROLE pg_read_all_settings; RESET ROLE; RESET SESSION AUTHORIZATION; + +-- test interaction of SET SESSION AUTHORIZATION and SET ROLE, +-- as well as propagation of these settings to parallel workers +GRANT regress_priv_user9 TO regress_priv_user8; + +SET SESSION AUTHORIZATION regress_priv_user8; +SET ROLE regress_priv_user9; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + +BEGIN; +SET SESSION AUTHORIZATION regress_priv_user10; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; +ROLLBACK; +SET force_parallel_mode = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET force_parallel_mode = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + +RESET SESSION AUTHORIZATION; +-- session_user at this point is installation-dependent +SET force_parallel_mode = 0; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; +SET force_parallel_mode = 1; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; + +RESET force_parallel_mode; + REVOKE pg_read_all_settings FROM regress_priv_user8; DROP USER regress_priv_user10;