Add SET ROLE. This is a partial commit of Stephen Frost's recent patch;
authorTom Lane
Mon, 25 Jul 2005 22:12:34 +0000 (22:12 +0000)
committerTom Lane
Mon, 25 Jul 2005 22:12:34 +0000 (22:12 +0000)
I'm still working on the has_role function and information_schema changes.

17 files changed:
doc/src/sgml/func.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/ref/pg_restore.sgml
doc/src/sgml/ref/set_role.sgml [new file with mode: 0644]
doc/src/sgml/ref/set_session_auth.sgml
doc/src/sgml/reference.sgml
src/backend/access/transam/xact.c
src/backend/commands/user.c
src/backend/commands/variable.c
src/backend/parser/gram.y
src/backend/utils/init/miscinit.c
src/backend/utils/misc/check_guc
src/backend/utils/misc/guc.c
src/include/commands/variable.h
src/include/miscadmin.h

index 59813e16f164c72899631a28c5ad03d7ca7da8db..370873551502e0f41e60e7eb08d09dd22cba0644 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -8266,7 +8266,9 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a);
     with .
     The current_user is the user identifier
     that is applicable for permission checking. Normally, it is equal
-    to the session user, but it changes during the execution of
+    to the session user, but it can be changed with
+    .
+    It also changes during the execution of
     functions with the attribute SECURITY DEFINER.
     In Unix parlance, the session user is the real user and
     the current user is the effective user.
index 33e9e68b9d55b2dc757253fd2cfaac543cac17a9..d993b64ad0486958a44787d5006af3ff00d609f1 100644 (file)
@@ -1,5 +1,5 @@
 
@@ -102,6 +102,7 @@ Complete list of usable sgml source files in this directory.
 
 
 
+
 
 
 
index a6d8bb240784f77492dc0022abfb7f32099e6c71..288ae20a411ed05e3604d3aaa851bc4df323a84f 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -474,8 +474,8 @@ PostgreSQL documentation
       
       
        
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        
index c61ae09404224a57f1e9f6c4005080e505a3c48e..4cee1a4ed7249a00090f871a8827ba88b290ce8d 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -277,8 +277,8 @@ PostgreSQL documentation
       
       
        
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        
index 9b2b5fc3f26ef1c5a7ca47126a8a043a8d172605..d4a1a3e0f0594fc08a5b9ae612e01f98ef92694a 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  
       
       
        
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        
diff --git a/doc/src/sgml/ref/set_role.sgml b/doc/src/sgml/ref/set_role.sgml
new file mode 100644 (file)
index 0000000..6fbe40f
--- /dev/null
@@ -0,0 +1,116 @@
+
+
+  SET ROLE
+  SQL - Language Statements
+
+  SET ROLE
+  set the current user identifier of the current session
+
+  SET ROLE
+
+
+SET [ SESSION | LOCAL ] ROLE rolename
+SET [ SESSION | LOCAL ] ROLE NONE
+RESET ROLE
+
+
+  Description
+
+  
+   This command sets the current user
+   identifier of the current SQL-session context to be 
+   class="parameter">rolename.  The role name may be
+   written as either an identifier or a string literal.  Using this
+   command, it is possible to either add privileges or restrict one's
+   privileges.
+  
+
+  
+   The specified rolename
+   must be a role that the current session user is a member of.
+   (If the session user is a superuser, any role can be selected.)
+  
+
+  
+   The SESSION and LOCAL modifiers act the same
+   as for the regular 
+   command.
+  
+
+  
+   The NONE and RESET forms reset the current
+   user identifier to be the current session user identifier.
+   These forms may be executed by any user.
+  
+
+  Examples
+
+
+SELECT SESSION_USER, CURRENT_USER;
+
+ session_user | current_user 
+--------------+--------------
+ peter        | peter
+
+SET ROLE 'paul';
+
+SELECT SESSION_USER, CURRENT_USER;
+
+ session_user | current_user 
+--------------+--------------
+ peter        | paul
+
+
+  Compatibility
+
+  
+   PostgreSQL
+   allows identifier syntax ("rolename"), while
+   the SQL standard requires the role name to be written as a string
+   literal.  SQL does not allow this command during a transaction;
+   PostgreSQL does not make this
+   restriction because there is no reason to.
+   The SESSION and LOCAL modifiers are a
+   PostgreSQL extension, as is the
+   RESET syntax.
+  
+
+  See Also
+
+  
+   
+  
+
+
+
index 7014b8d2ab357cc063d442743df83d5ea2aab94f..334847fb00e99cdcec53ba86745407b15434c9a3 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   SET SESSION AUTHORIZATION
@@ -31,7 +31,7 @@ RESET SESSION AUTHORIZATION
    class="parameter">username.  The user name may be
    written as either an identifier or a string literal.  Using this
    command, it is possible, for example, to temporarily become an
-   unprivileged user and later switch back to become a superuser.
+   unprivileged user and later switch back to being a superuser.
   
 
   
@@ -39,8 +39,9 @@ RESET SESSION AUTHORIZATION
    authenticated) user name provided by the client.  The current user
    identifier is normally equal to the session user identifier, but
    may change temporarily in the context of setuid
-   functions and similar mechanisms.  The current user identifier is
-   relevant for permission checking.
+   functions and similar mechanisms; it can also be changed by
+   .
+   The current user identifier is relevant for permission checking.
   
 
   
@@ -93,10 +94,24 @@ SELECT SESSION_USER, CURRENT_USER;
    allows identifier syntax ("username"), which SQL
    does not.  SQL does not allow this command during a transaction;
    PostgreSQL does not make this
-   restriction because there is no reason to.  The privileges
-   necessary to execute this command are left implementation-defined
-   by the standard.
+   restriction because there is no reason to.
+   The SESSION and LOCAL modifiers are a
+   PostgreSQL extension, as is the
+   RESET syntax.
   
+
+  
+   The privileges necessary to execute this command are left
+   implementation-defined by the standard.
+  
+
+  See Also
+
+  
+   
+  
  
 
 
index 4edec85c1225d17abcd7fd3859ebc07c42a8b71a..63ecfe12041f5aeffab9754d18d7d8029eefac8c 100644 (file)
@@ -1,5 +1,5 @@
 
@@ -134,6 +134,7 @@ PostgreSQL Reference Manual
    &selectInto;
    &set;
    &setConstraints;
+   &setRole;
    &setSessionAuth;
    &setTransaction;
    &show;
index c75da3d432c42177f61a9c8c8b4bf0351b57ea99..ee33030292f771843c874ff53946c34044dc15b9 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.210 2005/07/13 22:46:09 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.211 2005/07/25 22:12:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1865,7 +1865,7 @@ AbortTransaction(void)
 
    /*
     * Reset user id which might have been changed transiently.  We cannot
-    * use s->currentUser, but must get the session userid from
+    * use s->currentUser, but must get the session outer-level userid from
     * miscinit.c.
     *
     * (Note: it is not necessary to restore session authorization here
@@ -1874,7 +1874,7 @@ AbortTransaction(void)
     * DEFINER function could send control here with the wrong current
     * userid.)
     */
-   SetUserId(GetSessionUserId());
+   SetUserId(GetOuterUserId());
 
    /*
     * do abort processing
index 4a46343d5d873d001decfad52a938b41089d1602..5f8eeae30df436b34395b8db202db813cade540f 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.156 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.157 2005/07/25 22:12:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -227,7 +227,8 @@ CreateRole(CreateRoleStmt *stmt)
                     errmsg("permission denied to create role")));
    }
 
-   if (strcmp(stmt->role, "public") == 0)
+   if (strcmp(stmt->role, "public") == 0 ||
+       strcmp(stmt->role, "none") == 0)
        ereport(ERROR,
                (errcode(ERRCODE_RESERVED_NAME),
                 errmsg("role name \"%s\" is reserved",
@@ -760,11 +761,15 @@ DropRole(DropRoleStmt *stmt)
        if (roleid == GetUserId())
            ereport(ERROR,
                    (errcode(ERRCODE_OBJECT_IN_USE),
-                    errmsg("current role cannot be dropped")));
+                    errmsg("current user cannot be dropped")));
+       if (roleid == GetOuterUserId())
+           ereport(ERROR,
+                   (errcode(ERRCODE_OBJECT_IN_USE),
+                    errmsg("current user cannot be dropped")));
        if (roleid == GetSessionUserId())
            ereport(ERROR,
                    (errcode(ERRCODE_OBJECT_IN_USE),
-                    errmsg("session role cannot be dropped")));
+                    errmsg("session user cannot be dropped")));
 
        /*
         * For safety's sake, we allow createrole holders to drop ordinary
@@ -893,7 +898,8 @@ RenameRole(const char *oldname, const char *newname)
     * XXX Client applications probably store the session user somewhere,
     * so renaming it could cause confusion.  On the other hand, there may
     * not be an actual problem besides a little confusion, so think about
-    * this and decide.
+    * this and decide.  Same for SET ROLE ... we don't restrict renaming
+    * the current effective userid, though.
     */
 
    roleid = HeapTupleGetOid(oldtuple);
@@ -901,7 +907,11 @@ RenameRole(const char *oldname, const char *newname)
    if (roleid == GetSessionUserId())
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("session role may not be renamed")));
+                errmsg("session user may not be renamed")));
+   if (roleid == GetOuterUserId())
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("current user may not be renamed")));
 
    /* make sure the new name doesn't exist */
    if (SearchSysCacheExists(AUTHNAME,
@@ -911,6 +921,13 @@ RenameRole(const char *oldname, const char *newname)
                (errcode(ERRCODE_DUPLICATE_OBJECT),
                 errmsg("role \"%s\" already exists", newname)));
 
+   if (strcmp(newname, "public") == 0 ||
+       strcmp(newname, "none") == 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_RESERVED_NAME),
+                errmsg("role name \"%s\" is reserved",
+                       newname)));
+
    /*
     * createrole is enough privilege unless you want to mess with a superuser
     */
index 494ab6b491e10bebfa9cdd1531b4643644f492fe..9254d57e345bcaf99727be68cd3cc4106e420865 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.111 2005/07/21 03:56:10 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.112 2005/07/25 22:12:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "miscadmin.h"
 #include "parser/scansup.h"
 #include "pgtime.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/syscache.h"
@@ -684,3 +685,142 @@ show_session_authorization(void)
 
    return endptr + 1;
 }
+
+
+/*
+ * SET ROLE
+ *
+ * When resetting session auth after an error, we can't expect to do catalog
+ * lookups.  Hence, the stored form of the value must provide a numeric oid
+ * that can be re-used directly.  We implement this exactly like SET
+ * SESSION AUTHORIZATION.
+ *
+ * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
+ * a translation of "none" to InvalidOid.
+ */
+extern char *role_string;      /* in guc.c */
+
+const char *
+assign_role(const char *value, bool doit, GucSource source)
+{
+   Oid     roleid = InvalidOid;
+   bool        is_superuser = false;
+   const char *actual_rolename = value;
+   char       *result;
+
+   if (strspn(value, "x") == NAMEDATALEN &&
+       (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
+   {
+       /* might be a saved userid string */
+       Oid     savedoid;
+       char       *endptr;
+
+       savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
+
+       if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
+       {
+           /* syntactically valid, so break out the data */
+           roleid = savedoid;
+           is_superuser = (value[NAMEDATALEN] == 'T');
+           actual_rolename = endptr + 1;
+       }
+   }
+
+   if (roleid == InvalidOid &&
+       strcmp(actual_rolename, "none") != 0)
+   {
+       /* not a saved ID, so look it up */
+       HeapTuple   roleTup;
+
+       if (!IsTransactionState())
+       {
+           /*
+            * Can't do catalog lookups, so fail.  The upshot of this is
+            * that role cannot be set in postgresql.conf, which seems 
+            * like a good thing anyway.
+            */
+           return NULL;
+       }
+
+       roleTup = SearchSysCache(AUTHNAME,
+                                PointerGetDatum(value),
+                                0, 0, 0);
+       if (!HeapTupleIsValid(roleTup))
+       {
+           if (source >= PGC_S_INTERACTIVE)
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("role \"%s\" does not exist", value)));
+           return NULL;
+       }
+
+       roleid = HeapTupleGetOid(roleTup);
+       is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+
+       ReleaseSysCache(roleTup);
+
+       /*
+        * Verify that session user is allowed to become this role
+        */
+       if (!is_member_of_role(GetSessionUserId(), roleid))
+       {
+           if (source >= PGC_S_INTERACTIVE)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                        errmsg("permission denied to set role \"%s\"",
+                               value)));
+           return NULL;
+       }
+   }
+
+   if (doit)
+       SetCurrentRoleId(roleid, is_superuser);
+
+   result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
+   if (!result)
+       return NULL;
+
+   memset(result, 'x', NAMEDATALEN);
+
+   sprintf(result + NAMEDATALEN, "%c%u,%s",
+           is_superuser ? 'T' : 'F',
+           roleid,
+           actual_rolename);
+
+   return result;
+}
+
+const char *
+show_role(void)
+{
+   /*
+    * Extract the role name from the stored string; see
+    * assign_role
+    */
+   const char *value = role_string;
+   Oid     savedoid;
+   char       *endptr;
+
+   /* This special case only applies if no SET ROLE has been done */
+   if (value == NULL || strcmp(value, "none") == 0)
+       return "none";
+
+   Assert(strspn(value, "x") == NAMEDATALEN &&
+          (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
+
+   savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
+
+   Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
+
+   /*
+    * Check that the stored string still matches the effective setting,
+    * else return "none".  This is a kluge to deal with the fact that
+    * SET SESSION AUTHORIZATION logically resets SET ROLE to NONE, but
+    * we cannot set the GUC role variable from assign_session_authorization
+    * (because we haven't got enough info to call set_config_option).
+    */
+   if (savedoid != GetCurrentRoleId())
+       return "none";
+
+   return endptr + 1;
+}
index 8afc948a07a88ccf049837124ef7cb4246f7784a..3730068915f16b03530cbe733a997fb0b53fc0e0 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.501 2005/06/29 20:34:13 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.502 2005/07/25 22:12:32 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1004,6 +1004,13 @@ set_rest:  var_name TO var_list_or_default
                        n->args = list_make1(makeStringConst($2, NULL));
                    $$ = n;
                }
+           | ROLE ColId_or_Sconst
+               {
+                   VariableSetStmt *n = makeNode(VariableSetStmt);
+                   n->name = "role";
+                   n->args = list_make1(makeStringConst($2, NULL));
+                   $$ = n;
+               }
            | SESSION AUTHORIZATION ColId_or_Sconst
                {
                    VariableSetStmt *n = makeNode(VariableSetStmt);
index 389ad06f2fe8a8560ddea4cd2de85fc9a395e525..66d6d1725e082c810e27ec8bb8b4ba5e101ef69e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.146 2005/07/14 05:13:41 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.147 2005/07/25 22:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,24 +270,44 @@ make_absolute_path(const char *path)
 
 
 /* ----------------------------------------------------------------
- * Role ID things
+ * User ID state
  *
- * The authenticated user is determined at connection start and never
- * changes.  The session user can be changed only by SET SESSION
- * AUTHORIZATION.  The current user may change when "setuid" functions
- * are implemented.  Conceptually there is a stack, whose bottom
- * is the session user.  You are yourself responsible to save and
- * restore the current user id if you need to change it.
+ * We have to track several different values associated with the concept
+ * of "user ID".
+ *
+ * AuthenticatedUserId is determined at connection start and never changes.
+ *
+ * SessionUserId is initially the same as AuthenticatedUserId, but can be
+ * changed by SET SESSION AUTHORIZATION (if AuthenticatedUserIsSuperuser).
+ * This is the ID reported by the SESSION_USER SQL function.
+ *
+ * OuterUserId is the current user ID in effect at the "outer level" (outside
+ * any transaction or function).  This is initially the same as SessionUserId,
+ * but can be changed by SET ROLE to any role that SessionUserId is a
+ * member of.  We store this mainly so that AbortTransaction knows what to
+ * reset CurrentUserId to.
+ *
+ * CurrentUserId is the current effective user ID; this is the one to use
+ * for all normal permissions-checking purposes.  At outer level this will
+ * be the same as OuterUserId, but it changes during calls to SECURITY
+ * DEFINER functions, as well as locally in some specialized commands.
  * ----------------------------------------------------------------
  */
 static Oid AuthenticatedUserId = InvalidOid;
 static Oid SessionUserId = InvalidOid;
+static Oid OuterUserId = InvalidOid;
 static Oid CurrentUserId = InvalidOid;
 
+/* We also have to remember the superuser state of some of these levels */
 static bool AuthenticatedUserIsSuperuser = false;
+static bool SessionUserIsSuperuser = false;
+
+/* We also remember if a SET ROLE is currently active */
+static bool SetRoleIsActive = false;
+
 
 /*
- * This function is relevant for all privilege checks.
+ * GetUserId/SetUserId - get/set the current effective user ID.
  */
 Oid
 GetUserId(void)
@@ -298,15 +318,37 @@ GetUserId(void)
 
 
 void
-SetUserId(Oid roleid)
+SetUserId(Oid userid)
 {
-   AssertArg(OidIsValid(roleid));
-   CurrentUserId = roleid;
+   AssertArg(OidIsValid(userid));
+   CurrentUserId = userid;
 }
 
 
 /*
- * This value is only relevant for informational purposes.
+ * GetOuterUserId/SetOuterUserId - get/set the outer-level user ID.
+ */
+Oid
+GetOuterUserId(void)
+{
+   AssertState(OidIsValid(OuterUserId));
+   return OuterUserId;
+}
+
+
+static void
+SetOuterUserId(Oid userid)
+{
+   AssertArg(OidIsValid(userid));
+   OuterUserId = userid;
+
+   /* We force the effective user ID to match, too */
+   CurrentUserId = userid;
+}
+
+
+/*
+ * GetSessionUserId/SetSessionUserId - get/set the session user ID.
  */
 Oid
 GetSessionUserId(void)
@@ -316,17 +358,23 @@ GetSessionUserId(void)
 }
 
 
-void
-SetSessionUserId(Oid roleid)
+static void
+SetSessionUserId(Oid userid, bool is_superuser)
 {
-   AssertArg(OidIsValid(roleid));
-   SessionUserId = roleid;
-   /* Current user defaults to session user. */
-   if (!OidIsValid(CurrentUserId))
-       CurrentUserId = roleid;
+   AssertArg(OidIsValid(userid));
+   SessionUserId = userid;
+   SessionUserIsSuperuser = is_superuser;
+   SetRoleIsActive = false;
+
+   /* We force the effective user IDs to match, too */
+   OuterUserId = userid;
+   CurrentUserId = userid;
 }
 
 
+/*
+ * Initialize user identity during normal backend startup
+ */
 void
 InitializeSessionUserId(const char *rolename)
 {
@@ -364,7 +412,8 @@ InitializeSessionUserId(const char *rolename)
    AuthenticatedUserId = roleid;
    AuthenticatedUserIsSuperuser = rform->rolsuper;
 
-   SetSessionUserId(roleid);   /* sets CurrentUserId too */
+   /* This sets OuterUserId/CurrentUserId too */
+   SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
 
    /* Record username and superuser status as GUC settings too */
    SetConfigOption("session_authorization", rolename,
@@ -391,6 +440,9 @@ InitializeSessionUserId(const char *rolename)
 }
 
 
+/*
+ * Initialize user identity during special backend startup
+ */
 void
 InitializeSessionUserIdStandalone(void)
 {
@@ -403,7 +455,7 @@ InitializeSessionUserIdStandalone(void)
    AuthenticatedUserId = BOOTSTRAP_SUPERUSERID;
    AuthenticatedUserIsSuperuser = true;
 
-   SetSessionUserId(BOOTSTRAP_SUPERUSERID);
+   SetSessionUserId(BOOTSTRAP_SUPERUSERID, true);
 }
 
 
@@ -414,21 +466,82 @@ InitializeSessionUserIdStandalone(void)
  * 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.
  */
 void
-SetSessionAuthorization(Oid roleid, bool is_superuser)
+SetSessionAuthorization(Oid userid, bool is_superuser)
 {
    /* Must have authenticated already, else can't make permission check */
    AssertState(OidIsValid(AuthenticatedUserId));
 
-   if (roleid != AuthenticatedUserId &&
+   if (userid != AuthenticatedUserId &&
        !AuthenticatedUserIsSuperuser)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
              errmsg("permission denied to set session authorization")));
 
-   SetSessionUserId(roleid);
-   SetUserId(roleid);
+   SetSessionUserId(userid, is_superuser);
+
+   SetConfigOption("is_superuser",
+                   is_superuser ? "on" : "off",
+                   PGC_INTERNAL, PGC_S_OVERRIDE);
+}
+
+/*
+ * Report current role id
+ *     This follows the semantics of SET ROLE, ie return the outer-level ID
+ *     not the current effective ID, and return InvalidOid when the setting
+ *     is logically SET ROLE NONE.
+ */
+Oid
+GetCurrentRoleId(void)
+{
+   if (SetRoleIsActive)
+       return OuterUserId;
+   else
+       return InvalidOid;
+}
+
+/*
+ * Change Role ID while running (SET ROLE)
+ *
+ * If roleid is InvalidOid, we are doing SET ROLE NONE: revert to the
+ * session user authorization.  In this case the is_superuser argument
+ * is ignored.
+ *
+ * When roleid is not InvalidOid, the caller must have checked whether
+ * the session user has permission to become that role.  (We cannot check
+ * here because this routine must be able to execute in a failed transaction
+ * to restore a prior value of the ROLE GUC variable.)
+ */
+void
+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 (!OidIsValid(roleid))
+   {
+       if (!OidIsValid(SessionUserId))
+           return;
+
+       roleid = SessionUserId;
+       is_superuser = SessionUserIsSuperuser;
+
+       SetRoleIsActive = false;
+   }
+   else
+       SetRoleIsActive = true;
+
+   SetOuterUserId(roleid);
 
    SetConfigOption("is_superuser",
                    is_superuser ? "on" : "off",
index 5b545d5f4368586191e588951b2bd49197f384ca..3332e636427bc8479ee206393538d9d0fe1404b1 100755 (executable)
@@ -18,7 +18,7 @@
 ## can be ignored
 INTENTIONALLY_NOT_INCLUDED="autocommit debug_deadlocks exit_on_error \
 is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \
-pre_auth_delay seed server_encoding server_version session_authorization \
+pre_auth_delay role seed server_encoding server_version session_authorization \
 trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks trace_notify \
 trace_userlocks transaction_isolation transaction_read_only \
 zero_damaged_pages"
index 6400ef566b2b9f3a865b93980e3951cf6a763775..726a093d0d7427b89da4fd73a49dbe576838deaa 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut .
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.277 2005/07/23 21:05:47 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.278 2005/07/25 22:12:33 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -195,7 +195,8 @@ static int  block_size;
 static bool    integer_datetimes;
 static bool    standard_compliant_strings;
 
-/* should be static, but commands/variable.c needs to get at it */
+/* should be static, but commands/variable.c needs to get at these */
+char      *role_string;
 char      *session_authorization_string;
 
 
@@ -1828,6 +1829,17 @@ static struct config_string ConfigureNamesString[] =
        PG_VERSION, NULL, NULL
    },
 
+   {
+       /* Not for general use --- used by SET ROLE */
+       {"role", PGC_USERSET, UNGROUPED,
+           gettext_noop("Sets the current role."),
+           NULL,
+           GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+       },
+       &role_string,
+       "none", assign_role, show_role
+   },
+
    {
        /* Not for general use --- used by SET SESSION AUTHORIZATION */
        {"session_authorization", PGC_USERSET, UNGROUPED,
@@ -2048,8 +2060,6 @@ static bool guc_dirty;            /* TRUE if need to do commit/abort work */
 
 static bool reporting_enabled; /* TRUE to enable GUC_REPORT */
 
-static char *guc_string_workspace;     /* for avoiding memory leaks */
-
 
 static int guc_var_compare(const void *a, const void *b);
 static int guc_name_compare(const char *namea, const char *nameb);
@@ -2576,8 +2586,6 @@ InitializeGUCOptions(void)
 
    reporting_enabled = false;
 
-   guc_string_workspace = NULL;
-
    /*
     * Prevent any attempt to override the transaction modes from
     * non-interactive sources.
@@ -2976,13 +2984,6 @@ AtEOXact_GUC(bool isCommit, bool isSubXact)
    if (!guc_dirty)
        return;
 
-   /* Prevent memory leak if ereport during an assign_hook */
-   if (guc_string_workspace)
-   {
-       free(guc_string_workspace);
-       guc_string_workspace = NULL;
-   }
-
    my_level = GetCurrentTransactionNestLevel();
    Assert(isSubXact ? (my_level > 1) : (my_level == 1));
 
@@ -3389,6 +3390,33 @@ parse_real(const char *value, double *result)
 }
 
 
+/*
+ * Call a GucStringAssignHook function, being careful to free the
+ * "newval" string if the hook ereports.
+ *
+ * This is split out of set_config_option just to avoid the "volatile"
+ * qualifiers that would otherwise have to be plastered all over.
+ */
+static const char *
+call_string_assign_hook(GucStringAssignHook assign_hook,
+                       char *newval, bool doit, GucSource source)
+{
+   const char *result;
+
+   PG_TRY();
+   {
+       result = (*assign_hook) (newval, doit, source);
+   }
+   PG_CATCH();
+   {
+       free(newval);
+       PG_RE_THROW();
+   }
+   PG_END_TRY();
+
+   return result;
+}
+
 
 /*
  * Sets option `name' to given value. The value should be a string
@@ -3833,21 +3861,18 @@ set_config_option(const char *name, const char *value,
                    break;
                }
 
-               /*
-                * Remember string in workspace, so that we can free it
-                * and avoid a permanent memory leak if hook ereports.
-                */
-               if (guc_string_workspace)
-                   free(guc_string_workspace);
-               guc_string_workspace = newval;
-
                if (conf->assign_hook)
                {
                    const char *hookresult;
 
-                   hookresult = (*conf->assign_hook) (newval,
-                                                      changeVal, source);
-                   guc_string_workspace = NULL;
+                   /*
+                    * If the hook ereports, we have to make sure we free
+                    * newval, else it will be a permanent memory leak.
+                    */
+                   hookresult = call_string_assign_hook(conf->assign_hook,
+                                                        newval,
+                                                        changeVal,
+                                                        source);
                    if (hookresult == NULL)
                    {
                        free(newval);
@@ -3874,8 +3899,6 @@ set_config_option(const char *name, const char *value,
                    }
                }
 
-               guc_string_workspace = NULL;
-
                if (changeVal || makeDefault)
                {
                    /* Save old value to support transaction abort */
@@ -4305,8 +4328,7 @@ init_custom_variable(struct config_generic * gen,
 }
 
 void
-DefineCustomBoolVariable(
-                        const char *name,
+DefineCustomBoolVariable(const char *name,
                         const char *short_desc,
                         const char *long_desc,
                         bool *valueAddr,
@@ -4328,8 +4350,7 @@ DefineCustomBoolVariable(
 }
 
 void
-DefineCustomIntVariable(
-                       const char *name,
+DefineCustomIntVariable(const char *name,
                        const char *short_desc,
                        const char *long_desc,
                        int *valueAddr,
@@ -4355,8 +4376,7 @@ DefineCustomIntVariable(
 }
 
 void
-DefineCustomRealVariable(
-                        const char *name,
+DefineCustomRealVariable(const char *name,
                         const char *short_desc,
                         const char *long_desc,
                         double *valueAddr,
@@ -4382,8 +4402,7 @@ DefineCustomRealVariable(
 }
 
 void
-DefineCustomStringVariable(
-                          const char *name,
+DefineCustomStringVariable(const char *name,
                           const char *short_desc,
                           const char *long_desc,
                           char **valueAddr,
index f3d4dc681c82727a808e978b51768c09cf1adb91..9814336325dfe1e234c1ea2f429692e773e02b6f 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.25 2004/12/31 22:03:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.26 2005/07/25 22:12:34 tgl Exp $
  */
 #ifndef VARIABLE_H
 #define VARIABLE_H
@@ -26,6 +26,9 @@ extern bool assign_random_seed(double value,
 extern const char *show_random_seed(void);
 extern const char *assign_client_encoding(const char *value,
                       bool doit, GucSource source);
+extern const char *assign_role(const char *value,
+                            bool doit, GucSource source);
+extern const char *show_role(void);
 extern const char *assign_session_authorization(const char *value,
                             bool doit, GucSource source);
 extern const char *show_session_authorization(void);
index 1ee085e51a00343ee55f4b28394dd2971ac98152..5697a691e63a2636114d616cda7cec9bb3d80b4f 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.177 2005/07/04 04:51:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.178 2005/07/25 22:12:34 tgl Exp $
  *
  * NOTES
  *   some of the information in this file should be moved to other files.
@@ -230,12 +230,14 @@ extern void SetDatabasePath(const char *path);
 
 extern char *GetUserNameFromId(Oid roleid);
 extern Oid GetUserId(void);
-extern void SetUserId(Oid roleid);
+extern void SetUserId(Oid userid);
+extern Oid GetOuterUserId(void);
 extern Oid GetSessionUserId(void);
-extern void SetSessionUserId(Oid roleid);
 extern void InitializeSessionUserId(const char *rolename);
 extern void InitializeSessionUserIdStandalone(void);
-extern void SetSessionAuthorization(Oid roleid, bool is_superuser);
+extern void SetSessionAuthorization(Oid userid, bool is_superuser);
+extern Oid GetCurrentRoleId(void);
+extern void SetCurrentRoleId(Oid roleid, bool is_superuser);
 
 extern void SetDataDir(const char *dir);
 extern void ChangeToDataDir(void);