Reserve the "pg_" namespace for roles
authorStephen Frost <sfrost@snowman.net>
Fri, 8 Apr 2016 20:56:27 +0000 (16:56 -0400)
committerStephen Frost <sfrost@snowman.net>
Fri, 8 Apr 2016 20:56:27 +0000 (16:56 -0400)
This will prevent users from creating roles which begin with "pg_" and
will check for those roles before allowing an upgrade using pg_upgrade.

This will allow for default roles to be provided at initdb time.

Reviews by José Luis Tallón and Robert Haas

21 files changed:
doc/src/sgml/ref/psql-ref.sgml
src/backend/catalog/aclchk.c
src/backend/catalog/catalog.c
src/backend/commands/alter.c
src/backend/commands/foreigncmds.c
src/backend/commands/policy.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/tablespace.c
src/backend/commands/user.c
src/backend/commands/variable.c
src/backend/utils/adt/acl.c
src/bin/pg_dump/pg_dumpall.c
src/bin/pg_upgrade/check.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/include/utils/acl.h
src/test/regress/expected/rolenames.out
src/test/regress/sql/rolenames.sql

index d8b9a03ee0ee9f1d32e0bdbf7b9031f93661c469..60f882231bc0302e45dab22b218227c814acdaf2 100644 (file)
@@ -1365,13 +1365,15 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\dg[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dg[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\du</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\dg+</literal> is used, additional information
@@ -1525,13 +1527,15 @@ testdb=&gt;
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\du[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
         Lists database roles.
         (Since the concepts of <quote>users</> and <quote>groups</> have been
         unified into <quote>roles</>, this command is now equivalent to
         <literal>\dg</literal>.)
+        By default, only user-created roles are shown; supply the
+        <literal>S</literal> modifier to include system roles.
         If <replaceable class="parameter">pattern</replaceable> is specified,
         only those roles whose names match the pattern are listed.
         If the form <literal>\du+</literal> is used, additional information
index 975fe13fcf96fdddb209dd452519b1b4312802b8..4b49bb67ba06cc71a93bab00048f710773370422 100644 (file)
@@ -423,6 +423,9 @@ ExecuteGrantStmt(GrantStmt *stmt)
                                grantee_uid = ACL_ID_PUBLIC;
                                break;
                        default:
+                               if (!IsBootstrapProcessingMode())
+                                       check_rolespec_name((Node *) grantee,
+                       "Cannot GRANT or REVOKE privileges to or from a reserved role.");
                                grantee_uid = get_rolespec_oid((Node *) grantee, false);
                                break;
                }
@@ -918,6 +921,8 @@ ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt)
                                grantee_uid = ACL_ID_PUBLIC;
                                break;
                        default:
+                               check_rolespec_name((Node *) grantee,
+       "Cannot GRANT or REVOKE default privileges to or from a reserved role.");
                                grantee_uid = get_rolespec_oid((Node *) grantee, false);
                                break;
                }
@@ -1008,6 +1013,8 @@ ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt)
                {
                        RoleSpec   *rolespec = lfirst(rolecell);
 
+                       check_rolespec_name((Node *) rolespec,
+                                               "Cannot alter default privileges for reserved role.");
                        iacls.roleid = get_rolespec_oid((Node *) rolespec, false);
 
                        /*
index bead2c1e22250f6b2b03bd603c3c5c51a035e988..d1cf45bef47b1b285a445bb09194edbdbbd14194 100644 (file)
@@ -184,8 +184,9 @@ IsToastNamespace(Oid namespaceId)
  *             True iff name starts with the pg_ prefix.
  *
  *             For some classes of objects, the prefix pg_ is reserved for
- *             system objects only.  As of 8.0, this is only true for
- *             schema and tablespace names.
+ *             system objects only.  As of 8.0, this was only true for
+ *             schema and tablespace names.  With 9.6, this is also true
+ *             for roles.
  */
 bool
 IsReservedName(const char *name)
index 7e39422ecd66153c40e92e10481146e861f1a171..27b757959280f0f6711f43067ea080659e5b6ee6 100644 (file)
@@ -747,6 +747,9 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
 {
        Oid                     newowner = get_rolespec_oid(stmt->newowner, false);
 
+       check_rolespec_name(stmt->newowner,
+                                               "Cannot make reserved roles owners of objects.");
+
        switch (stmt->objectType)
        {
                case OBJECT_DATABASE:
index 804bab2e1f5cadd63cb737ebd551eee021dd128a..88cefb7f958477cba5149b4e220306423c3215ee 100644 (file)
@@ -1148,6 +1148,10 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
        else
                useId = get_rolespec_oid(stmt->user, false);
 
+       /* Additional check to protect reserved role names */
+       check_rolespec_name(stmt->user,
+                                               "Cannot specify reserved role as mapping user.");
+
        /* Check that the server exists. */
        srv = GetForeignServerByName(stmt->servername, false);
 
@@ -1248,6 +1252,10 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
        else
                useId = get_rolespec_oid(stmt->user, false);
 
+       /* Additional check to protect reserved role names */
+       check_rolespec_name(stmt->user,
+                                               "Cannot alter reserved role mapping user.");
+
        srv = GetForeignServerByName(stmt->servername, false);
 
        umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
@@ -1337,6 +1345,11 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
        else
        {
                useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
+
+               /* Additional check to protect reserved role names */
+               check_rolespec_name(stmt->user,
+                                                       "Cannot remove reserved role mapping user.");
+
                if (!OidIsValid(useId))
                {
                        /*
index 93d15e477afddce51b57848af7cc68fc172e7caf..146b36c2fa577e972854382c9ed361a42f7f24e0 100644 (file)
@@ -176,8 +176,13 @@ policy_role_list_to_array(List *roles, int *num_roles)
                        return role_oids;
                }
                else
+               {
+                       /* Additional check to protect reserved role names */
+                       check_rolespec_name((Node *) spec,
+                                                       "Cannot specify reserved role as policy target");
                        role_oids[i++] =
                                ObjectIdGetDatum(get_rolespec_oid((Node *) spec, false));
+               }
        }
 
        return role_oids;
index a60ceb8eba7cb710a5503d2c51259b4b2942298e..dea3299ced5171df46bf956558e7449e9e3d249b 100644 (file)
@@ -65,6 +65,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
        else
                owner_uid = saved_uid;
 
+       /* Additional check to protect reserved role names */
+       check_rolespec_name(stmt->authrole,
+                                               "Cannot specify reserved role as owner.");
+
        /* fill schema name with the user name if not specified */
        if (!schemaName)
        {
index 96dc923bcdf493b3eca87218801bd411df54d872..eaf76d2b902b99cc91254c54c9e06f163b2d59b9 100644 (file)
@@ -3566,6 +3566,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                                                                                (List *) cmd->def, lockmode);
                        break;
                case AT_ChangeOwner:    /* ALTER OWNER */
+                       check_rolespec_name(cmd->newowner,
+                                                               "Cannot specify reserved role as owner.");
                        ATExecChangeOwner(RelationGetRelid(rel),
                                                          get_rolespec_oid(cmd->newowner, false),
                                                          false, lockmode);
index 7902d433d552e4b627f975f74167b90c19d95d1d..fe7f25337dc0e5863831271eb70a0666b66042fb 100644 (file)
@@ -256,6 +256,10 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
        else
                ownerId = GetUserId();
 
+       /* Additional check to protect reserved role names */
+       check_rolespec_name(stmt->owner,
+                                               "Cannot specify reserved role as owner.");
+
        /* Unix-ify the offered path, and strip any trailing slashes */
        location = pstrdup(stmt->location);
        canonicalize_path(location);
index 4baeaa2676ea10fcb0a537b40c53e817b21aafa6..cc3d5645343e6af0b3a3dfbb7f11b8868cd592dc 100644 (file)
@@ -17,6 +17,7 @@
 #include "access/htup_details.h"
 #include "access/xact.h"
 #include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
@@ -311,6 +312,17 @@ CreateRole(CreateRoleStmt *stmt)
                                         errmsg("permission denied to create role")));
        }
 
+       /*
+        * Check that the user is not trying to create a role in the reserved
+        * "pg_" namespace.
+        */
+       if (IsReservedName(stmt->role))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        stmt->role),
+                                errdetail("Role names starting with \"pg_\" are reserved.")));
+
        /*
         * Check the pg_authid relation to be certain the role doesn't already
         * exist.
@@ -507,6 +519,9 @@ AlterRole(AlterRoleStmt *stmt)
        DefElem    *dbypassRLS = NULL;
        Oid                     roleid;
 
+       check_rolespec_name(stmt->role,
+                                               "Cannot alter reserved roles.");
+
        /* Extract options from the statement node tree */
        foreach(option, stmt->options)
        {
@@ -857,6 +872,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
 
        if (stmt->role)
        {
+               check_rolespec_name(stmt->role,
+                                                       "Cannot alter reserved roles.");
+
                roletuple = get_rolespec_tuple(stmt->role);
                roleid = HeapTupleGetOid(roletuple);
 
@@ -1117,6 +1135,7 @@ RenameRole(const char *oldname, const char *newname)
        int                     i;
        Oid                     roleid;
        ObjectAddress address;
+       Form_pg_authid authform;
 
        rel = heap_open(AuthIdRelationId, RowExclusiveLock);
        dsc = RelationGetDescr(rel);
@@ -1136,6 +1155,7 @@ RenameRole(const char *oldname, const char *newname)
         */
 
        roleid = HeapTupleGetOid(oldtuple);
+       authform = (Form_pg_authid) GETSTRUCT(oldtuple);
 
        if (roleid == GetSessionUserId())
                ereport(ERROR,
@@ -1146,6 +1166,24 @@ RenameRole(const char *oldname, const char *newname)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("current user cannot be renamed")));
 
+       /*
+        * Check that the user is not trying to rename a system role and
+        * not trying to rename a role into the reserved "pg_" namespace.
+        */
+       if (IsReservedName(NameStr(authform->rolname)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        NameStr(authform->rolname)),
+                                errdetail("Role names starting with \"pg_\" are reserved.")));
+
+       if (IsReservedName(newname))
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                        newname),
+                                errdetail("Role names starting with \"pg_\" are reserved.")));
+
        /* make sure the new name doesn't exist */
        if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
                ereport(ERROR,
@@ -1224,10 +1262,18 @@ GrantRole(GrantRoleStmt *stmt)
        ListCell   *item;
 
        if (stmt->grantor)
+       {
+               check_rolespec_name(stmt->grantor,
+                                                       "Cannot specify reserved role as grantor.");
                grantor = get_rolespec_oid(stmt->grantor, false);
+       }
        else
                grantor = GetUserId();
 
+       foreach(item, stmt->grantee_roles)
+               check_rolespec_name(lfirst(item),
+                                                       "Cannot GRANT roles to a reserved role.");
+
        grantee_ids = roleSpecsToIds(stmt->grantee_roles);
 
        /* AccessShareLock is enough since we aren't modifying pg_authid */
@@ -1318,6 +1364,9 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
                                         errmsg("permission denied to reassign objects")));
        }
 
+       check_rolespec_name(stmt->newrole,
+                                               "Cannot specify reserved role as owner.");
+
        /* Must have privileges on the receiving side too */
        newrole = get_rolespec_oid(stmt->newrole, false);
 
index f801faacd29c541d36ba9a49d8108dc85e4838f1..57da0149d9fc35c844860078cb39cb71e409482a 100644 (file)
@@ -854,6 +854,9 @@ check_role(char **newval, void **extra, GucSource source)
                roleid = InvalidOid;
                is_superuser = false;
        }
+       /* Do not allow setting role to a reserved role. */
+       else if (strncmp(*newval, "pg_", 3) == 0)
+               return false;
        else
        {
                if (!IsTransactionState())
index 63fbce017c5855cb6624fa5f134b0a65d907b524..d2b23d05cbd3896ec204aa4afae5f52008190b18 100644 (file)
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
@@ -5247,3 +5248,41 @@ get_rolespec_name(const Node *node)
 
        return rolename;
 }
+
+/*
+ * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
+ * if provided.
+ *
+ * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
+ * message is provided.
+ */
+void
+check_rolespec_name(const Node *node, const char *detail_msg)
+{
+       RoleSpec   *role;
+
+       if (!node)
+               return;
+
+       role = (RoleSpec *) node;
+
+       Assert(IsA(node, RoleSpec));
+
+       if (role->roletype != ROLESPEC_CSTRING)
+               return;
+
+       if (IsReservedName(role->rolename))
+       {
+               if (detail_msg)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_RESERVED_NAME),
+                                        errmsg("role \"%s\" is reserved",
+                                                role->rolename),
+                                        errdetail("%s", detail_msg)));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_RESERVED_NAME),
+                                        errmsg("role \"%s\" is reserved",
+                                                role->rolename)));
+       }
+}
index a59493710b2d376e48213ab42f83c535434f7798..a7dc41c46d04ce946224524e08c6120cd41d6865 100644 (file)
@@ -665,7 +665,7 @@ dumpRoles(PGconn *conn)
        int                     i;
 
        /* note: rolconfig is dumped later */
-       if (server_version >= 90500)
+       if (server_version >= 90600)
                printfPQExpBuffer(buf,
                                                  "SELECT oid, rolname, rolsuper, rolinherit, "
                                                  "rolcreaterole, rolcreatedb, "
@@ -674,6 +674,7 @@ dumpRoles(PGconn *conn)
                         "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
                                                  "rolname = current_user AS is_current_user "
                                                  "FROM pg_authid "
+                                                 "WHERE rolname !~ '^pg_' "
                                                  "ORDER BY 2");
        else if (server_version >= 90100)
                printfPQExpBuffer(buf,
@@ -771,6 +772,13 @@ dumpRoles(PGconn *conn)
                auth_oid = atooid(PQgetvalue(res, i, i_oid));
                rolename = PQgetvalue(res, i, i_rolname);
 
+               if (strncmp(rolename,"pg_",3) == 0)
+               {
+                       fprintf(stderr, _("%s: role name starting with 'pg_' skipped (%s)\n"),
+                                       progname, rolename);
+                       continue;
+               }
+
                resetPQExpBuffer(buf);
 
                if (binary_upgrade)
@@ -896,6 +904,7 @@ dumpRoleMembership(PGconn *conn)
                                           "LEFT JOIN pg_authid ur on ur.oid = a.roleid "
                                           "LEFT JOIN pg_authid um on um.oid = a.member "
                                           "LEFT JOIN pg_authid ug on ug.oid = a.grantor "
+                                          "WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
                                           "ORDER BY 1,2,3");
 
        if (PQntuples(res) > 0)
index f932094bd0da563a23a6ea5fe59439416dd27d3f..6b6f5ba7ea6570e062d9096c0775434666ca832e 100644 (file)
@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
+static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void get_bin_version(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
 
@@ -96,6 +97,11 @@ check_and_dump_old_cluster(bool live_check)
        check_for_prepared_transactions(&old_cluster);
        check_for_reg_data_type_usage(&old_cluster);
        check_for_isn_and_int8_passing_mismatch(&old_cluster);
+
+       /* 9.5 and below should not have roles starting with pg_ */
+       if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
+               check_for_pg_role_prefix(&old_cluster);
+
        if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
                old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
                check_for_jsonb_9_4_usage(&old_cluster);
@@ -629,7 +635,8 @@ check_is_install_user(ClusterInfo *cluster)
        res = executeQueryOrDie(conn,
                                                        "SELECT rolsuper, oid "
                                                        "FROM pg_catalog.pg_roles "
-                                                       "WHERE rolname = current_user");
+                                                       "WHERE rolname = current_user "
+                                                       "AND rolname !~ '^pg_'");
 
        /*
         * We only allow the install user in the new cluster (see comment below)
@@ -645,7 +652,8 @@ check_is_install_user(ClusterInfo *cluster)
 
        res = executeQueryOrDie(conn,
                                                        "SELECT COUNT(*) "
-                                                       "FROM pg_catalog.pg_roles ");
+                                                       "FROM pg_catalog.pg_roles "
+                                                       "WHERE rolname !~ '^pg_'");
 
        if (PQntuples(res) != 1)
                pg_fatal("could not determine the number of users\n");
@@ -1033,6 +1041,34 @@ check_for_jsonb_9_4_usage(ClusterInfo *cluster)
                check_ok();
 }
 
+/*
+ * check_for_pg_role_prefix()
+ *
+ *     Versions older than 9.6 should not have any pg_* roles
+ */
+static void
+check_for_pg_role_prefix(ClusterInfo *cluster)
+{
+       PGresult   *res;
+       PGconn     *conn = connectToServer(cluster, "template1");
+
+       prep_status("Checking for roles starting with 'pg_'");
+
+       res = executeQueryOrDie(conn,
+                                                       "SELECT * "
+                                                       "FROM pg_catalog.pg_roles "
+                                                       "WHERE rolname ~ '^pg_'");
+
+       if (PQntuples(res) != 0)
+               pg_fatal("The %s cluster contains roles starting with 'pg_'\n",
+                                CLUSTER_NAME(cluster));
+
+       PQclear(res);
+
+       PQfinish(conn);
+
+       check_ok();
+}
 
 static void
 get_bin_version(ClusterInfo *cluster)
index 1d326a81afe8b1b3a10d04742e0694fb5969fe3b..9eae76f7ba8655b54ec0603f93685f706b8d4482 100644 (file)
@@ -429,7 +429,7 @@ exec_command(const char *cmd,
                                break;
                        case 'g':
                                /* no longer distinct from \du */
-                               success = describeRoles(pattern, show_verbose);
+                               success = describeRoles(pattern, show_verbose, show_system);
                                break;
                        case 'l':
                                success = do_lo_list();
@@ -474,7 +474,7 @@ exec_command(const char *cmd,
                                        success = PSQL_CMD_UNKNOWN;
                                break;
                        case 'u':
-                               success = describeRoles(pattern, show_verbose);
+                               success = describeRoles(pattern, show_verbose, show_system);
                                break;
                        case 'F':                       /* text search subsystem */
                                switch (cmd[2])
index 7b2f4e64ef199620f147da5fec8a440d9909c2e0..0a771bd2107fd41336a1148c06ff772c35f84dca 100644 (file)
@@ -2646,7 +2646,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
  * Describes roles.  Any schema portion of the pattern is ignored.
  */
 bool
-describeRoles(const char *pattern, bool verbose)
+describeRoles(const char *pattern, bool verbose, bool showSystem)
 {
        PQExpBufferData buf;
        PGresult   *res;
@@ -2691,6 +2691,9 @@ describeRoles(const char *pattern, bool verbose)
 
                appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
+               if (!showSystem && !pattern)
+                       appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
+
                processSQLNamePattern(pset.db, &buf, pattern, false, false,
                                                          NULL, "r.rolname", NULL, NULL);
        }
index e4fc79edd14eb156e6ba9507a3e74e90ff049708..96722756b46dcb2bacf9353f336e5a2f434d1197 100644 (file)
@@ -25,7 +25,7 @@ extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
 extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
 
 /* \du, \dg */
-extern bool describeRoles(const char *pattern, bool verbose);
+extern bool describeRoles(const char *pattern, bool verbose, bool showSystem);
 
 /* \drds */
 extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
index 7549451d21691de9fef52a57f6eea088b7aa7511..eda31e2fb0fa18bcf06bb692326c4c6fe35ee605 100644 (file)
@@ -229,7 +229,7 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\dFd[+] [PATTERN]      list text search dictionaries\n"));
        fprintf(output, _("  \\dFp[+] [PATTERN]      list text search parsers\n"));
        fprintf(output, _("  \\dFt[+] [PATTERN]      list text search templates\n"));
-       fprintf(output, _("  \\dg[+]  [PATTERN]      list roles\n"));
+       fprintf(output, _("  \\dg[S+] [PATTERN]      list roles\n"));
        fprintf(output, _("  \\di[S+] [PATTERN]      list indexes\n"));
        fprintf(output, _("  \\dl                    list large objects, same as \\lo_list\n"));
        fprintf(output, _("  \\dL[S+] [PATTERN]      list procedural languages\n"));
@@ -242,7 +242,7 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\ds[S+] [PATTERN]      list sequences\n"));
        fprintf(output, _("  \\dt[S+] [PATTERN]      list tables\n"));
        fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
-       fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
+       fprintf(output, _("  \\du[S+] [PATTERN]      list roles\n"));
        fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
        fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
        fprintf(output, _("  \\dx[+]  [PATTERN]      list extensions\n"));
index 4e15a14d65a18ddcfb16141be9e8ec4950c13866..d91437b2346d2aee34452eba6586a26e56b13a8b 100644 (file)
@@ -231,6 +231,7 @@ extern void check_is_member_of_role(Oid member, Oid role);
 extern Oid     get_role_oid(const char *rolename, bool missing_ok);
 extern Oid     get_role_oid_or_public(const char *rolename);
 extern Oid     get_rolespec_oid(const Node *node, bool missing_ok);
+extern void    check_rolespec_name(const Node *node, const char *detail_msg);
 extern HeapTuple get_rolespec_tuple(const Node *node);
 extern char *get_rolespec_name(const Node *node);
 
index 8f88c025e8fb10f6203ca86ee4584acbe11a0f09..01b3b90ec14dcc877dcafd5309f601f87e76b305 100644 (file)
@@ -78,6 +78,18 @@ CREATE ROLE "none"; -- error
 ERROR:  role name "none" is reserved
 LINE 1: CREATE ROLE "none";
                     ^
+CREATE ROLE pg_abc; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abc"; -- error
+ERROR:  role name "pg_abc" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE pg_abcdef; -- error
+ERROR:  role name "pg_abcdef" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "pg_abcdef"; -- error
+ERROR:  role name "pg_abcdef" is reserved
+DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -804,6 +816,14 @@ LINE 1: DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9;
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 NOTICE:  role "nonexistent" does not exist, skipping
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_abc; -- error
+ERROR:  role "pg_abc" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+GRANT pg_abc TO pg_abcdef; -- error
+ERROR:  role "pg_abcdef" is reserved
+DETAIL:  Cannot GRANT roles to a reserved role.
+SET ROLE pg_testrole; -- error
+ERROR:  invalid value for parameter "role": "pg_testrole"
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';
  proname  | proacl 
index e8c6b335ec2e897d68c0795c3fba10c0162359d5..1e0e9af8da1935d684eae766dbc436620bdcb188 100644 (file)
@@ -57,6 +57,11 @@ CREATE ROLE "public"; -- error
 CREATE ROLE none; -- error
 CREATE ROLE "none"; -- error
 
+CREATE ROLE pg_abc; -- error
+CREATE ROLE "pg_abc"; -- error
+CREATE ROLE pg_abcdef; -- error
+CREATE ROLE "pg_abcdef"; -- error
+
 CREATE ROLE testrol0 SUPERUSER LOGIN;
 CREATE ROLE testrolx SUPERUSER LOGIN;
 CREATE ROLE testrol2 SUPERUSER;
@@ -376,6 +381,11 @@ DROP USER MAPPING IF EXISTS FOR CURRENT_ROLE SERVER sv9; --error
 DROP USER MAPPING IF EXISTS FOR nonexistent SERVER sv9;  -- error
 
 -- GRANT/REVOKE
+GRANT testrol0 TO pg_abc; -- error
+GRANT pg_abc TO pg_abcdef; -- error
+
+SET ROLE pg_testrole; -- error
+
 UPDATE pg_proc SET proacl = null WHERE proname LIKE 'testagg_';
 SELECT proname, proacl FROM pg_proc WHERE proname LIKE 'testagg_';