LCOV - code coverage report
Current view: top level - src/backend/commands - user.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.3 % 1114 984
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 21 21
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 62.9 % 940 591

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

Generated by: LCOV version 2.3.2-1