LCOV - code coverage report
Current view: top level - src/backend/commands - user.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.4 % 860 769
Test Date: 2026-03-10 05:14:54 Functions: 100.0 % 21 21
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * user.c
       4              :  *    Commands for manipulating roles (formerly called users).
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  * src/backend/commands/user.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/genam.h"
      16              : #include "access/htup_details.h"
      17              : #include "access/table.h"
      18              : #include "access/xact.h"
      19              : #include "catalog/binary_upgrade.h"
      20              : #include "catalog/catalog.h"
      21              : #include "catalog/dependency.h"
      22              : #include "catalog/indexing.h"
      23              : #include "catalog/objectaccess.h"
      24              : #include "catalog/pg_auth_members.h"
      25              : #include "catalog/pg_authid.h"
      26              : #include "catalog/pg_database.h"
      27              : #include "catalog/pg_db_role_setting.h"
      28              : #include "commands/comment.h"
      29              : #include "commands/dbcommands.h"
      30              : #include "commands/defrem.h"
      31              : #include "commands/seclabel.h"
      32              : #include "commands/user.h"
      33              : #include "libpq/crypt.h"
      34              : #include "miscadmin.h"
      35              : #include "port/pg_bitutils.h"
      36              : #include "storage/lmgr.h"
      37              : #include "utils/acl.h"
      38              : #include "utils/builtins.h"
      39              : #include "utils/catcache.h"
      40              : #include "utils/fmgroids.h"
      41              : #include "utils/syscache.h"
      42              : #include "utils/varlena.h"
      43              : 
      44              : /*
      45              :  * Removing a role grant - or the admin option on it - might recurse to
      46              :  * dependent grants. We use these values to reason about what would need to
      47              :  * be done in such cases.
      48              :  *
      49              :  * RRG_NOOP indicates a grant that would not need to be altered by the
      50              :  * operation.
      51              :  *
      52              :  * RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
      53              :  * admin_option set to false by the operation.
      54              :  *
      55              :  * Similarly, RRG_REMOVE_INHERIT_OPTION and RRG_REMOVE_SET_OPTION indicate
      56              :  * grants that would need to have the corresponding options set to false.
      57              :  *
      58              :  * RRG_DELETE_GRANT indicates a grant that would need to be removed entirely
      59              :  * by the operation.
      60              :  */
      61              : typedef enum
      62              : {
      63              :     RRG_NOOP,
      64              :     RRG_REMOVE_ADMIN_OPTION,
      65              :     RRG_REMOVE_INHERIT_OPTION,
      66              :     RRG_REMOVE_SET_OPTION,
      67              :     RRG_DELETE_GRANT,
      68              : } RevokeRoleGrantAction;
      69              : 
      70              : /* Potentially set by pg_upgrade_support functions */
      71              : Oid         binary_upgrade_next_pg_authid_oid = InvalidOid;
      72              : 
      73              : typedef struct
      74              : {
      75              :     unsigned    specified;
      76              :     bool        admin;
      77              :     bool        inherit;
      78              :     bool        set;
      79              : } GrantRoleOptions;
      80              : 
      81              : #define GRANT_ROLE_SPECIFIED_ADMIN          0x0001
      82              : #define GRANT_ROLE_SPECIFIED_INHERIT        0x0002
      83              : #define GRANT_ROLE_SPECIFIED_SET            0x0004
      84              : 
      85              : /* GUC parameters */
      86              : int         Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;
      87              : char       *createrole_self_grant = "";
      88              : static bool createrole_self_grant_enabled = false;
      89              : static GrantRoleOptions createrole_self_grant_options;
      90              : 
      91              : /* Hook to check passwords in CreateRole() and AlterRole() */
      92              : check_password_hook_type check_password_hook = NULL;
      93              : 
      94              : static void AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
      95              :                         List *memberSpecs, List *memberIds,
      96              :                         Oid grantorId, GrantRoleOptions *popt);
      97              : static void DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
      98              :                         List *memberSpecs, List *memberIds,
      99              :                         Oid grantorId, GrantRoleOptions *popt,
     100              :                         DropBehavior behavior);
     101              : static void check_role_membership_authorization(Oid currentUserId, Oid roleid,
     102              :                                                 bool is_grant);
     103              : static Oid  check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId,
     104              :                                bool is_grant);
     105              : static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
     106              : static bool plan_single_revoke(CatCList *memlist,
     107              :                                RevokeRoleGrantAction *actions,
     108              :                                Oid member, Oid grantor,
     109              :                                GrantRoleOptions *popt,
     110              :                                DropBehavior behavior);
     111              : static void plan_member_revoke(CatCList *memlist,
     112              :                                RevokeRoleGrantAction *actions, Oid member);
     113              : static void plan_recursive_revoke(CatCList *memlist,
     114              :                                   RevokeRoleGrantAction *actions,
     115              :                                   int index,
     116              :                                   bool revoke_admin_option_only,
     117              :                                   DropBehavior behavior);
     118              : static void InitGrantRoleOptions(GrantRoleOptions *popt);
     119              : 
     120              : 
     121              : /* Check if current user has createrole privileges */
     122              : static bool
     123         1140 : have_createrole_privilege(void)
     124              : {
     125         1140 :     return has_createrole_privilege(GetUserId());
     126              : }
     127              : 
     128              : 
     129              : /*
     130              :  * CREATE ROLE
     131              :  */
     132              : Oid
     133          973 : CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
     134              : {
     135              :     Relation    pg_authid_rel;
     136              :     TupleDesc   pg_authid_dsc;
     137              :     HeapTuple   tuple;
     138          973 :     Datum       new_record[Natts_pg_authid] = {0};
     139          973 :     bool        new_record_nulls[Natts_pg_authid] = {0};
     140          973 :     Oid         currentUserId = GetUserId();
     141              :     Oid         roleid;
     142              :     ListCell   *item;
     143              :     ListCell   *option;
     144          973 :     char       *password = NULL;    /* user password */
     145          973 :     bool        issuper = false;    /* Make the user a superuser? */
     146          973 :     bool        inherit = true; /* Auto inherit privileges? */
     147          973 :     bool        createrole = false; /* Can this user create roles? */
     148          973 :     bool        createdb = false;   /* Can the user create databases? */
     149          973 :     bool        canlogin = false;   /* Can this user login? */
     150          973 :     bool        isreplication = false;  /* Is this a replication role? */
     151          973 :     bool        bypassrls = false;  /* Is this a row security enabled role? */
     152          973 :     int         connlimit = -1; /* maximum connections allowed */
     153          973 :     List       *addroleto = NIL;    /* roles to make this a member of */
     154          973 :     List       *rolemembers = NIL;  /* roles to be members of this role */
     155          973 :     List       *adminmembers = NIL; /* roles to be admins of this role */
     156          973 :     char       *validUntil = NULL;  /* time the login is valid until */
     157              :     Datum       validUntil_datum;   /* same, as timestamptz Datum */
     158              :     bool        validUntil_null;
     159          973 :     DefElem    *dpassword = NULL;
     160          973 :     DefElem    *dissuper = NULL;
     161          973 :     DefElem    *dinherit = NULL;
     162          973 :     DefElem    *dcreaterole = NULL;
     163          973 :     DefElem    *dcreatedb = NULL;
     164          973 :     DefElem    *dcanlogin = NULL;
     165          973 :     DefElem    *disreplication = NULL;
     166          973 :     DefElem    *dconnlimit = NULL;
     167          973 :     DefElem    *daddroleto = NULL;
     168          973 :     DefElem    *drolemembers = NULL;
     169          973 :     DefElem    *dadminmembers = NULL;
     170          973 :     DefElem    *dvalidUntil = NULL;
     171          973 :     DefElem    *dbypassRLS = NULL;
     172              :     GrantRoleOptions popt;
     173              : 
     174              :     /* Report error if name has \n or \r character. */
     175          973 :     if (strpbrk(stmt->role, "\n\r"))
     176            1 :         ereport(ERROR,
     177              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     178              :                  errmsg("role name \"%s\" contains a newline or carriage return character", stmt->role)));
     179              : 
     180              :     /* The defaults can vary depending on the original statement type */
     181          972 :     switch (stmt->stmt_type)
     182              :     {
     183          731 :         case ROLESTMT_ROLE:
     184          731 :             break;
     185          229 :         case ROLESTMT_USER:
     186          229 :             canlogin = true;
     187              :             /* may eventually want inherit to default to false here */
     188          229 :             break;
     189           12 :         case ROLESTMT_GROUP:
     190           12 :             break;
     191              :     }
     192              : 
     193              :     /* Extract options from the statement node tree */
     194         1575 :     foreach(option, stmt->options)
     195              :     {
     196          603 :         DefElem    *defel = (DefElem *) lfirst(option);
     197              : 
     198          603 :         if (strcmp(defel->defname, "password") == 0)
     199              :         {
     200           64 :             if (dpassword)
     201            0 :                 errorConflictingDefElem(defel, pstate);
     202           64 :             dpassword = defel;
     203              :         }
     204          539 :         else if (strcmp(defel->defname, "sysid") == 0)
     205              :         {
     206            3 :             ereport(NOTICE,
     207              :                     (errmsg("SYSID can no longer be specified")));
     208              :         }
     209          536 :         else if (strcmp(defel->defname, "superuser") == 0)
     210              :         {
     211           97 :             if (dissuper)
     212            0 :                 errorConflictingDefElem(defel, pstate);
     213           97 :             dissuper = defel;
     214              :         }
     215          439 :         else if (strcmp(defel->defname, "inherit") == 0)
     216              :         {
     217           32 :             if (dinherit)
     218            0 :                 errorConflictingDefElem(defel, pstate);
     219           32 :             dinherit = defel;
     220              :         }
     221          407 :         else if (strcmp(defel->defname, "createrole") == 0)
     222              :         {
     223           45 :             if (dcreaterole)
     224            0 :                 errorConflictingDefElem(defel, pstate);
     225           45 :             dcreaterole = defel;
     226              :         }
     227          362 :         else if (strcmp(defel->defname, "createdb") == 0)
     228              :         {
     229           35 :             if (dcreatedb)
     230            0 :                 errorConflictingDefElem(defel, pstate);
     231           35 :             dcreatedb = defel;
     232              :         }
     233          327 :         else if (strcmp(defel->defname, "canlogin") == 0)
     234              :         {
     235          154 :             if (dcanlogin)
     236            0 :                 errorConflictingDefElem(defel, pstate);
     237          154 :             dcanlogin = defel;
     238              :         }
     239          173 :         else if (strcmp(defel->defname, "isreplication") == 0)
     240              :         {
     241           46 :             if (disreplication)
     242            0 :                 errorConflictingDefElem(defel, pstate);
     243           46 :             disreplication = defel;
     244              :         }
     245          127 :         else if (strcmp(defel->defname, "connectionlimit") == 0)
     246              :         {
     247            7 :             if (dconnlimit)
     248            0 :                 errorConflictingDefElem(defel, pstate);
     249            7 :             dconnlimit = defel;
     250              :         }
     251          120 :         else if (strcmp(defel->defname, "addroleto") == 0)
     252              :         {
     253           50 :             if (daddroleto)
     254            0 :                 errorConflictingDefElem(defel, pstate);
     255           50 :             daddroleto = defel;
     256              :         }
     257           70 :         else if (strcmp(defel->defname, "rolemembers") == 0)
     258              :         {
     259           14 :             if (drolemembers)
     260            0 :                 errorConflictingDefElem(defel, pstate);
     261           14 :             drolemembers = defel;
     262              :         }
     263           56 :         else if (strcmp(defel->defname, "adminmembers") == 0)
     264              :         {
     265           11 :             if (dadminmembers)
     266            0 :                 errorConflictingDefElem(defel, pstate);
     267           11 :             dadminmembers = defel;
     268              :         }
     269           45 :         else if (strcmp(defel->defname, "validUntil") == 0)
     270              :         {
     271            4 :             if (dvalidUntil)
     272            0 :                 errorConflictingDefElem(defel, pstate);
     273            4 :             dvalidUntil = defel;
     274              :         }
     275           41 :         else if (strcmp(defel->defname, "bypassrls") == 0)
     276              :         {
     277           41 :             if (dbypassRLS)
     278            0 :                 errorConflictingDefElem(defel, pstate);
     279           41 :             dbypassRLS = defel;
     280              :         }
     281              :         else
     282            0 :             elog(ERROR, "option \"%s\" not recognized",
     283              :                  defel->defname);
     284              :     }
     285              : 
     286          972 :     if (dpassword && dpassword->arg)
     287           58 :         password = strVal(dpassword->arg);
     288          972 :     if (dissuper)
     289           97 :         issuper = boolVal(dissuper->arg);
     290          972 :     if (dinherit)
     291           32 :         inherit = boolVal(dinherit->arg);
     292          972 :     if (dcreaterole)
     293           45 :         createrole = boolVal(dcreaterole->arg);
     294          972 :     if (dcreatedb)
     295           35 :         createdb = boolVal(dcreatedb->arg);
     296          972 :     if (dcanlogin)
     297          154 :         canlogin = boolVal(dcanlogin->arg);
     298          972 :     if (disreplication)
     299           46 :         isreplication = boolVal(disreplication->arg);
     300          972 :     if (dconnlimit)
     301              :     {
     302            7 :         connlimit = intVal(dconnlimit->arg);
     303            7 :         if (connlimit < -1)
     304            0 :             ereport(ERROR,
     305              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     306              :                      errmsg("invalid connection limit: %d", connlimit)));
     307              :     }
     308          972 :     if (daddroleto)
     309           50 :         addroleto = (List *) daddroleto->arg;
     310          972 :     if (drolemembers)
     311           14 :         rolemembers = (List *) drolemembers->arg;
     312          972 :     if (dadminmembers)
     313           11 :         adminmembers = (List *) dadminmembers->arg;
     314          972 :     if (dvalidUntil)
     315            4 :         validUntil = strVal(dvalidUntil->arg);
     316          972 :     if (dbypassRLS)
     317           41 :         bypassrls = boolVal(dbypassRLS->arg);
     318              : 
     319              :     /* Check some permissions first */
     320          972 :     if (!superuser_arg(currentUserId))
     321              :     {
     322          111 :         if (!has_createrole_privilege(currentUserId))
     323            0 :             ereport(ERROR,
     324              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     325              :                      errmsg("permission denied to create role"),
     326              :                      errdetail("Only roles with the %s attribute may create roles.",
     327              :                                "CREATEROLE")));
     328          111 :         if (issuper)
     329            3 :             ereport(ERROR,
     330              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     331              :                      errmsg("permission denied to create role"),
     332              :                      errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
     333              :                                "SUPERUSER", "SUPERUSER")));
     334          108 :         if (createdb && !have_createdb_privilege())
     335            3 :             ereport(ERROR,
     336              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     337              :                      errmsg("permission denied to create role"),
     338              :                      errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
     339              :                                "CREATEDB", "CREATEDB")));
     340          105 :         if (isreplication && !has_rolreplication(currentUserId))
     341            6 :             ereport(ERROR,
     342              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     343              :                      errmsg("permission denied to create role"),
     344              :                      errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
     345              :                                "REPLICATION", "REPLICATION")));
     346           99 :         if (bypassrls && !has_bypassrls_privilege(currentUserId))
     347            3 :             ereport(ERROR,
     348              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     349              :                      errmsg("permission denied to create role"),
     350              :                      errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
     351              :                                "BYPASSRLS", "BYPASSRLS")));
     352              :     }
     353              : 
     354              :     /*
     355              :      * Check that the user is not trying to create a role in the reserved
     356              :      * "pg_" namespace.
     357              :      */
     358          957 :     if (IsReservedName(stmt->role))
     359            4 :         ereport(ERROR,
     360              :                 (errcode(ERRCODE_RESERVED_NAME),
     361              :                  errmsg("role name \"%s\" is reserved",
     362              :                         stmt->role),
     363              :                  errdetail("Role names starting with \"pg_\" are reserved.")));
     364              : 
     365              :     /*
     366              :      * If built with appropriate switch, whine when regression-testing
     367              :      * conventions for role names are violated.
     368              :      */
     369              : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
     370              :     if (strncmp(stmt->role, "regress_", 8) != 0)
     371              :         elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
     372              : #endif
     373              : 
     374              :     /*
     375              :      * Check the pg_authid relation to be certain the role doesn't already
     376              :      * exist.
     377              :      */
     378          953 :     pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
     379          953 :     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
     380              : 
     381          953 :     if (OidIsValid(get_role_oid(stmt->role, true)))
     382            3 :         ereport(ERROR,
     383              :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     384              :                  errmsg("role \"%s\" already exists",
     385              :                         stmt->role)));
     386              : 
     387              :     /* Convert validuntil to internal form */
     388          950 :     if (validUntil)
     389              :     {
     390            4 :         validUntil_datum = DirectFunctionCall3(timestamptz_in,
     391              :                                                CStringGetDatum(validUntil),
     392              :                                                ObjectIdGetDatum(InvalidOid),
     393              :                                                Int32GetDatum(-1));
     394            4 :         validUntil_null = false;
     395              :     }
     396              :     else
     397              :     {
     398          946 :         validUntil_datum = (Datum) 0;
     399          946 :         validUntil_null = true;
     400              :     }
     401              : 
     402              :     /*
     403              :      * Call the password checking hook if there is one defined
     404              :      */
     405          950 :     if (check_password_hook && password)
     406            0 :         (*check_password_hook) (stmt->role,
     407              :                                 password,
     408              :                                 get_password_type(password),
     409              :                                 validUntil_datum,
     410              :                                 validUntil_null);
     411              : 
     412              :     /*
     413              :      * Build a tuple to insert
     414              :      */
     415          950 :     new_record[Anum_pg_authid_rolname - 1] =
     416          950 :         DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
     417          950 :     new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
     418          950 :     new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
     419          950 :     new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
     420          950 :     new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
     421          950 :     new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
     422          950 :     new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
     423          950 :     new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
     424              : 
     425          950 :     if (password)
     426              :     {
     427              :         char       *shadow_pass;
     428           58 :         const char *logdetail = NULL;
     429              : 
     430              :         /*
     431              :          * Don't allow an empty password. Libpq treats an empty password the
     432              :          * same as no password at all, and won't even try to authenticate. But
     433              :          * other clients might, so allowing it would be confusing. By clearing
     434              :          * the password when an empty string is specified, the account is
     435              :          * consistently locked for all clients.
     436              :          *
     437              :          * Note that this only covers passwords stored in the database itself.
     438              :          * There are also checks in the authentication code, to forbid an
     439              :          * empty password from being used with authentication methods that
     440              :          * fetch the password from an external system, like LDAP or PAM.
     441              :          */
     442          113 :         if (password[0] == '\0' ||
     443           55 :             plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK)
     444              :         {
     445            3 :             ereport(NOTICE,
     446              :                     (errmsg("empty string is not a valid password, clearing password")));
     447            3 :             new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     448              :         }
     449              :         else
     450              :         {
     451              :             /* Encrypt the password to the requested format. */
     452           55 :             shadow_pass = encrypt_password(Password_encryption, stmt->role,
     453              :                                            password);
     454           52 :             new_record[Anum_pg_authid_rolpassword - 1] =
     455           52 :                 CStringGetTextDatum(shadow_pass);
     456              :         }
     457              :     }
     458              :     else
     459          892 :         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     460              : 
     461          947 :     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
     462          947 :     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
     463              : 
     464          947 :     new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
     465              : 
     466              :     /*
     467              :      * pg_largeobject_metadata contains pg_authid.oid's, so we use the
     468              :      * binary-upgrade override.
     469              :      */
     470          947 :     if (IsBinaryUpgrade)
     471              :     {
     472            8 :         if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
     473            0 :             ereport(ERROR,
     474              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     475              :                      errmsg("pg_authid OID value not set when in binary upgrade mode")));
     476              : 
     477            8 :         roleid = binary_upgrade_next_pg_authid_oid;
     478            8 :         binary_upgrade_next_pg_authid_oid = InvalidOid;
     479              :     }
     480              :     else
     481              :     {
     482          939 :         roleid = GetNewOidWithIndex(pg_authid_rel, AuthIdOidIndexId,
     483              :                                     Anum_pg_authid_oid);
     484              :     }
     485              : 
     486          947 :     new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(roleid);
     487              : 
     488          947 :     tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
     489              : 
     490              :     /*
     491              :      * Insert new record in the pg_authid table
     492              :      */
     493          947 :     CatalogTupleInsert(pg_authid_rel, tuple);
     494              : 
     495              :     /*
     496              :      * Advance command counter so we can see new record; else tests in
     497              :      * AddRoleMems may fail.
     498              :      */
     499          947 :     if (addroleto || adminmembers || rolemembers)
     500           72 :         CommandCounterIncrement();
     501              : 
     502              :     /* Default grant. */
     503          947 :     InitGrantRoleOptions(&popt);
     504              : 
     505              :     /*
     506              :      * Add the new role to the specified existing roles.
     507              :      */
     508          947 :     if (addroleto)
     509              :     {
     510           50 :         RoleSpec   *thisrole = makeNode(RoleSpec);
     511           50 :         List       *thisrole_list = list_make1(thisrole);
     512           50 :         List       *thisrole_oidlist = list_make1_oid(roleid);
     513              : 
     514           50 :         thisrole->roletype = ROLESPEC_CSTRING;
     515           50 :         thisrole->rolename = stmt->role;
     516           50 :         thisrole->location = -1;
     517              : 
     518           64 :         foreach(item, addroleto)
     519              :         {
     520           50 :             RoleSpec   *oldrole = lfirst(item);
     521           50 :             HeapTuple   oldroletup = get_rolespec_tuple(oldrole);
     522           50 :             Form_pg_authid oldroleform = (Form_pg_authid) GETSTRUCT(oldroletup);
     523           50 :             Oid         oldroleid = oldroleform->oid;
     524           50 :             char       *oldrolename = NameStr(oldroleform->rolname);
     525              : 
     526              :             /* can only add this role to roles for which you have rights */
     527           50 :             check_role_membership_authorization(currentUserId, oldroleid, true);
     528           14 :             AddRoleMems(currentUserId, oldrolename, oldroleid,
     529              :                         thisrole_list,
     530              :                         thisrole_oidlist,
     531              :                         InvalidOid, &popt);
     532              : 
     533           14 :             ReleaseSysCache(oldroletup);
     534              :         }
     535              :     }
     536              : 
     537              :     /*
     538              :      * If the current user isn't a superuser, make them an admin of the new
     539              :      * role so that they can administer the new object they just created.
     540              :      * Superusers will be able to do that anyway.
     541              :      *
     542              :      * The grantor of record for this implicit grant is the bootstrap
     543              :      * superuser, which means that the CREATEROLE user cannot revoke the
     544              :      * grant. They can however grant the created role back to themselves with
     545              :      * different options, since they enjoy ADMIN OPTION on it.
     546              :      */
     547          911 :     if (!superuser())
     548              :     {
     549           60 :         RoleSpec   *current_role = makeNode(RoleSpec);
     550              :         GrantRoleOptions poptself;
     551              :         List       *memberSpecs;
     552           60 :         List       *memberIds = list_make1_oid(currentUserId);
     553              : 
     554           60 :         current_role->roletype = ROLESPEC_CURRENT_ROLE;
     555           60 :         current_role->location = -1;
     556           60 :         memberSpecs = list_make1(current_role);
     557              : 
     558           60 :         poptself.specified = GRANT_ROLE_SPECIFIED_ADMIN
     559              :             | GRANT_ROLE_SPECIFIED_INHERIT
     560              :             | GRANT_ROLE_SPECIFIED_SET;
     561           60 :         poptself.admin = true;
     562           60 :         poptself.inherit = false;
     563           60 :         poptself.set = false;
     564              : 
     565           60 :         AddRoleMems(BOOTSTRAP_SUPERUSERID, stmt->role, roleid,
     566              :                     memberSpecs, memberIds,
     567              :                     BOOTSTRAP_SUPERUSERID, &poptself);
     568              : 
     569              :         /*
     570              :          * We must make the implicit grant visible to the code below, else the
     571              :          * additional grants will fail.
     572              :          */
     573           60 :         CommandCounterIncrement();
     574              : 
     575              :         /*
     576              :          * Because of the implicit grant above, a CREATEROLE user who creates
     577              :          * a role has the ability to grant that role back to themselves with
     578              :          * the INHERIT or SET options, if they wish to inherit the role's
     579              :          * privileges or be able to SET ROLE to it. The createrole_self_grant
     580              :          * GUC can be used to make this happen automatically. This has no
     581              :          * security implications since the same user is able to make the same
     582              :          * grant using an explicit GRANT statement; it's just convenient.
     583              :          */
     584           60 :         if (createrole_self_grant_enabled)
     585            3 :             AddRoleMems(currentUserId, stmt->role, roleid,
     586              :                         memberSpecs, memberIds,
     587              :                         currentUserId, &createrole_self_grant_options);
     588              :     }
     589              : 
     590              :     /*
     591              :      * Add the specified members to this new role. adminmembers get the admin
     592              :      * option, rolemembers don't.
     593              :      *
     594              :      * NB: No permissions check is required here. If you have enough rights to
     595              :      * create a role, you can add any members you like.
     596              :      */
     597          911 :     AddRoleMems(currentUserId, stmt->role, roleid,
     598              :                 rolemembers, roleSpecsToIds(rolemembers),
     599              :                 InvalidOid, &popt);
     600          908 :     popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
     601          908 :     popt.admin = true;
     602          908 :     AddRoleMems(currentUserId, stmt->role, roleid,
     603              :                 adminmembers, roleSpecsToIds(adminmembers),
     604              :                 InvalidOid, &popt);
     605              : 
     606              :     /* Post creation hook for new role */
     607          905 :     InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
     608              : 
     609              :     /*
     610              :      * Close pg_authid, but keep lock till commit.
     611              :      */
     612          905 :     table_close(pg_authid_rel, NoLock);
     613              : 
     614          905 :     return roleid;
     615              : }
     616              : 
     617              : 
     618              : /*
     619              :  * ALTER ROLE
     620              :  *
     621              :  * Note: the rolemembers option accepted here is intended to support the
     622              :  * backwards-compatible ALTER GROUP syntax.  Although it will work to say
     623              :  * "ALTER ROLE role ROLE rolenames", we don't document it.
     624              :  */
     625              : Oid
     626          239 : AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
     627              : {
     628          239 :     Datum       new_record[Natts_pg_authid] = {0};
     629          239 :     bool        new_record_nulls[Natts_pg_authid] = {0};
     630          239 :     bool        new_record_repl[Natts_pg_authid] = {0};
     631              :     Relation    pg_authid_rel;
     632              :     TupleDesc   pg_authid_dsc;
     633              :     HeapTuple   tuple,
     634              :                 new_tuple;
     635              :     Form_pg_authid authform;
     636              :     ListCell   *option;
     637              :     char       *rolename;
     638          239 :     char       *password = NULL;    /* user password */
     639          239 :     int         connlimit = -1; /* maximum connections allowed */
     640          239 :     char       *validUntil = NULL;  /* time the login is valid until */
     641              :     Datum       validUntil_datum;   /* same, as timestamptz Datum */
     642              :     bool        validUntil_null;
     643          239 :     DefElem    *dpassword = NULL;
     644          239 :     DefElem    *dissuper = NULL;
     645          239 :     DefElem    *dinherit = NULL;
     646          239 :     DefElem    *dcreaterole = NULL;
     647          239 :     DefElem    *dcreatedb = NULL;
     648          239 :     DefElem    *dcanlogin = NULL;
     649          239 :     DefElem    *disreplication = NULL;
     650          239 :     DefElem    *dconnlimit = NULL;
     651          239 :     DefElem    *drolemembers = NULL;
     652          239 :     DefElem    *dvalidUntil = NULL;
     653          239 :     DefElem    *dbypassRLS = NULL;
     654              :     Oid         roleid;
     655          239 :     Oid         currentUserId = GetUserId();
     656              :     GrantRoleOptions popt;
     657              : 
     658          239 :     check_rolespec_name(stmt->role,
     659          239 :                         _("Cannot alter reserved roles."));
     660              : 
     661              :     /* Extract options from the statement node tree */
     662          652 :     foreach(option, stmt->options)
     663              :     {
     664          413 :         DefElem    *defel = (DefElem *) lfirst(option);
     665              : 
     666          413 :         if (strcmp(defel->defname, "password") == 0)
     667              :         {
     668           48 :             if (dpassword)
     669            0 :                 errorConflictingDefElem(defel, pstate);
     670           48 :             dpassword = defel;
     671              :         }
     672          365 :         else if (strcmp(defel->defname, "superuser") == 0)
     673              :         {
     674           58 :             if (dissuper)
     675            0 :                 errorConflictingDefElem(defel, pstate);
     676           58 :             dissuper = defel;
     677              :         }
     678          307 :         else if (strcmp(defel->defname, "inherit") == 0)
     679              :         {
     680           42 :             if (dinherit)
     681            0 :                 errorConflictingDefElem(defel, pstate);
     682           42 :             dinherit = defel;
     683              :         }
     684          265 :         else if (strcmp(defel->defname, "createrole") == 0)
     685              :         {
     686           33 :             if (dcreaterole)
     687            0 :                 errorConflictingDefElem(defel, pstate);
     688           33 :             dcreaterole = defel;
     689              :         }
     690          232 :         else if (strcmp(defel->defname, "createdb") == 0)
     691              :         {
     692           42 :             if (dcreatedb)
     693            0 :                 errorConflictingDefElem(defel, pstate);
     694           42 :             dcreatedb = defel;
     695              :         }
     696          190 :         else if (strcmp(defel->defname, "canlogin") == 0)
     697              :         {
     698           45 :             if (dcanlogin)
     699            0 :                 errorConflictingDefElem(defel, pstate);
     700           45 :             dcanlogin = defel;
     701              :         }
     702          145 :         else if (strcmp(defel->defname, "isreplication") == 0)
     703              :         {
     704           76 :             if (disreplication)
     705            0 :                 errorConflictingDefElem(defel, pstate);
     706           76 :             disreplication = defel;
     707              :         }
     708           69 :         else if (strcmp(defel->defname, "connectionlimit") == 0)
     709              :         {
     710            6 :             if (dconnlimit)
     711            0 :                 errorConflictingDefElem(defel, pstate);
     712            6 :             dconnlimit = defel;
     713              :         }
     714           63 :         else if (strcmp(defel->defname, "rolemembers") == 0 &&
     715           21 :                  stmt->action != 0)
     716              :         {
     717           21 :             if (drolemembers)
     718            0 :                 errorConflictingDefElem(defel, pstate);
     719           21 :             drolemembers = defel;
     720              :         }
     721           42 :         else if (strcmp(defel->defname, "validUntil") == 0)
     722              :         {
     723            0 :             if (dvalidUntil)
     724            0 :                 errorConflictingDefElem(defel, pstate);
     725            0 :             dvalidUntil = defel;
     726              :         }
     727           42 :         else if (strcmp(defel->defname, "bypassrls") == 0)
     728              :         {
     729           42 :             if (dbypassRLS)
     730            0 :                 errorConflictingDefElem(defel, pstate);
     731           42 :             dbypassRLS = defel;
     732              :         }
     733              :         else
     734            0 :             elog(ERROR, "option \"%s\" not recognized",
     735              :                  defel->defname);
     736              :     }
     737              : 
     738          239 :     if (dpassword && dpassword->arg)
     739           48 :         password = strVal(dpassword->arg);
     740          239 :     if (dconnlimit)
     741              :     {
     742            6 :         connlimit = intVal(dconnlimit->arg);
     743            6 :         if (connlimit < -1)
     744            0 :             ereport(ERROR,
     745              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     746              :                      errmsg("invalid connection limit: %d", connlimit)));
     747              :     }
     748          239 :     if (dvalidUntil)
     749            0 :         validUntil = strVal(dvalidUntil->arg);
     750              : 
     751              :     /*
     752              :      * Scan the pg_authid relation to be certain the user exists.
     753              :      */
     754          239 :     pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
     755          239 :     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
     756              : 
     757          239 :     tuple = get_rolespec_tuple(stmt->role);
     758          231 :     authform = (Form_pg_authid) GETSTRUCT(tuple);
     759          231 :     rolename = pstrdup(NameStr(authform->rolname));
     760          231 :     roleid = authform->oid;
     761              : 
     762              :     /* To mess with a superuser in any way you gotta be superuser. */
     763          231 :     if (!superuser() && authform->rolsuper)
     764            0 :         ereport(ERROR,
     765              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     766              :                  errmsg("permission denied to alter role"),
     767              :                  errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
     768              :                            "SUPERUSER", "SUPERUSER")));
     769          231 :     if (!superuser() && dissuper)
     770            9 :         ereport(ERROR,
     771              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     772              :                  errmsg("permission denied to alter role"),
     773              :                  errdetail("Only roles with the %s attribute may change the %s attribute.",
     774              :                            "SUPERUSER", "SUPERUSER")));
     775              : 
     776              :     /*
     777              :      * Most changes to a role require that you both have CREATEROLE privileges
     778              :      * and also ADMIN OPTION on the role.
     779              :      */
     780          222 :     if (!have_createrole_privilege() ||
     781          204 :         !is_admin_of_role(GetUserId(), roleid))
     782              :     {
     783              :         /* things an unprivileged user certainly can't do */
     784           21 :         if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
     785           18 :             dvalidUntil || disreplication || dbypassRLS)
     786            3 :             ereport(ERROR,
     787              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     788              :                      errmsg("permission denied to alter role"),
     789              :                      errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
     790              :                                "CREATEROLE", "ADMIN", rolename)));
     791              : 
     792              :         /* an unprivileged user can change their own password */
     793           18 :         if (dpassword && roleid != currentUserId)
     794            3 :             ereport(ERROR,
     795              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     796              :                      errmsg("permission denied to alter role"),
     797              :                      errdetail("To change another role's password, the current user must have the %s attribute and the %s option on the role.",
     798              :                                "CREATEROLE", "ADMIN")));
     799              :     }
     800          201 :     else if (!superuser())
     801              :     {
     802              :         /*
     803              :          * Even if you have both CREATEROLE and ADMIN OPTION on a role, you
     804              :          * can only change the CREATEDB, REPLICATION, or BYPASSRLS attributes
     805              :          * if they are set for your own role (or you are the superuser).
     806              :          */
     807           30 :         if (dcreatedb && !have_createdb_privilege())
     808            3 :             ereport(ERROR,
     809              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     810              :                      errmsg("permission denied to alter role"),
     811              :                      errdetail("Only roles with the %s attribute may change the %s attribute.",
     812              :                                "CREATEDB", "CREATEDB")));
     813           27 :         if (disreplication && !has_rolreplication(currentUserId))
     814            3 :             ereport(ERROR,
     815              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     816              :                      errmsg("permission denied to alter role"),
     817              :                      errdetail("Only roles with the %s attribute may change the %s attribute.",
     818              :                                "REPLICATION", "REPLICATION")));
     819           24 :         if (dbypassRLS && !has_bypassrls_privilege(currentUserId))
     820            3 :             ereport(ERROR,
     821              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     822              :                      errmsg("permission denied to alter role"),
     823              :                      errdetail("Only roles with the %s attribute may change the %s attribute.",
     824              :                                "BYPASSRLS", "BYPASSRLS")));
     825              :     }
     826              : 
     827              :     /* To add or drop members, you need ADMIN OPTION. */
     828          207 :     if (drolemembers && !is_admin_of_role(currentUserId, roleid))
     829            6 :         ereport(ERROR,
     830              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     831              :                  errmsg("permission denied to alter role"),
     832              :                  errdetail("Only roles with the %s option on role \"%s\" may add or drop members.",
     833              :                            "ADMIN", rolename)));
     834              : 
     835              :     /* Convert validuntil to internal form */
     836          201 :     if (dvalidUntil)
     837              :     {
     838            0 :         validUntil_datum = DirectFunctionCall3(timestamptz_in,
     839              :                                                CStringGetDatum(validUntil),
     840              :                                                ObjectIdGetDatum(InvalidOid),
     841              :                                                Int32GetDatum(-1));
     842            0 :         validUntil_null = false;
     843              :     }
     844              :     else
     845              :     {
     846              :         /* fetch existing setting in case hook needs it */
     847          201 :         validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
     848              :                                            Anum_pg_authid_rolvaliduntil,
     849              :                                            &validUntil_null);
     850              :     }
     851              : 
     852              :     /*
     853              :      * Call the password checking hook if there is one defined
     854              :      */
     855          201 :     if (check_password_hook && password)
     856            7 :         (*check_password_hook) (rolename,
     857              :                                 password,
     858              :                                 get_password_type(password),
     859              :                                 validUntil_datum,
     860              :                                 validUntil_null);
     861              : 
     862              :     /*
     863              :      * Build an updated tuple, perusing the information just obtained
     864              :      */
     865              : 
     866              :     /*
     867              :      * issuper/createrole/etc
     868              :      */
     869          197 :     if (dissuper)
     870              :     {
     871           49 :         bool        should_be_super = boolVal(dissuper->arg);
     872              : 
     873           49 :         if (!should_be_super && roleid == BOOTSTRAP_SUPERUSERID)
     874            0 :             ereport(ERROR,
     875              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     876              :                      errmsg("permission denied to alter role"),
     877              :                      errdetail("The bootstrap superuser must have the %s attribute.",
     878              :                                "SUPERUSER")));
     879              : 
     880           49 :         new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
     881           49 :         new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
     882              :     }
     883              : 
     884          197 :     if (dinherit)
     885              :     {
     886           39 :         new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
     887           39 :         new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
     888              :     }
     889              : 
     890          197 :     if (dcreaterole)
     891              :     {
     892           33 :         new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
     893           33 :         new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
     894              :     }
     895              : 
     896          197 :     if (dcreatedb)
     897              :     {
     898           39 :         new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
     899           39 :         new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
     900              :     }
     901              : 
     902          197 :     if (dcanlogin)
     903              :     {
     904           42 :         new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
     905           42 :         new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
     906              :     }
     907              : 
     908          197 :     if (disreplication)
     909              :     {
     910           65 :         new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
     911           65 :         new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
     912              :     }
     913              : 
     914          197 :     if (dconnlimit)
     915              :     {
     916            3 :         new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
     917            3 :         new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
     918              :     }
     919              : 
     920              :     /* password */
     921          197 :     if (password)
     922              :     {
     923              :         char       *shadow_pass;
     924           41 :         const char *logdetail = NULL;
     925              : 
     926              :         /* Like in CREATE USER, don't allow an empty password. */
     927           82 :         if (password[0] == '\0' ||
     928           41 :             plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
     929              :         {
     930            6 :             ereport(NOTICE,
     931              :                     (errmsg("empty string is not a valid password, clearing password")));
     932            6 :             new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     933              :         }
     934              :         else
     935              :         {
     936              :             /* Encrypt the password to the requested format. */
     937           35 :             shadow_pass = encrypt_password(Password_encryption, rolename,
     938              :                                            password);
     939           32 :             new_record[Anum_pg_authid_rolpassword - 1] =
     940           32 :                 CStringGetTextDatum(shadow_pass);
     941              :         }
     942           38 :         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
     943              :     }
     944              : 
     945              :     /* unset password */
     946          194 :     if (dpassword && dpassword->arg == NULL)
     947              :     {
     948            0 :         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
     949            0 :         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     950              :     }
     951              : 
     952              :     /* valid until */
     953          194 :     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
     954          194 :     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
     955          194 :     new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
     956              : 
     957          194 :     if (dbypassRLS)
     958              :     {
     959           39 :         new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
     960           39 :         new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
     961              :     }
     962              : 
     963          194 :     new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
     964              :                                   new_record_nulls, new_record_repl);
     965          194 :     CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
     966              : 
     967          194 :     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
     968              : 
     969          194 :     ReleaseSysCache(tuple);
     970          194 :     heap_freetuple(new_tuple);
     971              : 
     972          194 :     InitGrantRoleOptions(&popt);
     973              : 
     974              :     /*
     975              :      * Advance command counter so we can see new record; else tests in
     976              :      * AddRoleMems may fail.
     977              :      */
     978          194 :     if (drolemembers)
     979              :     {
     980           15 :         List       *rolemembers = (List *) drolemembers->arg;
     981              : 
     982           15 :         CommandCounterIncrement();
     983              : 
     984           15 :         if (stmt->action == +1) /* add members to role */
     985            9 :             AddRoleMems(currentUserId, rolename, roleid,
     986              :                         rolemembers, roleSpecsToIds(rolemembers),
     987              :                         InvalidOid, &popt);
     988            6 :         else if (stmt->action == -1) /* drop members from role */
     989            6 :             DelRoleMems(currentUserId, rolename, roleid,
     990              :                         rolemembers, roleSpecsToIds(rolemembers),
     991              :                         InvalidOid, &popt, DROP_RESTRICT);
     992              :     }
     993              : 
     994              :     /*
     995              :      * Close pg_authid, but keep lock till commit.
     996              :      */
     997          194 :     table_close(pg_authid_rel, NoLock);
     998              : 
     999          194 :     return roleid;
    1000              : }
    1001              : 
    1002              : 
    1003              : /*
    1004              :  * ALTER ROLE ... SET
    1005              :  */
    1006              : Oid
    1007           46 : AlterRoleSet(AlterRoleSetStmt *stmt)
    1008              : {
    1009              :     HeapTuple   roletuple;
    1010              :     Form_pg_authid roleform;
    1011           46 :     Oid         databaseid = InvalidOid;
    1012           46 :     Oid         roleid = InvalidOid;
    1013              : 
    1014           46 :     if (stmt->role)
    1015              :     {
    1016           42 :         check_rolespec_name(stmt->role,
    1017           42 :                             _("Cannot alter reserved roles."));
    1018              : 
    1019           42 :         roletuple = get_rolespec_tuple(stmt->role);
    1020           38 :         roleform = (Form_pg_authid) GETSTRUCT(roletuple);
    1021           38 :         roleid = roleform->oid;
    1022              : 
    1023              :         /*
    1024              :          * Obtain a lock on the role and make sure it didn't go away in the
    1025              :          * meantime.
    1026              :          */
    1027           38 :         shdepLockAndCheckObject(AuthIdRelationId, roleid);
    1028              : 
    1029              :         /*
    1030              :          * To mess with a superuser you gotta be superuser; otherwise you need
    1031              :          * CREATEROLE plus admin option on the target role; unless you're just
    1032              :          * trying to change your own settings
    1033              :          */
    1034           38 :         if (roleform->rolsuper)
    1035              :         {
    1036           16 :             if (!superuser())
    1037            0 :                 ereport(ERROR,
    1038              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1039              :                          errmsg("permission denied to alter role"),
    1040              :                          errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
    1041              :                                    "SUPERUSER", "SUPERUSER")));
    1042              :         }
    1043              :         else
    1044              :         {
    1045           22 :             if ((!have_createrole_privilege() ||
    1046           17 :                  !is_admin_of_role(GetUserId(), roleid))
    1047            5 :                 && roleid != GetUserId())
    1048            0 :                 ereport(ERROR,
    1049              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1050              :                          errmsg("permission denied to alter role"),
    1051              :                          errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
    1052              :                                    "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
    1053              :         }
    1054              : 
    1055           38 :         ReleaseSysCache(roletuple);
    1056              :     }
    1057              : 
    1058              :     /* look up and lock the database, if specified */
    1059           42 :     if (stmt->database != NULL)
    1060              :     {
    1061            2 :         databaseid = get_database_oid(stmt->database, false);
    1062            2 :         shdepLockAndCheckObject(DatabaseRelationId, databaseid);
    1063              : 
    1064            2 :         if (!stmt->role)
    1065              :         {
    1066              :             /*
    1067              :              * If no role is specified, then this is effectively the same as
    1068              :              * ALTER DATABASE ... SET, so use the same permission check.
    1069              :              */
    1070            0 :             if (!object_ownercheck(DatabaseRelationId, databaseid, GetUserId()))
    1071            0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
    1072            0 :                                stmt->database);
    1073              :         }
    1074              :     }
    1075              : 
    1076           42 :     if (!stmt->role && !stmt->database)
    1077              :     {
    1078              :         /* Must be superuser to alter settings globally. */
    1079            4 :         if (!superuser())
    1080            0 :             ereport(ERROR,
    1081              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1082              :                      errmsg("permission denied to alter setting"),
    1083              :                      errdetail("Only roles with the %s attribute may alter settings globally.",
    1084              :                                "SUPERUSER")));
    1085              :     }
    1086              : 
    1087           42 :     AlterSetting(databaseid, roleid, stmt->setstmt);
    1088              : 
    1089           40 :     return roleid;
    1090              : }
    1091              : 
    1092              : 
    1093              : /*
    1094              :  * DROP ROLE
    1095              :  */
    1096              : void
    1097          884 : DropRole(DropRoleStmt *stmt)
    1098              : {
    1099              :     Relation    pg_authid_rel,
    1100              :                 pg_auth_members_rel;
    1101              :     ListCell   *item;
    1102          884 :     List       *role_oids = NIL;
    1103              : 
    1104          884 :     if (!have_createrole_privilege())
    1105            0 :         ereport(ERROR,
    1106              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1107              :                  errmsg("permission denied to drop role"),
    1108              :                  errdetail("Only roles with the %s attribute and the %s option on the target roles may drop roles.",
    1109              :                            "CREATEROLE", "ADMIN")));
    1110              : 
    1111              :     /*
    1112              :      * Scan the pg_authid relation to find the Oid of the role(s) to be
    1113              :      * deleted and perform preliminary permissions and sanity checks.
    1114              :      */
    1115          884 :     pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
    1116          884 :     pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock);
    1117              : 
    1118         1769 :     foreach(item, stmt->roles)
    1119              :     {
    1120          939 :         RoleSpec   *rolspec = lfirst(item);
    1121              :         char       *role;
    1122              :         HeapTuple   tuple,
    1123              :                     tmp_tuple;
    1124              :         Form_pg_authid roleform;
    1125              :         ScanKeyData scankey;
    1126              :         SysScanDesc sscan;
    1127              :         Oid         roleid;
    1128              : 
    1129          939 :         if (rolspec->roletype != ROLESPEC_CSTRING)
    1130            0 :             ereport(ERROR,
    1131              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1132              :                      errmsg("cannot use special role specifier in DROP ROLE")));
    1133          939 :         role = rolspec->rolename;
    1134              : 
    1135          939 :         tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
    1136          939 :         if (!HeapTupleIsValid(tuple))
    1137              :         {
    1138          150 :             if (!stmt->missing_ok)
    1139              :             {
    1140           45 :                 ereport(ERROR,
    1141              :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
    1142              :                          errmsg("role \"%s\" does not exist", role)));
    1143              :             }
    1144              :             else
    1145              :             {
    1146          105 :                 ereport(NOTICE,
    1147              :                         (errmsg("role \"%s\" does not exist, skipping",
    1148              :                                 role)));
    1149              :             }
    1150              : 
    1151          105 :             continue;
    1152              :         }
    1153              : 
    1154          789 :         roleform = (Form_pg_authid) GETSTRUCT(tuple);
    1155          789 :         roleid = roleform->oid;
    1156              : 
    1157          789 :         if (roleid == GetUserId())
    1158            3 :             ereport(ERROR,
    1159              :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1160              :                      errmsg("current user cannot be dropped")));
    1161          786 :         if (roleid == GetOuterUserId())
    1162            0 :             ereport(ERROR,
    1163              :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1164              :                      errmsg("current user cannot be dropped")));
    1165          786 :         if (roleid == GetSessionUserId())
    1166            0 :             ereport(ERROR,
    1167              :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1168              :                      errmsg("session user cannot be dropped")));
    1169              : 
    1170              :         /*
    1171              :          * For safety's sake, we allow createrole holders to drop ordinary
    1172              :          * roles but not superuser roles, and only if they also have ADMIN
    1173              :          * OPTION.
    1174              :          */
    1175          786 :         if (roleform->rolsuper && !superuser())
    1176            3 :             ereport(ERROR,
    1177              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1178              :                      errmsg("permission denied to drop role"),
    1179              :                      errdetail("Only roles with the %s attribute may drop roles with the %s attribute.",
    1180              :                                "SUPERUSER", "SUPERUSER")));
    1181          783 :         if (!is_admin_of_role(GetUserId(), roleid))
    1182            3 :             ereport(ERROR,
    1183              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1184              :                      errmsg("permission denied to drop role"),
    1185              :                      errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may drop this role.",
    1186              :                                "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
    1187              : 
    1188              :         /* DROP hook for the role being removed */
    1189          780 :         InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
    1190              : 
    1191              :         /* Don't leak the syscache tuple */
    1192          780 :         ReleaseSysCache(tuple);
    1193              : 
    1194              :         /*
    1195              :          * Lock the role, so nobody can add dependencies to her while we drop
    1196              :          * her.  We keep the lock until the end of transaction.
    1197              :          */
    1198          780 :         LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
    1199              : 
    1200              :         /*
    1201              :          * If there is a pg_auth_members entry that has one of the roles to be
    1202              :          * dropped as the roleid or member, it should be silently removed, but
    1203              :          * if there is a pg_auth_members entry that has one of the roles to be
    1204              :          * dropped as the grantor, the operation should fail.
    1205              :          *
    1206              :          * It's possible, however, that a single pg_auth_members entry could
    1207              :          * fall into multiple categories - e.g. the user could do "GRANT foo
    1208              :          * TO bar GRANTED BY baz" and then "DROP ROLE baz, bar". We want such
    1209              :          * an operation to succeed regardless of the order in which the
    1210              :          * to-be-dropped roles are passed to DROP ROLE.
    1211              :          *
    1212              :          * To make that work, we remove all pg_auth_members entries that can
    1213              :          * be silently removed in this loop, and then below we'll make a
    1214              :          * second pass over the list of roles to be removed and check for any
    1215              :          * remaining dependencies.
    1216              :          */
    1217          780 :         ScanKeyInit(&scankey,
    1218              :                     Anum_pg_auth_members_roleid,
    1219              :                     BTEqualStrategyNumber, F_OIDEQ,
    1220              :                     ObjectIdGetDatum(roleid));
    1221              : 
    1222          780 :         sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
    1223              :                                    true, NULL, 1, &scankey);
    1224              : 
    1225          905 :         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
    1226              :         {
    1227              :             Form_pg_auth_members authmem_form;
    1228              : 
    1229          125 :             authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
    1230          125 :             deleteSharedDependencyRecordsFor(AuthMemRelationId,
    1231              :                                              authmem_form->oid, 0);
    1232          125 :             CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
    1233              :         }
    1234              : 
    1235          780 :         systable_endscan(sscan);
    1236              : 
    1237          780 :         ScanKeyInit(&scankey,
    1238              :                     Anum_pg_auth_members_member,
    1239              :                     BTEqualStrategyNumber, F_OIDEQ,
    1240              :                     ObjectIdGetDatum(roleid));
    1241              : 
    1242          780 :         sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
    1243              :                                    true, NULL, 1, &scankey);
    1244              : 
    1245          914 :         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
    1246              :         {
    1247              :             Form_pg_auth_members authmem_form;
    1248              : 
    1249          134 :             authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
    1250          134 :             deleteSharedDependencyRecordsFor(AuthMemRelationId,
    1251              :                                              authmem_form->oid, 0);
    1252          134 :             CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
    1253              :         }
    1254              : 
    1255          780 :         systable_endscan(sscan);
    1256              : 
    1257              :         /*
    1258              :          * Advance command counter so that later iterations of this loop will
    1259              :          * see the changes already made.  This is essential if, for example,
    1260              :          * we are trying to drop both a role and one of its direct members ---
    1261              :          * we'll get an error if we try to delete the linking pg_auth_members
    1262              :          * tuple twice.  (We do not need a CCI between the two delete loops
    1263              :          * above, because it's not allowed for a role to directly contain
    1264              :          * itself.)
    1265              :          */
    1266          780 :         CommandCounterIncrement();
    1267              : 
    1268              :         /* Looks tentatively OK, add it to the list if not there yet. */
    1269          780 :         role_oids = list_append_unique_oid(role_oids, roleid);
    1270              :     }
    1271              : 
    1272              :     /*
    1273              :      * Second pass over the roles to be removed.
    1274              :      */
    1275         1545 :     foreach(item, role_oids)
    1276              :     {
    1277          777 :         Oid         roleid = lfirst_oid(item);
    1278              :         HeapTuple   tuple;
    1279              :         Form_pg_authid roleform;
    1280              :         char       *detail;
    1281              :         char       *detail_log;
    1282              : 
    1283              :         /*
    1284              :          * Re-find the pg_authid tuple.
    1285              :          *
    1286              :          * Since we've taken a lock on the role OID, it shouldn't be possible
    1287              :          * for the tuple to have been deleted -- or for that matter updated --
    1288              :          * unless the user is manually modifying the system catalogs.
    1289              :          */
    1290          777 :         tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
    1291          777 :         if (!HeapTupleIsValid(tuple))
    1292            0 :             elog(ERROR, "could not find tuple for role %u", roleid);
    1293          777 :         roleform = (Form_pg_authid) GETSTRUCT(tuple);
    1294              : 
    1295              :         /*
    1296              :          * Check for pg_shdepend entries depending on this role.
    1297              :          *
    1298              :          * This needs to happen after we've completed removing any
    1299              :          * pg_auth_members entries that can be removed silently, in order to
    1300              :          * avoid spurious failures. See notes above for more details.
    1301              :          */
    1302          777 :         if (checkSharedDependencies(AuthIdRelationId, roleid,
    1303              :                                     &detail, &detail_log))
    1304           62 :             ereport(ERROR,
    1305              :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    1306              :                      errmsg("role \"%s\" cannot be dropped because some objects depend on it",
    1307              :                             NameStr(roleform->rolname)),
    1308              :                      errdetail_internal("%s", detail),
    1309              :                      errdetail_log("%s", detail_log)));
    1310              : 
    1311              :         /*
    1312              :          * Remove the role from the pg_authid table
    1313              :          */
    1314          715 :         CatalogTupleDelete(pg_authid_rel, &tuple->t_self);
    1315              : 
    1316          715 :         ReleaseSysCache(tuple);
    1317              : 
    1318              :         /*
    1319              :          * Remove any comments or security labels on this role.
    1320              :          */
    1321          715 :         DeleteSharedComments(roleid, AuthIdRelationId);
    1322          715 :         DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
    1323              : 
    1324              :         /*
    1325              :          * Remove settings for this role.
    1326              :          */
    1327          715 :         DropSetting(InvalidOid, roleid);
    1328              :     }
    1329              : 
    1330              :     /*
    1331              :      * Now we can clean up; but keep locks until commit.
    1332              :      */
    1333          768 :     table_close(pg_auth_members_rel, NoLock);
    1334          768 :     table_close(pg_authid_rel, NoLock);
    1335          768 : }
    1336              : 
    1337              : /*
    1338              :  * Rename role
    1339              :  */
    1340              : ObjectAddress
    1341           15 : RenameRole(const char *oldname, const char *newname)
    1342              : {
    1343              :     HeapTuple   oldtuple,
    1344              :                 newtuple;
    1345              :     TupleDesc   dsc;
    1346              :     Relation    rel;
    1347              :     Datum       datum;
    1348              :     bool        isnull;
    1349              :     Datum       repl_val[Natts_pg_authid];
    1350              :     bool        repl_null[Natts_pg_authid];
    1351              :     bool        repl_repl[Natts_pg_authid];
    1352              :     int         i;
    1353              :     Oid         roleid;
    1354              :     ObjectAddress address;
    1355              :     Form_pg_authid authform;
    1356              : 
    1357              :     /* Report error if name has \n or \r character. */
    1358           15 :     if (strpbrk(newname, "\n\r"))
    1359            0 :         ereport(ERROR,
    1360              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1361              :                  errmsg("role name \"%s\" contains a newline or carriage return character", newname)));
    1362              : 
    1363           15 :     rel = table_open(AuthIdRelationId, RowExclusiveLock);
    1364           15 :     dsc = RelationGetDescr(rel);
    1365              : 
    1366           15 :     oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
    1367           15 :     if (!HeapTupleIsValid(oldtuple))
    1368            0 :         ereport(ERROR,
    1369              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1370              :                  errmsg("role \"%s\" does not exist", oldname)));
    1371              : 
    1372              :     /*
    1373              :      * XXX Client applications probably store the session user somewhere, so
    1374              :      * renaming it could cause confusion.  On the other hand, there may not be
    1375              :      * an actual problem besides a little confusion, so think about this and
    1376              :      * decide.  Same for SET ROLE ... we don't restrict renaming the current
    1377              :      * effective userid, though.
    1378              :      */
    1379              : 
    1380           15 :     authform = (Form_pg_authid) GETSTRUCT(oldtuple);
    1381           15 :     roleid = authform->oid;
    1382              : 
    1383           15 :     if (roleid == GetSessionUserId())
    1384            0 :         ereport(ERROR,
    1385              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1386              :                  errmsg("session user cannot be renamed")));
    1387           15 :     if (roleid == GetOuterUserId())
    1388            0 :         ereport(ERROR,
    1389              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1390              :                  errmsg("current user cannot be renamed")));
    1391              : 
    1392              :     /*
    1393              :      * Check that the user is not trying to rename a system role and not
    1394              :      * trying to rename a role into the reserved "pg_" namespace.
    1395              :      */
    1396           15 :     if (IsReservedName(NameStr(authform->rolname)))
    1397            0 :         ereport(ERROR,
    1398              :                 (errcode(ERRCODE_RESERVED_NAME),
    1399              :                  errmsg("role name \"%s\" is reserved",
    1400              :                         NameStr(authform->rolname)),
    1401              :                  errdetail("Role names starting with \"pg_\" are reserved.")));
    1402              : 
    1403           15 :     if (IsReservedName(newname))
    1404            0 :         ereport(ERROR,
    1405              :                 (errcode(ERRCODE_RESERVED_NAME),
    1406              :                  errmsg("role name \"%s\" is reserved",
    1407              :                         newname),
    1408              :                  errdetail("Role names starting with \"pg_\" are reserved.")));
    1409              : 
    1410              :     /*
    1411              :      * If built with appropriate switch, whine when regression-testing
    1412              :      * conventions for role names are violated.
    1413              :      */
    1414              : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
    1415              :     if (strncmp(newname, "regress_", 8) != 0)
    1416              :         elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
    1417              : #endif
    1418              : 
    1419              :     /* make sure the new name doesn't exist */
    1420           15 :     if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
    1421            0 :         ereport(ERROR,
    1422              :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    1423              :                  errmsg("role \"%s\" already exists", newname)));
    1424              : 
    1425              :     /*
    1426              :      * Only superusers can mess with superusers. Otherwise, a user with
    1427              :      * CREATEROLE can rename a role for which they have ADMIN OPTION.
    1428              :      */
    1429           15 :     if (authform->rolsuper)
    1430              :     {
    1431            3 :         if (!superuser())
    1432            0 :             ereport(ERROR,
    1433              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1434              :                      errmsg("permission denied to rename role"),
    1435              :                      errdetail("Only roles with the %s attribute may rename roles with the %s attribute.",
    1436              :                                "SUPERUSER", "SUPERUSER")));
    1437              :     }
    1438              :     else
    1439              :     {
    1440           12 :         if (!have_createrole_privilege() ||
    1441           12 :             !is_admin_of_role(GetUserId(), roleid))
    1442            3 :             ereport(ERROR,
    1443              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1444              :                      errmsg("permission denied to rename role"),
    1445              :                      errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may rename this role.",
    1446              :                                "CREATEROLE", "ADMIN", NameStr(authform->rolname))));
    1447              :     }
    1448              : 
    1449              :     /* OK, construct the modified tuple */
    1450          156 :     for (i = 0; i < Natts_pg_authid; i++)
    1451          144 :         repl_repl[i] = false;
    1452              : 
    1453           12 :     repl_repl[Anum_pg_authid_rolname - 1] = true;
    1454           12 :     repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
    1455              :                                                                CStringGetDatum(newname));
    1456           12 :     repl_null[Anum_pg_authid_rolname - 1] = false;
    1457              : 
    1458           12 :     datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
    1459              : 
    1460           12 :     if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
    1461              :     {
    1462              :         /* MD5 uses the username as salt, so just clear it on a rename */
    1463            3 :         repl_repl[Anum_pg_authid_rolpassword - 1] = true;
    1464            3 :         repl_null[Anum_pg_authid_rolpassword - 1] = true;
    1465              : 
    1466            3 :         ereport(NOTICE,
    1467              :                 (errmsg("MD5 password cleared because of role rename")));
    1468              :     }
    1469              : 
    1470           12 :     newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
    1471           12 :     CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
    1472              : 
    1473           12 :     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
    1474              : 
    1475           12 :     ObjectAddressSet(address, AuthIdRelationId, roleid);
    1476              : 
    1477           12 :     ReleaseSysCache(oldtuple);
    1478              : 
    1479              :     /*
    1480              :      * Close pg_authid, but keep lock till commit.
    1481              :      */
    1482           12 :     table_close(rel, NoLock);
    1483              : 
    1484           12 :     return address;
    1485              : }
    1486              : 
    1487              : /*
    1488              :  * GrantRoleStmt
    1489              :  *
    1490              :  * Grant/Revoke roles to/from roles
    1491              :  */
    1492              : void
    1493          321 : GrantRole(ParseState *pstate, GrantRoleStmt *stmt)
    1494              : {
    1495              :     Relation    pg_authid_rel;
    1496              :     Oid         grantor;
    1497              :     List       *grantee_ids;
    1498              :     ListCell   *item;
    1499              :     GrantRoleOptions popt;
    1500          321 :     Oid         currentUserId = GetUserId();
    1501              : 
    1502              :     /* Parse options list. */
    1503          321 :     InitGrantRoleOptions(&popt);
    1504          503 :     foreach(item, stmt->opt)
    1505              :     {
    1506          182 :         DefElem    *opt = (DefElem *) lfirst(item);
    1507          182 :         char       *optval = defGetString(opt);
    1508              : 
    1509          182 :         if (strcmp(opt->defname, "admin") == 0)
    1510              :         {
    1511           94 :             popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
    1512              : 
    1513           94 :             if (parse_bool(optval, &popt.admin))
    1514           94 :                 continue;
    1515              :         }
    1516           88 :         else if (strcmp(opt->defname, "inherit") == 0)
    1517              :         {
    1518           47 :             popt.specified |= GRANT_ROLE_SPECIFIED_INHERIT;
    1519           47 :             if (parse_bool(optval, &popt.inherit))
    1520           47 :                 continue;
    1521              :         }
    1522           41 :         else if (strcmp(opt->defname, "set") == 0)
    1523              :         {
    1524           41 :             popt.specified |= GRANT_ROLE_SPECIFIED_SET;
    1525           41 :             if (parse_bool(optval, &popt.set))
    1526           41 :                 continue;
    1527              :         }
    1528              :         else
    1529            0 :             ereport(ERROR,
    1530              :                     errcode(ERRCODE_SYNTAX_ERROR),
    1531              :                     errmsg("unrecognized role option \"%s\"", opt->defname),
    1532              :                     parser_errposition(pstate, opt->location));
    1533              : 
    1534            0 :         ereport(ERROR,
    1535              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1536              :                  errmsg("unrecognized value for role option \"%s\": \"%s\"",
    1537              :                         opt->defname, optval),
    1538              :                  parser_errposition(pstate, opt->location)));
    1539              :     }
    1540              : 
    1541              :     /* Lookup OID of grantor, if specified. */
    1542          321 :     if (stmt->grantor)
    1543           60 :         grantor = get_rolespec_oid(stmt->grantor, false);
    1544              :     else
    1545          261 :         grantor = InvalidOid;
    1546              : 
    1547          318 :     grantee_ids = roleSpecsToIds(stmt->grantee_roles);
    1548              : 
    1549              :     /* AccessShareLock is enough since we aren't modifying pg_authid */
    1550          318 :     pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
    1551              : 
    1552              :     /*
    1553              :      * Step through all of the granted roles and add, update, or remove
    1554              :      * entries in pg_auth_members as appropriate. If stmt->is_grant is true,
    1555              :      * we are adding new grants or, if they already exist, updating options on
    1556              :      * those grants. If stmt->is_grant is false, we are revoking grants or
    1557              :      * removing options from them.
    1558              :      */
    1559          577 :     foreach(item, stmt->granted_roles)
    1560              :     {
    1561          319 :         AccessPriv *priv = (AccessPriv *) lfirst(item);
    1562          319 :         char       *rolename = priv->priv_name;
    1563              :         Oid         roleid;
    1564              : 
    1565              :         /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
    1566          319 :         if (rolename == NULL || priv->cols != NIL)
    1567            0 :             ereport(ERROR,
    1568              :                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1569              :                      errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
    1570              : 
    1571          319 :         roleid = get_role_oid(rolename, false);
    1572          319 :         check_role_membership_authorization(currentUserId,
    1573          319 :                                             roleid, stmt->is_grant);
    1574          274 :         if (stmt->is_grant)
    1575          199 :             AddRoleMems(currentUserId, rolename, roleid,
    1576              :                         stmt->grantee_roles, grantee_ids,
    1577              :                         grantor, &popt);
    1578              :         else
    1579           75 :             DelRoleMems(currentUserId, rolename, roleid,
    1580              :                         stmt->grantee_roles, grantee_ids,
    1581              :                         grantor, &popt, stmt->behavior);
    1582              :     }
    1583              : 
    1584              :     /*
    1585              :      * Close pg_authid, but keep lock till commit.
    1586              :      */
    1587          258 :     table_close(pg_authid_rel, NoLock);
    1588          258 : }
    1589              : 
    1590              : /*
    1591              :  * DropOwnedObjects
    1592              :  *
    1593              :  * Drop the objects owned by a given list of roles.
    1594              :  */
    1595              : void
    1596           74 : DropOwnedObjects(DropOwnedStmt *stmt)
    1597              : {
    1598           74 :     List       *role_ids = roleSpecsToIds(stmt->roles);
    1599              :     ListCell   *cell;
    1600              : 
    1601              :     /* Check privileges */
    1602          157 :     foreach(cell, role_ids)
    1603              :     {
    1604           89 :         Oid         roleid = lfirst_oid(cell);
    1605              : 
    1606           89 :         if (!has_privs_of_role(GetUserId(), roleid))
    1607            6 :             ereport(ERROR,
    1608              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1609              :                      errmsg("permission denied to drop objects"),
    1610              :                      errdetail("Only roles with privileges of role \"%s\" may drop objects owned by it.",
    1611              :                                GetUserNameFromId(roleid, false))));
    1612              :     }
    1613              : 
    1614              :     /* Ok, do it */
    1615           68 :     shdepDropOwned(role_ids, stmt->behavior);
    1616           65 : }
    1617              : 
    1618              : /*
    1619              :  * ReassignOwnedObjects
    1620              :  *
    1621              :  * Give the objects owned by a given list of roles away to another user.
    1622              :  */
    1623              : void
    1624           23 : ReassignOwnedObjects(ReassignOwnedStmt *stmt)
    1625              : {
    1626           23 :     List       *role_ids = roleSpecsToIds(stmt->roles);
    1627              :     ListCell   *cell;
    1628              :     Oid         newrole;
    1629              : 
    1630              :     /* Check privileges */
    1631           40 :     foreach(cell, role_ids)
    1632              :     {
    1633           23 :         Oid         roleid = lfirst_oid(cell);
    1634              : 
    1635           23 :         if (!has_privs_of_role(GetUserId(), roleid))
    1636            6 :             ereport(ERROR,
    1637              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1638              :                      errmsg("permission denied to reassign objects"),
    1639              :                      errdetail("Only roles with privileges of role \"%s\" may reassign objects owned by it.",
    1640              :                                GetUserNameFromId(roleid, false))));
    1641              :     }
    1642              : 
    1643              :     /* Must have privileges on the receiving side too */
    1644           17 :     newrole = get_rolespec_oid(stmt->newrole, false);
    1645              : 
    1646           17 :     if (!has_privs_of_role(GetUserId(), newrole))
    1647            3 :         ereport(ERROR,
    1648              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1649              :                  errmsg("permission denied to reassign objects"),
    1650              :                  errdetail("Only roles with privileges of role \"%s\" may reassign objects to it.",
    1651              :                            GetUserNameFromId(newrole, false))));
    1652              : 
    1653              :     /* Ok, do it */
    1654           14 :     shdepReassignOwned(role_ids, newrole);
    1655           14 : }
    1656              : 
    1657              : /*
    1658              :  * roleSpecsToIds
    1659              :  *
    1660              :  * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
    1661              :  *
    1662              :  * ROLESPEC_PUBLIC is not allowed.
    1663              :  */
    1664              : List *
    1665         2264 : roleSpecsToIds(List *memberNames)
    1666              : {
    1667         2264 :     List       *result = NIL;
    1668              :     ListCell   *l;
    1669              : 
    1670         2778 :     foreach(l, memberNames)
    1671              :     {
    1672          514 :         RoleSpec   *rolespec = lfirst_node(RoleSpec, l);
    1673              :         Oid         roleid;
    1674              : 
    1675          514 :         roleid = get_rolespec_oid(rolespec, false);
    1676          514 :         result = lappend_oid(result, roleid);
    1677              :     }
    1678         2264 :     return result;
    1679              : }
    1680              : 
    1681              : /*
    1682              :  * AddRoleMems -- Add given members to the specified role
    1683              :  *
    1684              :  * currentUserId: OID of role performing the operation
    1685              :  * rolename: name of role to add to (used only for error messages)
    1686              :  * roleid: OID of role to add to
    1687              :  * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
    1688              :  * memberIds: OIDs of roles to add
    1689              :  * grantorId: OID that should be recorded as having granted the membership
    1690              :  * (InvalidOid if not set explicitly)
    1691              :  * popt: information about grant options
    1692              :  */
    1693              : static void
    1694         2104 : AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
    1695              :             List *memberSpecs, List *memberIds,
    1696              :             Oid grantorId, GrantRoleOptions *popt)
    1697              : {
    1698              :     Relation    pg_authmem_rel;
    1699              :     TupleDesc   pg_authmem_dsc;
    1700              :     ListCell   *specitem;
    1701              :     ListCell   *iditem;
    1702              : 
    1703              :     Assert(list_length(memberSpecs) == list_length(memberIds));
    1704              : 
    1705              :     /* Validate grantor (and resolve implicit grantor if not specified). */
    1706         2104 :     grantorId = check_role_grantor(currentUserId, roleid, grantorId, true);
    1707              : 
    1708         2101 :     pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
    1709         2101 :     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
    1710              : 
    1711              :     /*
    1712              :      * Only allow changes to this role by one backend at a time, so that we
    1713              :      * can check integrity constraints like the lack of circular ADMIN OPTION
    1714              :      * grants without fear of race conditions.
    1715              :      */
    1716         2101 :     LockSharedObject(AuthIdRelationId, roleid, 0,
    1717              :                      ShareUpdateExclusiveLock);
    1718              : 
    1719              :     /* Preliminary sanity checks. */
    1720         2443 :     forboth(specitem, memberSpecs, iditem, memberIds)
    1721              :     {
    1722          351 :         RoleSpec   *memberRole = lfirst_node(RoleSpec, specitem);
    1723          351 :         Oid         memberid = lfirst_oid(iditem);
    1724              : 
    1725              :         /*
    1726              :          * pg_database_owner is never a role member.  Lifting this restriction
    1727              :          * would require a policy decision about membership loops.  One could
    1728              :          * prevent loops, which would include making "ALTER DATABASE x OWNER
    1729              :          * TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
    1730              :          * proposed_datdba).  Hence, gaining a membership could reduce what a
    1731              :          * role could do.  Alternately, one could allow these memberships to
    1732              :          * complete loops.  A role could then have actual WITH ADMIN OPTION on
    1733              :          * itself, prompting a decision about is_admin_of_role() treatment of
    1734              :          * the case.
    1735              :          *
    1736              :          * Lifting this restriction also has policy implications for ownership
    1737              :          * of shared objects (databases and tablespaces).  We allow such
    1738              :          * ownership, but we might find cause to ban it in the future.
    1739              :          * Designing such a ban would more troublesome if the design had to
    1740              :          * address pg_database_owner being a member of role FOO that owns a
    1741              :          * shared object.  (The effect of such ownership is that any owner of
    1742              :          * another database can act as the owner of affected shared objects.)
    1743              :          */
    1744          351 :         if (memberid == ROLE_PG_DATABASE_OWNER)
    1745            3 :             ereport(ERROR,
    1746              :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1747              :                     errmsg("role \"%s\" cannot be a member of any role",
    1748              :                            get_rolespec_name(memberRole)));
    1749              : 
    1750              :         /*
    1751              :          * Refuse creation of membership loops, including the trivial case
    1752              :          * where a role is made a member of itself.  We do this by checking to
    1753              :          * see if the target role is already a member of the proposed member
    1754              :          * role.  We have to ignore possible superuserness, however, else we
    1755              :          * could never grant membership in a superuser-privileged role.
    1756              :          */
    1757          348 :         if (is_member_of_role_nosuper(roleid, memberid))
    1758            6 :             ereport(ERROR,
    1759              :                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1760              :                      errmsg("role \"%s\" is a member of role \"%s\"",
    1761              :                             rolename, get_rolespec_name(memberRole))));
    1762              :     }
    1763              : 
    1764              :     /*
    1765              :      * Disallow attempts to grant ADMIN OPTION back to a user who granted it
    1766              :      * to you, similar to what check_circularity does for ACLs. We want the
    1767              :      * chains of grants to remain acyclic, so that it's always possible to use
    1768              :      * REVOKE .. CASCADE to clean up all grants that depend on the one being
    1769              :      * revoked.
    1770              :      *
    1771              :      * NB: This check might look redundant with the check for membership loops
    1772              :      * above, but it isn't. That's checking for role-member loop (e.g. A is a
    1773              :      * member of B and B is a member of A) while this is checking for a
    1774              :      * member-grantor loop (e.g. A gave ADMIN OPTION on X to B and now B, who
    1775              :      * has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION on
    1776              :      * X back to A).
    1777              :      */
    1778         2092 :     if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID)
    1779              :     {
    1780              :         CatCList   *memlist;
    1781              :         RevokeRoleGrantAction *actions;
    1782              :         int         i;
    1783              : 
    1784              :         /* Get the list of members for this role. */
    1785           72 :         memlist = SearchSysCacheList1(AUTHMEMROLEMEM,
    1786              :                                       ObjectIdGetDatum(roleid));
    1787              : 
    1788              :         /*
    1789              :          * Figure out what would happen if we removed all existing grants to
    1790              :          * every role to which we've been asked to make a new grant.
    1791              :          */
    1792           72 :         actions = initialize_revoke_actions(memlist);
    1793          114 :         foreach(iditem, memberIds)
    1794              :         {
    1795           42 :             Oid         memberid = lfirst_oid(iditem);
    1796              : 
    1797           42 :             if (memberid == BOOTSTRAP_SUPERUSERID)
    1798            0 :                 ereport(ERROR,
    1799              :                         (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1800              :                          errmsg("%s option cannot be granted back to your own grantor",
    1801              :                                 "ADMIN")));
    1802           42 :             plan_member_revoke(memlist, actions, memberid);
    1803              :         }
    1804              : 
    1805              :         /*
    1806              :          * If the result would be that the grantor role would no longer have
    1807              :          * the ability to perform the grant, then the proposed grant would
    1808              :          * create a circularity.
    1809              :          */
    1810           87 :         for (i = 0; i < memlist->n_members; ++i)
    1811              :         {
    1812              :             HeapTuple   authmem_tuple;
    1813              :             Form_pg_auth_members authmem_form;
    1814              : 
    1815           84 :             authmem_tuple = &memlist->members[i]->tuple;
    1816           84 :             authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    1817              : 
    1818           84 :             if (actions[i] == RRG_NOOP &&
    1819           78 :                 authmem_form->member == grantorId &&
    1820           69 :                 authmem_form->admin_option)
    1821           69 :                 break;
    1822              :         }
    1823           72 :         if (i >= memlist->n_members)
    1824            3 :             ereport(ERROR,
    1825              :                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1826              :                      errmsg("%s option cannot be granted back to your own grantor",
    1827              :                             "ADMIN")));
    1828              : 
    1829           69 :         ReleaseSysCacheList(memlist);
    1830              :     }
    1831              : 
    1832              :     /* Now perform the catalog updates. */
    1833         2428 :     forboth(specitem, memberSpecs, iditem, memberIds)
    1834              :     {
    1835          339 :         RoleSpec   *memberRole = lfirst_node(RoleSpec, specitem);
    1836          339 :         Oid         memberid = lfirst_oid(iditem);
    1837              :         HeapTuple   authmem_tuple;
    1838              :         HeapTuple   tuple;
    1839          339 :         Datum       new_record[Natts_pg_auth_members] = {0};
    1840          339 :         bool        new_record_nulls[Natts_pg_auth_members] = {0};
    1841          339 :         bool        new_record_repl[Natts_pg_auth_members] = {0};
    1842              : 
    1843              :         /* Common initialization for possible insert or update */
    1844          339 :         new_record[Anum_pg_auth_members_roleid - 1] =
    1845          339 :             ObjectIdGetDatum(roleid);
    1846          339 :         new_record[Anum_pg_auth_members_member - 1] =
    1847          339 :             ObjectIdGetDatum(memberid);
    1848          339 :         new_record[Anum_pg_auth_members_grantor - 1] =
    1849          339 :             ObjectIdGetDatum(grantorId);
    1850              : 
    1851              :         /* Find any existing tuple */
    1852          339 :         authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
    1853              :                                         ObjectIdGetDatum(roleid),
    1854              :                                         ObjectIdGetDatum(memberid),
    1855              :                                         ObjectIdGetDatum(grantorId));
    1856              : 
    1857              :         /*
    1858              :          * If we found a tuple, update it with new option values, unless there
    1859              :          * are no changes, in which case issue a WARNING.
    1860              :          *
    1861              :          * If we didn't find a tuple, just insert one.
    1862              :          */
    1863          339 :         if (HeapTupleIsValid(authmem_tuple))
    1864              :         {
    1865              :             Form_pg_auth_members authmem_form;
    1866           17 :             bool        at_least_one_change = false;
    1867              : 
    1868           17 :             authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    1869              : 
    1870           17 :             if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0
    1871            0 :                 && authmem_form->admin_option != popt->admin)
    1872              :             {
    1873            0 :                 new_record[Anum_pg_auth_members_admin_option - 1] =
    1874            0 :                     BoolGetDatum(popt->admin);
    1875            0 :                 new_record_repl[Anum_pg_auth_members_admin_option - 1] =
    1876              :                     true;
    1877            0 :                 at_least_one_change = true;
    1878              :             }
    1879              : 
    1880           17 :             if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0
    1881            8 :                 && authmem_form->inherit_option != popt->inherit)
    1882              :             {
    1883            8 :                 new_record[Anum_pg_auth_members_inherit_option - 1] =
    1884            8 :                     BoolGetDatum(popt->inherit);
    1885            8 :                 new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
    1886              :                     true;
    1887            8 :                 at_least_one_change = true;
    1888              :             }
    1889              : 
    1890           17 :             if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0
    1891            5 :                 && authmem_form->set_option != popt->set)
    1892              :             {
    1893            5 :                 new_record[Anum_pg_auth_members_set_option - 1] =
    1894            5 :                     BoolGetDatum(popt->set);
    1895            5 :                 new_record_repl[Anum_pg_auth_members_set_option - 1] =
    1896              :                     true;
    1897            5 :                 at_least_one_change = true;
    1898              :             }
    1899              : 
    1900           17 :             if (!at_least_one_change)
    1901              :             {
    1902            9 :                 ereport(NOTICE,
    1903              :                         (errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"",
    1904              :                                 get_rolespec_name(memberRole), rolename,
    1905              :                                 GetUserNameFromId(grantorId, false))));
    1906            9 :                 ReleaseSysCache(authmem_tuple);
    1907            9 :                 continue;
    1908              :             }
    1909              : 
    1910            8 :             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
    1911              :                                       new_record,
    1912              :                                       new_record_nulls, new_record_repl);
    1913            8 :             CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
    1914              : 
    1915            8 :             ReleaseSysCache(authmem_tuple);
    1916              :         }
    1917              :         else
    1918              :         {
    1919              :             Oid         objectId;
    1920          322 :             Oid        *newmembers = palloc_object(Oid);
    1921              : 
    1922              :             /*
    1923              :              * The values for these options can be taken directly from 'popt'.
    1924              :              * Either they were specified, or the defaults as set by
    1925              :              * InitGrantRoleOptions are correct.
    1926              :              */
    1927          322 :             new_record[Anum_pg_auth_members_admin_option - 1] =
    1928          322 :                 BoolGetDatum(popt->admin);
    1929          322 :             new_record[Anum_pg_auth_members_set_option - 1] =
    1930          322 :                 BoolGetDatum(popt->set);
    1931              : 
    1932              :             /*
    1933              :              * If the user specified a value for the inherit option, use
    1934              :              * whatever was specified. Otherwise, set the default value based
    1935              :              * on the role-level property.
    1936              :              */
    1937          322 :             if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
    1938           97 :                 new_record[Anum_pg_auth_members_inherit_option - 1] =
    1939           97 :                     BoolGetDatum(popt->inherit);
    1940              :             else
    1941              :             {
    1942              :                 HeapTuple   mrtup;
    1943              :                 Form_pg_authid mrform;
    1944              : 
    1945          225 :                 mrtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(memberid));
    1946          225 :                 if (!HeapTupleIsValid(mrtup))
    1947            0 :                     elog(ERROR, "cache lookup failed for role %u", memberid);
    1948          225 :                 mrform = (Form_pg_authid) GETSTRUCT(mrtup);
    1949          225 :                 new_record[Anum_pg_auth_members_inherit_option - 1] =
    1950          225 :                     BoolGetDatum(mrform->rolinherit);
    1951          225 :                 ReleaseSysCache(mrtup);
    1952              :             }
    1953              : 
    1954              :             /* get an OID for the new row and insert it */
    1955          322 :             objectId = GetNewOidWithIndex(pg_authmem_rel, AuthMemOidIndexId,
    1956              :                                           Anum_pg_auth_members_oid);
    1957          322 :             new_record[Anum_pg_auth_members_oid - 1] = ObjectIdGetDatum(objectId);
    1958          322 :             tuple = heap_form_tuple(pg_authmem_dsc,
    1959              :                                     new_record, new_record_nulls);
    1960          322 :             CatalogTupleInsert(pg_authmem_rel, tuple);
    1961              : 
    1962              :             /* updateAclDependencies wants to pfree array inputs */
    1963          322 :             newmembers[0] = grantorId;
    1964          322 :             updateAclDependencies(AuthMemRelationId, objectId,
    1965              :                                   0, InvalidOid,
    1966              :                                   0, NULL,
    1967              :                                   1, newmembers);
    1968              :         }
    1969              : 
    1970              :         /* CCI after each change, in case there are duplicates in list */
    1971          330 :         CommandCounterIncrement();
    1972              :     }
    1973              : 
    1974              :     /*
    1975              :      * Close pg_authmem, but keep lock till commit.
    1976              :      */
    1977         2089 :     table_close(pg_authmem_rel, NoLock);
    1978         2089 : }
    1979              : 
    1980              : /*
    1981              :  * DelRoleMems -- Remove given members from the specified role
    1982              :  *
    1983              :  * rolename: name of role to del from (used only for error messages)
    1984              :  * roleid: OID of role to del from
    1985              :  * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
    1986              :  * memberIds: OIDs of roles to del
    1987              :  * grantorId: who is revoking the membership
    1988              :  * popt: information about grant options
    1989              :  * behavior: RESTRICT or CASCADE behavior for recursive removal
    1990              :  */
    1991              : static void
    1992           81 : DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
    1993              :             List *memberSpecs, List *memberIds,
    1994              :             Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior)
    1995              : {
    1996              :     Relation    pg_authmem_rel;
    1997              :     TupleDesc   pg_authmem_dsc;
    1998              :     ListCell   *specitem;
    1999              :     ListCell   *iditem;
    2000              :     CatCList   *memlist;
    2001              :     RevokeRoleGrantAction *actions;
    2002              :     int         i;
    2003              : 
    2004              :     Assert(list_length(memberSpecs) == list_length(memberIds));
    2005              : 
    2006              :     /* Validate grantor (and resolve implicit grantor if not specified). */
    2007           81 :     grantorId = check_role_grantor(currentUserId, roleid, grantorId, false);
    2008              : 
    2009           81 :     pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
    2010           81 :     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
    2011              : 
    2012              :     /*
    2013              :      * Only allow changes to this role by one backend at a time, so that we
    2014              :      * can check for things like dependent privileges without fear of race
    2015              :      * conditions.
    2016              :      */
    2017           81 :     LockSharedObject(AuthIdRelationId, roleid, 0,
    2018              :                      ShareUpdateExclusiveLock);
    2019              : 
    2020           81 :     memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid));
    2021           81 :     actions = initialize_revoke_actions(memlist);
    2022              : 
    2023              :     /*
    2024              :      * We may need to recurse to dependent privileges if DROP_CASCADE was
    2025              :      * specified, or refuse to perform the operation if dependent privileges
    2026              :      * exist and DROP_RESTRICT was specified. plan_single_revoke() will figure
    2027              :      * out what to do with each catalog tuple.
    2028              :      */
    2029          156 :     forboth(specitem, memberSpecs, iditem, memberIds)
    2030              :     {
    2031           81 :         RoleSpec   *memberRole = lfirst(specitem);
    2032           81 :         Oid         memberid = lfirst_oid(iditem);
    2033              : 
    2034           81 :         if (!plan_single_revoke(memlist, actions, memberid, grantorId,
    2035              :                                 popt, behavior))
    2036              :         {
    2037            3 :             ereport(WARNING,
    2038              :                     (errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"",
    2039              :                             get_rolespec_name(memberRole), rolename,
    2040              :                             GetUserNameFromId(grantorId, false))));
    2041            3 :             continue;
    2042              :         }
    2043              :     }
    2044              : 
    2045              :     /*
    2046              :      * We now know what to do with each catalog tuple: it should either be
    2047              :      * left alone, deleted, or just have the admin_option flag cleared.
    2048              :      * Perform the appropriate action in each case.
    2049              :      */
    2050          218 :     for (i = 0; i < memlist->n_members; ++i)
    2051              :     {
    2052              :         HeapTuple   authmem_tuple;
    2053              :         Form_pg_auth_members authmem_form;
    2054              : 
    2055          143 :         if (actions[i] == RRG_NOOP)
    2056           62 :             continue;
    2057              : 
    2058           81 :         authmem_tuple = &memlist->members[i]->tuple;
    2059           81 :         authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    2060              : 
    2061           81 :         if (actions[i] == RRG_DELETE_GRANT)
    2062              :         {
    2063              :             /*
    2064              :              * Remove the entry altogether, after first removing its
    2065              :              * dependencies
    2066              :              */
    2067           57 :             deleteSharedDependencyRecordsFor(AuthMemRelationId,
    2068              :                                              authmem_form->oid, 0);
    2069           57 :             CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
    2070              :         }
    2071              :         else
    2072              :         {
    2073              :             /* Just turn off the specified option */
    2074              :             HeapTuple   tuple;
    2075           24 :             Datum       new_record[Natts_pg_auth_members] = {0};
    2076           24 :             bool        new_record_nulls[Natts_pg_auth_members] = {0};
    2077           24 :             bool        new_record_repl[Natts_pg_auth_members] = {0};
    2078              : 
    2079              :             /* Build a tuple to update with */
    2080           24 :             if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
    2081              :             {
    2082           15 :                 new_record[Anum_pg_auth_members_admin_option - 1] =
    2083           15 :                     BoolGetDatum(false);
    2084           15 :                 new_record_repl[Anum_pg_auth_members_admin_option - 1] =
    2085              :                     true;
    2086              :             }
    2087            9 :             else if (actions[i] == RRG_REMOVE_INHERIT_OPTION)
    2088              :             {
    2089            6 :                 new_record[Anum_pg_auth_members_inherit_option - 1] =
    2090            6 :                     BoolGetDatum(false);
    2091            6 :                 new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
    2092              :                     true;
    2093              :             }
    2094            3 :             else if (actions[i] == RRG_REMOVE_SET_OPTION)
    2095              :             {
    2096            3 :                 new_record[Anum_pg_auth_members_set_option - 1] =
    2097            3 :                     BoolGetDatum(false);
    2098            3 :                 new_record_repl[Anum_pg_auth_members_set_option - 1] =
    2099              :                     true;
    2100              :             }
    2101              :             else
    2102            0 :                 elog(ERROR, "unknown role revoke action");
    2103              : 
    2104           24 :             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
    2105              :                                       new_record,
    2106              :                                       new_record_nulls, new_record_repl);
    2107           24 :             CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
    2108              :         }
    2109              :     }
    2110              : 
    2111           75 :     ReleaseSysCacheList(memlist);
    2112              : 
    2113              :     /*
    2114              :      * Close pg_authmem, but keep lock till commit.
    2115              :      */
    2116           75 :     table_close(pg_authmem_rel, NoLock);
    2117           75 : }
    2118              : 
    2119              : /*
    2120              :  * Check that currentUserId has permission to modify the membership list for
    2121              :  * roleid. Throw an error if not.
    2122              :  */
    2123              : static void
    2124          369 : check_role_membership_authorization(Oid currentUserId, Oid roleid,
    2125              :                                     bool is_grant)
    2126              : {
    2127              :     /*
    2128              :      * The charter of pg_database_owner is to have exactly one, implicit,
    2129              :      * situation-dependent member.  There's no technical need for this
    2130              :      * restriction.  (One could lift it and take the further step of making
    2131              :      * object_ownercheck(DatabaseRelationId, ...) equivalent to
    2132              :      * has_privs_of_role(roleid, ROLE_PG_DATABASE_OWNER), in which case
    2133              :      * explicit, situation-independent members could act as the owner of any
    2134              :      * database.)
    2135              :      */
    2136          369 :     if (is_grant && roleid == ROLE_PG_DATABASE_OWNER)
    2137            6 :         ereport(ERROR,
    2138              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2139              :                 errmsg("role \"%s\" cannot have explicit members",
    2140              :                        GetUserNameFromId(roleid, false)));
    2141              : 
    2142              :     /* To mess with a superuser role, you gotta be superuser. */
    2143          363 :     if (superuser_arg(roleid))
    2144              :     {
    2145            8 :         if (!superuser_arg(currentUserId))
    2146              :         {
    2147            3 :             if (is_grant)
    2148            3 :                 ereport(ERROR,
    2149              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2150              :                          errmsg("permission denied to grant role \"%s\"",
    2151              :                                 GetUserNameFromId(roleid, false)),
    2152              :                          errdetail("Only roles with the %s attribute may grant roles with the %s attribute.",
    2153              :                                    "SUPERUSER", "SUPERUSER")));
    2154              :             else
    2155            0 :                 ereport(ERROR,
    2156              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2157              :                          errmsg("permission denied to revoke role \"%s\"",
    2158              :                                 GetUserNameFromId(roleid, false)),
    2159              :                          errdetail("Only roles with the %s attribute may revoke roles with the %s attribute.",
    2160              :                                    "SUPERUSER", "SUPERUSER")));
    2161              :         }
    2162              :     }
    2163              :     else
    2164              :     {
    2165              :         /*
    2166              :          * Otherwise, must have admin option on the role to be changed.
    2167              :          */
    2168          355 :         if (!is_admin_of_role(currentUserId, roleid))
    2169              :         {
    2170           72 :             if (is_grant)
    2171           72 :                 ereport(ERROR,
    2172              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2173              :                          errmsg("permission denied to grant role \"%s\"",
    2174              :                                 GetUserNameFromId(roleid, false)),
    2175              :                          errdetail("Only roles with the %s option on role \"%s\" may grant this role.",
    2176              :                                    "ADMIN", GetUserNameFromId(roleid, false))));
    2177              :             else
    2178            0 :                 ereport(ERROR,
    2179              :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2180              :                          errmsg("permission denied to revoke role \"%s\"",
    2181              :                                 GetUserNameFromId(roleid, false)),
    2182              :                          errdetail("Only roles with the %s option on role \"%s\" may revoke this role.",
    2183              :                                    "ADMIN", GetUserNameFromId(roleid, false))));
    2184              :         }
    2185              :     }
    2186          288 : }
    2187              : 
    2188              : /*
    2189              :  * Sanity-check, or infer, the grantor for a GRANT or REVOKE statement
    2190              :  * targeting a role.
    2191              :  *
    2192              :  * The grantor must always be either a role with ADMIN OPTION on the role in
    2193              :  * which membership is being granted, or the bootstrap superuser. This is
    2194              :  * similar to the restriction enforced by select_best_grantor, except that
    2195              :  * roles don't have owners, so we regard the bootstrap superuser as the
    2196              :  * implicit owner.
    2197              :  *
    2198              :  * If the grantor was not explicitly specified by the user, grantorId should
    2199              :  * be passed as InvalidOid, and this function will infer the user to be
    2200              :  * recorded as the grantor. In many cases, this will be the current user, but
    2201              :  * things get more complicated when the current user doesn't possess ADMIN
    2202              :  * OPTION on the role but rather relies on having SUPERUSER privileges, or
    2203              :  * on inheriting the privileges of a role which does have ADMIN OPTION. See
    2204              :  * below for details.
    2205              :  *
    2206              :  * If the grantor was specified by the user, then it must be a user that
    2207              :  * can legally be recorded as the grantor, as per the rule stated above.
    2208              :  * This is an integrity constraint, not a permissions check, and thus even
    2209              :  * superusers are subject to this restriction. However, there is also a
    2210              :  * permissions check: to specify a role as the grantor, the current user
    2211              :  * must possess the privileges of that role. Superusers will always pass
    2212              :  * this check, but for non-superusers it may lead to an error.
    2213              :  *
    2214              :  * The return value is the OID to be regarded as the grantor when executing
    2215              :  * the operation.
    2216              :  */
    2217              : static Oid
    2218         2185 : check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant)
    2219              : {
    2220              :     /* If the grantor ID was not specified, pick one to use. */
    2221         2185 :     if (!OidIsValid(grantorId))
    2222              :     {
    2223              :         /*
    2224              :          * Grants where the grantor is recorded as the bootstrap superuser do
    2225              :          * not depend on any other existing grants, so always default to this
    2226              :          * interpretation when possible.
    2227              :          */
    2228         2065 :         if (superuser_arg(currentUserId))
    2229         1897 :             return BOOTSTRAP_SUPERUSERID;
    2230              : 
    2231              :         /*
    2232              :          * Otherwise, the grantor must either have ADMIN OPTION on the role or
    2233              :          * inherit the privileges of a role which does. In the former case,
    2234              :          * record the grantor as the current user; in the latter, pick one of
    2235              :          * the roles that is "most directly" inherited by the current role
    2236              :          * (i.e. fewest "hops").
    2237              :          *
    2238              :          * (We shouldn't fail to find a best grantor, because we've already
    2239              :          * established that the current user has permission to perform the
    2240              :          * operation.)
    2241              :          */
    2242          168 :         grantorId = select_best_admin(currentUserId, roleid);
    2243          168 :         if (!OidIsValid(grantorId))
    2244            0 :             elog(ERROR, "no possible grantors");
    2245          168 :         return grantorId;
    2246              :     }
    2247              : 
    2248              :     /*
    2249              :      * If an explicit grantor is specified, it must be a role whose privileges
    2250              :      * the current user possesses.
    2251              :      *
    2252              :      * It should also be a role that has ADMIN OPTION on the target role, but
    2253              :      * we check this condition only in case of GRANT. For REVOKE, no matching
    2254              :      * grant should exist anyway, but if it somehow does, let the user get rid
    2255              :      * of it.
    2256              :      */
    2257          120 :     if (is_grant)
    2258              :     {
    2259          108 :         if (!has_privs_of_role(currentUserId, grantorId))
    2260            0 :             ereport(ERROR,
    2261              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2262              :                      errmsg("permission denied to grant privileges as role \"%s\"",
    2263              :                             GetUserNameFromId(grantorId, false)),
    2264              :                      errdetail("Only roles with privileges of role \"%s\" may grant privileges as this role.",
    2265              :                                GetUserNameFromId(grantorId, false))));
    2266              : 
    2267          153 :         if (grantorId != BOOTSTRAP_SUPERUSERID &&
    2268           45 :             select_best_admin(grantorId, roleid) != grantorId)
    2269            3 :             ereport(ERROR,
    2270              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2271              :                      errmsg("permission denied to grant privileges as role \"%s\"",
    2272              :                             GetUserNameFromId(grantorId, false)),
    2273              :                      errdetail("The grantor must have the %s option on role \"%s\".",
    2274              :                                "ADMIN", GetUserNameFromId(roleid, false))));
    2275              :     }
    2276              :     else
    2277              :     {
    2278           12 :         if (!has_privs_of_role(currentUserId, grantorId))
    2279            0 :             ereport(ERROR,
    2280              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2281              :                      errmsg("permission denied to revoke privileges granted by role \"%s\"",
    2282              :                             GetUserNameFromId(grantorId, false)),
    2283              :                      errdetail("Only roles with privileges of role \"%s\" may revoke privileges granted by this role.",
    2284              :                                GetUserNameFromId(grantorId, false))));
    2285              :     }
    2286              : 
    2287              :     /*
    2288              :      * If a grantor was specified explicitly, always attribute the grant to
    2289              :      * that role (unless we error out above).
    2290              :      */
    2291          117 :     return grantorId;
    2292              : }
    2293              : 
    2294              : /*
    2295              :  * Initialize an array of RevokeRoleGrantAction objects.
    2296              :  *
    2297              :  * 'memlist' should be a list of all grants for the target role.
    2298              :  *
    2299              :  * This constructs an array indicating that no actions are to be performed;
    2300              :  * that is, every element is initially RRG_NOOP.
    2301              :  */
    2302              : static RevokeRoleGrantAction *
    2303          153 : initialize_revoke_actions(CatCList *memlist)
    2304              : {
    2305              :     RevokeRoleGrantAction *result;
    2306              :     int         i;
    2307              : 
    2308          153 :     if (memlist->n_members == 0)
    2309            0 :         return NULL;
    2310              : 
    2311          153 :     result = palloc_array(RevokeRoleGrantAction, memlist->n_members);
    2312          416 :     for (i = 0; i < memlist->n_members; i++)
    2313          263 :         result[i] = RRG_NOOP;
    2314          153 :     return result;
    2315              : }
    2316              : 
    2317              : /*
    2318              :  * Figure out what we would need to do in order to revoke a grant, or just the
    2319              :  * admin option on a grant, given that there might be dependent privileges.
    2320              :  *
    2321              :  * 'memlist' should be a list of all grants for the target role.
    2322              :  *
    2323              :  * Whatever actions prove to be necessary will be signalled by updating
    2324              :  * 'actions'.
    2325              :  *
    2326              :  * If behavior is DROP_RESTRICT, an error will occur if there are dependent
    2327              :  * role membership grants; if DROP_CASCADE, those grants will be scheduled
    2328              :  * for deletion.
    2329              :  *
    2330              :  * The return value is true if the matching grant was found in the list,
    2331              :  * and false if not.
    2332              :  */
    2333              : static bool
    2334           81 : plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
    2335              :                    Oid member, Oid grantor, GrantRoleOptions *popt,
    2336              :                    DropBehavior behavior)
    2337              : {
    2338              :     int         i;
    2339              : 
    2340              :     /*
    2341              :      * If popt.specified == 0, we're revoking the grant entirely; otherwise,
    2342              :      * we expect just one bit to be set, and we're revoking the corresponding
    2343              :      * option. As of this writing, there's no syntax that would allow for an
    2344              :      * attempt to revoke multiple options at once, and the logic below
    2345              :      * wouldn't work properly if such syntax were added, so assert that our
    2346              :      * caller isn't trying to do that.
    2347              :      */
    2348              :     Assert(pg_popcount32(popt->specified) <= 1);
    2349              : 
    2350          140 :     for (i = 0; i < memlist->n_members; ++i)
    2351              :     {
    2352              :         HeapTuple   authmem_tuple;
    2353              :         Form_pg_auth_members authmem_form;
    2354              : 
    2355          137 :         authmem_tuple = &memlist->members[i]->tuple;
    2356          137 :         authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    2357              : 
    2358          137 :         if (authmem_form->member == member &&
    2359           87 :             authmem_form->grantor == grantor)
    2360              :         {
    2361           78 :             if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
    2362              :             {
    2363              :                 /*
    2364              :                  * Revoking the INHERIT option doesn't change anything for
    2365              :                  * dependent privileges, so we don't need to recurse.
    2366              :                  */
    2367            6 :                 actions[i] = RRG_REMOVE_INHERIT_OPTION;
    2368              :             }
    2369           72 :             else if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0)
    2370              :             {
    2371              :                 /* Here too, no need to recurse. */
    2372            3 :                 actions[i] = RRG_REMOVE_SET_OPTION;
    2373              :             }
    2374              :             else
    2375              :             {
    2376              :                 bool        revoke_admin_option_only;
    2377              : 
    2378              :                 /*
    2379              :                  * Revoking the grant entirely, or ADMIN option on a grant,
    2380              :                  * implicates dependent privileges, so we may need to recurse.
    2381              :                  */
    2382           69 :                 revoke_admin_option_only =
    2383           69 :                     (popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0;
    2384           69 :                 plan_recursive_revoke(memlist, actions, i,
    2385              :                                       revoke_admin_option_only, behavior);
    2386              :             }
    2387           72 :             return true;
    2388              :         }
    2389              :     }
    2390              : 
    2391            3 :     return false;
    2392              : }
    2393              : 
    2394              : /*
    2395              :  * Figure out what we would need to do in order to revoke all grants to
    2396              :  * a given member, given that there might be dependent privileges.
    2397              :  *
    2398              :  * 'memlist' should be a list of all grants for the target role.
    2399              :  *
    2400              :  * Whatever actions prove to be necessary will be signalled by updating
    2401              :  * 'actions'.
    2402              :  */
    2403              : static void
    2404           42 : plan_member_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
    2405              :                    Oid member)
    2406              : {
    2407              :     int         i;
    2408              : 
    2409           93 :     for (i = 0; i < memlist->n_members; ++i)
    2410              :     {
    2411              :         HeapTuple   authmem_tuple;
    2412              :         Form_pg_auth_members authmem_form;
    2413              : 
    2414           51 :         authmem_tuple = &memlist->members[i]->tuple;
    2415           51 :         authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    2416              : 
    2417           51 :         if (authmem_form->member == member)
    2418            3 :             plan_recursive_revoke(memlist, actions, i, false, DROP_CASCADE);
    2419              :     }
    2420           42 : }
    2421              : 
    2422              : /*
    2423              :  * Workhorse for figuring out recursive revocation of role grants.
    2424              :  *
    2425              :  * This is similar to what recursive_revoke() does for ACLs.
    2426              :  */
    2427              : static void
    2428           84 : plan_recursive_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
    2429              :                       int index,
    2430              :                       bool revoke_admin_option_only, DropBehavior behavior)
    2431              : {
    2432           84 :     bool        would_still_have_admin_option = false;
    2433              :     HeapTuple   authmem_tuple;
    2434              :     Form_pg_auth_members authmem_form;
    2435              :     int         i;
    2436              : 
    2437              :     /* If it's already been done, we can just return. */
    2438           84 :     if (actions[index] == RRG_DELETE_GRANT)
    2439            0 :         return;
    2440           84 :     if (actions[index] == RRG_REMOVE_ADMIN_OPTION &&
    2441              :         revoke_admin_option_only)
    2442            0 :         return;
    2443              : 
    2444              :     /* Locate tuple data. */
    2445           84 :     authmem_tuple = &memlist->members[index]->tuple;
    2446           84 :     authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    2447              : 
    2448              :     /*
    2449              :      * If the existing tuple does not have admin_option set, then we do not
    2450              :      * need to recurse. If we're just supposed to clear that bit we don't need
    2451              :      * to do anything at all; if we're supposed to remove the grant, we need
    2452              :      * to do something, but only to the tuple, and not any others.
    2453              :      */
    2454           84 :     if (!revoke_admin_option_only)
    2455              :     {
    2456           66 :         actions[index] = RRG_DELETE_GRANT;
    2457           66 :         if (!authmem_form->admin_option)
    2458           45 :             return;
    2459              :     }
    2460              :     else
    2461              :     {
    2462           18 :         if (!authmem_form->admin_option)
    2463            0 :             return;
    2464           18 :         actions[index] = RRG_REMOVE_ADMIN_OPTION;
    2465              :     }
    2466              : 
    2467              :     /* Determine whether the member would still have ADMIN OPTION. */
    2468          114 :     for (i = 0; i < memlist->n_members; ++i)
    2469              :     {
    2470              :         HeapTuple   am_cascade_tuple;
    2471              :         Form_pg_auth_members am_cascade_form;
    2472              : 
    2473           75 :         am_cascade_tuple = &memlist->members[i]->tuple;
    2474           75 :         am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
    2475              : 
    2476           75 :         if (am_cascade_form->member == authmem_form->member &&
    2477           39 :             am_cascade_form->admin_option && actions[i] == RRG_NOOP)
    2478              :         {
    2479            0 :             would_still_have_admin_option = true;
    2480            0 :             break;
    2481              :         }
    2482              :     }
    2483              : 
    2484              :     /* If the member would still have ADMIN OPTION, we need not recurse. */
    2485           39 :     if (would_still_have_admin_option)
    2486            0 :         return;
    2487              : 
    2488              :     /*
    2489              :      * Recurse to grants that are not yet slated for deletion which have this
    2490              :      * member as the grantor.
    2491              :      */
    2492          108 :     for (i = 0; i < memlist->n_members; ++i)
    2493              :     {
    2494              :         HeapTuple   am_cascade_tuple;
    2495              :         Form_pg_auth_members am_cascade_form;
    2496              : 
    2497           75 :         am_cascade_tuple = &memlist->members[i]->tuple;
    2498           75 :         am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
    2499              : 
    2500           75 :         if (am_cascade_form->grantor == authmem_form->member &&
    2501           18 :             actions[i] != RRG_DELETE_GRANT)
    2502              :         {
    2503           18 :             if (behavior == DROP_RESTRICT)
    2504            6 :                 ereport(ERROR,
    2505              :                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    2506              :                          errmsg("dependent privileges exist"),
    2507              :                          errhint("Use CASCADE to revoke them too.")));
    2508              : 
    2509           12 :             plan_recursive_revoke(memlist, actions, i, false, behavior);
    2510              :         }
    2511              :     }
    2512              : }
    2513              : 
    2514              : /*
    2515              :  * Initialize a GrantRoleOptions object with default values.
    2516              :  */
    2517              : static void
    2518         1462 : InitGrantRoleOptions(GrantRoleOptions *popt)
    2519              : {
    2520         1462 :     popt->specified = 0;
    2521         1462 :     popt->admin = false;
    2522         1462 :     popt->inherit = false;
    2523         1462 :     popt->set = true;
    2524         1462 : }
    2525              : 
    2526              : /*
    2527              :  * GUC check_hook for createrole_self_grant
    2528              :  */
    2529              : bool
    2530         1195 : check_createrole_self_grant(char **newval, void **extra, GucSource source)
    2531              : {
    2532              :     char       *rawstring;
    2533              :     List       *elemlist;
    2534              :     ListCell   *l;
    2535         1195 :     unsigned    options = 0;
    2536              :     unsigned   *result;
    2537              : 
    2538              :     /* Need a modifiable copy of string */
    2539         1195 :     rawstring = pstrdup(*newval);
    2540              : 
    2541         1195 :     if (!SplitIdentifierString(rawstring, ',', &elemlist))
    2542              :     {
    2543              :         /* syntax error in list */
    2544            0 :         GUC_check_errdetail("List syntax is invalid.");
    2545            0 :         pfree(rawstring);
    2546            0 :         list_free(elemlist);
    2547            0 :         return false;
    2548              :     }
    2549              : 
    2550         1201 :     foreach(l, elemlist)
    2551              :     {
    2552            6 :         char       *tok = (char *) lfirst(l);
    2553              : 
    2554            6 :         if (pg_strcasecmp(tok, "SET") == 0)
    2555            3 :             options |= GRANT_ROLE_SPECIFIED_SET;
    2556            3 :         else if (pg_strcasecmp(tok, "INHERIT") == 0)
    2557            3 :             options |= GRANT_ROLE_SPECIFIED_INHERIT;
    2558              :         else
    2559              :         {
    2560            0 :             GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
    2561            0 :             pfree(rawstring);
    2562            0 :             list_free(elemlist);
    2563            0 :             return false;
    2564              :         }
    2565              :     }
    2566              : 
    2567         1195 :     pfree(rawstring);
    2568         1195 :     list_free(elemlist);
    2569              : 
    2570         1195 :     result = (unsigned *) guc_malloc(LOG, sizeof(unsigned));
    2571         1195 :     if (!result)
    2572            0 :         return false;
    2573         1195 :     *result = options;
    2574         1195 :     *extra = result;
    2575              : 
    2576         1195 :     return true;
    2577              : }
    2578              : 
    2579              : /*
    2580              :  * GUC assign_hook for createrole_self_grant
    2581              :  */
    2582              : void
    2583         1195 : assign_createrole_self_grant(const char *newval, void *extra)
    2584              : {
    2585         1195 :     unsigned    options = *(unsigned *) extra;
    2586              : 
    2587         1195 :     createrole_self_grant_enabled = (options != 0);
    2588         1195 :     createrole_self_grant_options.specified = GRANT_ROLE_SPECIFIED_ADMIN
    2589              :         | GRANT_ROLE_SPECIFIED_INHERIT
    2590              :         | GRANT_ROLE_SPECIFIED_SET;
    2591         1195 :     createrole_self_grant_options.admin = false;
    2592         1195 :     createrole_self_grant_options.inherit =
    2593         1195 :         (options & GRANT_ROLE_SPECIFIED_INHERIT) != 0;
    2594         1195 :     createrole_self_grant_options.set =
    2595         1195 :         (options & GRANT_ROLE_SPECIFIED_SET) != 0;
    2596         1195 : }
        

Generated by: LCOV version 2.0-1