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

Generated by: LCOV version 1.14