LCOV - code coverage report
Current view: top level - src/backend/commands - user.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 89.4 % 860 769
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 21 21
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 71.4 % 802 573

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

Generated by: LCOV version 2.0-1