LCOV - code coverage report
Current view: top level - src/backend/commands - user.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 482 607 79.4 %
Date: 2017-09-29 15:12:54 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * user.c
       4             :  *    Commands for manipulating roles (formerly called users).
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, 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/heapam.h"
      17             : #include "access/htup_details.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/seclabel.h"
      31             : #include "commands/user.h"
      32             : #include "libpq/crypt.h"
      33             : #include "miscadmin.h"
      34             : #include "storage/lmgr.h"
      35             : #include "utils/acl.h"
      36             : #include "utils/builtins.h"
      37             : #include "utils/fmgroids.h"
      38             : #include "utils/syscache.h"
      39             : #include "utils/timestamp.h"
      40             : #include "utils/tqual.h"
      41             : 
      42             : /* Potentially set by pg_upgrade_support functions */
      43             : Oid         binary_upgrade_next_pg_authid_oid = InvalidOid;
      44             : 
      45             : 
      46             : /* GUC parameter */
      47             : int         Password_encryption = PASSWORD_TYPE_MD5;
      48             : 
      49             : /* Hook to check passwords in CreateRole() and AlterRole() */
      50             : check_password_hook_type check_password_hook = NULL;
      51             : 
      52             : static void AddRoleMems(const char *rolename, Oid roleid,
      53             :             List *memberSpecs, List *memberIds,
      54             :             Oid grantorId, bool admin_opt);
      55             : static void DelRoleMems(const char *rolename, Oid roleid,
      56             :             List *memberSpecs, List *memberIds,
      57             :             bool admin_opt);
      58             : 
      59             : 
      60             : /* Check if current user has createrole privileges */
      61             : static bool
      62         298 : have_createrole_privilege(void)
      63             : {
      64         298 :     return has_createrole_privilege(GetUserId());
      65             : }
      66             : 
      67             : 
      68             : /*
      69             :  * CREATE ROLE
      70             :  */
      71             : Oid
      72         118 : CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
      73             : {
      74             :     Relation    pg_authid_rel;
      75             :     TupleDesc   pg_authid_dsc;
      76             :     HeapTuple   tuple;
      77             :     Datum       new_record[Natts_pg_authid];
      78             :     bool        new_record_nulls[Natts_pg_authid];
      79             :     Oid         roleid;
      80             :     ListCell   *item;
      81             :     ListCell   *option;
      82         118 :     char       *password = NULL;    /* user password */
      83         118 :     bool        issuper = false;    /* Make the user a superuser? */
      84         118 :     bool        inherit = true; /* Auto inherit privileges? */
      85         118 :     bool        createrole = false; /* Can this user create roles? */
      86         118 :     bool        createdb = false;   /* Can the user create databases? */
      87         118 :     bool        canlogin = false;   /* Can this user login? */
      88         118 :     bool        isreplication = false;  /* Is this a replication role? */
      89         118 :     bool        bypassrls = false;  /* Is this a row security enabled role? */
      90         118 :     int         connlimit = -1; /* maximum connections allowed */
      91         118 :     List       *addroleto = NIL;    /* roles to make this a member of */
      92         118 :     List       *rolemembers = NIL;  /* roles to be members of this role */
      93         118 :     List       *adminmembers = NIL; /* roles to be admins of this role */
      94         118 :     char       *validUntil = NULL;  /* time the login is valid until */
      95             :     Datum       validUntil_datum;   /* same, as timestamptz Datum */
      96             :     bool        validUntil_null;
      97         118 :     DefElem    *dpassword = NULL;
      98         118 :     DefElem    *dissuper = NULL;
      99         118 :     DefElem    *dinherit = NULL;
     100         118 :     DefElem    *dcreaterole = NULL;
     101         118 :     DefElem    *dcreatedb = NULL;
     102         118 :     DefElem    *dcanlogin = NULL;
     103         118 :     DefElem    *disreplication = NULL;
     104         118 :     DefElem    *dconnlimit = NULL;
     105         118 :     DefElem    *daddroleto = NULL;
     106         118 :     DefElem    *drolemembers = NULL;
     107         118 :     DefElem    *dadminmembers = NULL;
     108         118 :     DefElem    *dvalidUntil = NULL;
     109         118 :     DefElem    *dbypassRLS = NULL;
     110             : 
     111             :     /* The defaults can vary depending on the original statement type */
     112         118 :     switch (stmt->stmt_type)
     113             :     {
     114             :         case ROLESTMT_ROLE:
     115          76 :             break;
     116             :         case ROLESTMT_USER:
     117          38 :             canlogin = true;
     118             :             /* may eventually want inherit to default to false here */
     119          38 :             break;
     120             :         case ROLESTMT_GROUP:
     121           4 :             break;
     122             :     }
     123             : 
     124             :     /* Extract options from the statement node tree */
     125         172 :     foreach(option, stmt->options)
     126             :     {
     127          54 :         DefElem    *defel = (DefElem *) lfirst(option);
     128             : 
     129          54 :         if (strcmp(defel->defname, "password") == 0)
     130             :         {
     131           6 :             if (dpassword)
     132           0 :                 ereport(ERROR,
     133             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     134             :                          errmsg("conflicting or redundant options"),
     135             :                          parser_errposition(pstate, defel->location)));
     136           6 :             dpassword = defel;
     137             :         }
     138          48 :         else if (strcmp(defel->defname, "sysid") == 0)
     139             :         {
     140           0 :             ereport(NOTICE,
     141             :                     (errmsg("SYSID can no longer be specified")));
     142             :         }
     143          48 :         else if (strcmp(defel->defname, "superuser") == 0)
     144             :         {
     145          14 :             if (dissuper)
     146           0 :                 ereport(ERROR,
     147             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     148             :                          errmsg("conflicting or redundant options"),
     149             :                          parser_errposition(pstate, defel->location)));
     150          14 :             dissuper = defel;
     151             :         }
     152          34 :         else if (strcmp(defel->defname, "inherit") == 0)
     153             :         {
     154           1 :             if (dinherit)
     155           0 :                 ereport(ERROR,
     156             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     157             :                          errmsg("conflicting or redundant options"),
     158             :                          parser_errposition(pstate, defel->location)));
     159           1 :             dinherit = defel;
     160             :         }
     161          33 :         else if (strcmp(defel->defname, "createrole") == 0)
     162             :         {
     163           3 :             if (dcreaterole)
     164           0 :                 ereport(ERROR,
     165             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     166             :                          errmsg("conflicting or redundant options"),
     167             :                          parser_errposition(pstate, defel->location)));
     168           3 :             dcreaterole = defel;
     169             :         }
     170          30 :         else if (strcmp(defel->defname, "createdb") == 0)
     171             :         {
     172           2 :             if (dcreatedb)
     173           0 :                 ereport(ERROR,
     174             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     175             :                          errmsg("conflicting or redundant options"),
     176             :                          parser_errposition(pstate, defel->location)));
     177           2 :             dcreatedb = defel;
     178             :         }
     179          28 :         else if (strcmp(defel->defname, "canlogin") == 0)
     180             :         {
     181          22 :             if (dcanlogin)
     182           0 :                 ereport(ERROR,
     183             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     184             :                          errmsg("conflicting or redundant options"),
     185             :                          parser_errposition(pstate, defel->location)));
     186          22 :             dcanlogin = defel;
     187             :         }
     188           6 :         else if (strcmp(defel->defname, "isreplication") == 0)
     189             :         {
     190           1 :             if (disreplication)
     191           0 :                 ereport(ERROR,
     192             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     193             :                          errmsg("conflicting or redundant options"),
     194             :                          parser_errposition(pstate, defel->location)));
     195           1 :             disreplication = defel;
     196             :         }
     197           5 :         else if (strcmp(defel->defname, "connectionlimit") == 0)
     198             :         {
     199           0 :             if (dconnlimit)
     200           0 :                 ereport(ERROR,
     201             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     202             :                          errmsg("conflicting or redundant options"),
     203             :                          parser_errposition(pstate, defel->location)));
     204           0 :             dconnlimit = defel;
     205             :         }
     206           5 :         else if (strcmp(defel->defname, "addroleto") == 0)
     207             :         {
     208           2 :             if (daddroleto)
     209           0 :                 ereport(ERROR,
     210             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     211             :                          errmsg("conflicting or redundant options"),
     212             :                          parser_errposition(pstate, defel->location)));
     213           2 :             daddroleto = defel;
     214             :         }
     215           3 :         else if (strcmp(defel->defname, "rolemembers") == 0)
     216             :         {
     217           1 :             if (drolemembers)
     218           0 :                 ereport(ERROR,
     219             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     220             :                          errmsg("conflicting or redundant options"),
     221             :                          parser_errposition(pstate, defel->location)));
     222           1 :             drolemembers = defel;
     223             :         }
     224           2 :         else if (strcmp(defel->defname, "adminmembers") == 0)
     225             :         {
     226           0 :             if (dadminmembers)
     227           0 :                 ereport(ERROR,
     228             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     229             :                          errmsg("conflicting or redundant options"),
     230             :                          parser_errposition(pstate, defel->location)));
     231           0 :             dadminmembers = defel;
     232             :         }
     233           2 :         else if (strcmp(defel->defname, "validUntil") == 0)
     234             :         {
     235           0 :             if (dvalidUntil)
     236           0 :                 ereport(ERROR,
     237             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     238             :                          errmsg("conflicting or redundant options"),
     239             :                          parser_errposition(pstate, defel->location)));
     240           0 :             dvalidUntil = defel;
     241             :         }
     242           2 :         else if (strcmp(defel->defname, "bypassrls") == 0)
     243             :         {
     244           2 :             if (dbypassRLS)
     245           0 :                 ereport(ERROR,
     246             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     247             :                          errmsg("conflicting or redundant options"),
     248             :                          parser_errposition(pstate, defel->location)));
     249           2 :             dbypassRLS = defel;
     250             :         }
     251             :         else
     252           0 :             elog(ERROR, "option \"%s\" not recognized",
     253             :                  defel->defname);
     254             :     }
     255             : 
     256         118 :     if (dpassword && dpassword->arg)
     257           5 :         password = strVal(dpassword->arg);
     258         118 :     if (dissuper)
     259          14 :         issuper = intVal(dissuper->arg) != 0;
     260         118 :     if (dinherit)
     261           1 :         inherit = intVal(dinherit->arg) != 0;
     262         118 :     if (dcreaterole)
     263           3 :         createrole = intVal(dcreaterole->arg) != 0;
     264         118 :     if (dcreatedb)
     265           2 :         createdb = intVal(dcreatedb->arg) != 0;
     266         118 :     if (dcanlogin)
     267          22 :         canlogin = intVal(dcanlogin->arg) != 0;
     268         118 :     if (disreplication)
     269           1 :         isreplication = intVal(disreplication->arg) != 0;
     270         118 :     if (dconnlimit)
     271             :     {
     272           0 :         connlimit = intVal(dconnlimit->arg);
     273           0 :         if (connlimit < -1)
     274           0 :             ereport(ERROR,
     275             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     276             :                      errmsg("invalid connection limit: %d", connlimit)));
     277             :     }
     278         118 :     if (daddroleto)
     279           2 :         addroleto = (List *) daddroleto->arg;
     280         118 :     if (drolemembers)
     281           1 :         rolemembers = (List *) drolemembers->arg;
     282         118 :     if (dadminmembers)
     283           0 :         adminmembers = (List *) dadminmembers->arg;
     284         118 :     if (dvalidUntil)
     285           0 :         validUntil = strVal(dvalidUntil->arg);
     286         118 :     if (dbypassRLS)
     287           2 :         bypassrls = intVal(dbypassRLS->arg) != 0;
     288             : 
     289             :     /* Check some permissions first */
     290         118 :     if (issuper)
     291             :     {
     292          11 :         if (!superuser())
     293           0 :             ereport(ERROR,
     294             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     295             :                      errmsg("must be superuser to create superusers")));
     296             :     }
     297         107 :     else if (isreplication)
     298             :     {
     299           1 :         if (!superuser())
     300           0 :             ereport(ERROR,
     301             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     302             :                      errmsg("must be superuser to create replication users")));
     303             :     }
     304         106 :     else if (bypassrls)
     305             :     {
     306           2 :         if (!superuser())
     307           0 :             ereport(ERROR,
     308             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     309             :                      errmsg("must be superuser to change bypassrls attribute")));
     310             :     }
     311             :     else
     312             :     {
     313         104 :         if (!have_createrole_privilege())
     314           0 :             ereport(ERROR,
     315             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     316             :                      errmsg("permission denied to create role")));
     317             :     }
     318             : 
     319             :     /*
     320             :      * Check that the user is not trying to create a role in the reserved
     321             :      * "pg_" namespace.
     322             :      */
     323         118 :     if (IsReservedName(stmt->role))
     324           4 :         ereport(ERROR,
     325             :                 (errcode(ERRCODE_RESERVED_NAME),
     326             :                  errmsg("role name \"%s\" is reserved",
     327             :                         stmt->role),
     328             :                  errdetail("Role names starting with \"pg_\" are reserved.")));
     329             : 
     330             :     /*
     331             :      * Check the pg_authid relation to be certain the role doesn't already
     332             :      * exist.
     333             :      */
     334         114 :     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
     335         114 :     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
     336             : 
     337         114 :     if (OidIsValid(get_role_oid(stmt->role, true)))
     338           1 :         ereport(ERROR,
     339             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     340             :                  errmsg("role \"%s\" already exists",
     341             :                         stmt->role)));
     342             : 
     343             :     /* Convert validuntil to internal form */
     344         113 :     if (validUntil)
     345             :     {
     346           0 :         validUntil_datum = DirectFunctionCall3(timestamptz_in,
     347             :                                                CStringGetDatum(validUntil),
     348             :                                                ObjectIdGetDatum(InvalidOid),
     349             :                                                Int32GetDatum(-1));
     350           0 :         validUntil_null = false;
     351             :     }
     352             :     else
     353             :     {
     354         113 :         validUntil_datum = (Datum) 0;
     355         113 :         validUntil_null = true;
     356             :     }
     357             : 
     358             :     /*
     359             :      * Call the password checking hook if there is one defined
     360             :      */
     361         113 :     if (check_password_hook && password)
     362           0 :         (*check_password_hook) (stmt->role,
     363             :                                 password,
     364             :                                 get_password_type(password),
     365             :                                 validUntil_datum,
     366             :                                 validUntil_null);
     367             : 
     368             :     /*
     369             :      * Build a tuple to insert
     370             :      */
     371         113 :     MemSet(new_record, 0, sizeof(new_record));
     372         113 :     MemSet(new_record_nulls, false, sizeof(new_record_nulls));
     373             : 
     374         113 :     new_record[Anum_pg_authid_rolname - 1] =
     375         113 :         DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
     376             : 
     377         113 :     new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
     378         113 :     new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
     379         113 :     new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
     380         113 :     new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
     381         113 :     new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
     382         113 :     new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
     383         113 :     new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
     384             : 
     385         113 :     if (password)
     386             :     {
     387             :         char       *shadow_pass;
     388             :         char       *logdetail;
     389             : 
     390             :         /*
     391             :          * Don't allow an empty password. Libpq treats an empty password the
     392             :          * same as no password at all, and won't even try to authenticate. But
     393             :          * other clients might, so allowing it would be confusing. By clearing
     394             :          * the password when an empty string is specified, the account is
     395             :          * consistently locked for all clients.
     396             :          *
     397             :          * Note that this only covers passwords stored in the database itself.
     398             :          * There are also checks in the authentication code, to forbid an
     399             :          * empty password from being used with authentication methods that
     400             :          * fetch the password from an external system, like LDAP or PAM.
     401             :          */
     402           9 :         if (password[0] == '\0' ||
     403           4 :             plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK)
     404             :         {
     405           1 :             ereport(NOTICE,
     406             :                     (errmsg("empty string is not a valid password, clearing password")));
     407           1 :             new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     408             :         }
     409             :         else
     410             :         {
     411             :             /* Encrypt the password to the requested format. */
     412           4 :             shadow_pass = encrypt_password(Password_encryption, stmt->role,
     413             :                                            password);
     414           4 :             new_record[Anum_pg_authid_rolpassword - 1] =
     415           4 :                 CStringGetTextDatum(shadow_pass);
     416             :         }
     417             :     }
     418             :     else
     419         108 :         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     420             : 
     421         113 :     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
     422         113 :     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
     423             : 
     424         113 :     new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
     425             : 
     426         113 :     tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
     427             : 
     428             :     /*
     429             :      * pg_largeobject_metadata contains pg_authid.oid's, so we use the
     430             :      * binary-upgrade override.
     431             :      */
     432         113 :     if (IsBinaryUpgrade)
     433             :     {
     434           0 :         if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
     435           0 :             ereport(ERROR,
     436             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     437             :                      errmsg("pg_authid OID value not set when in binary upgrade mode")));
     438             : 
     439           0 :         HeapTupleSetOid(tuple, binary_upgrade_next_pg_authid_oid);
     440           0 :         binary_upgrade_next_pg_authid_oid = InvalidOid;
     441             :     }
     442             : 
     443             :     /*
     444             :      * Insert new record in the pg_authid table
     445             :      */
     446         113 :     roleid = CatalogTupleInsert(pg_authid_rel, tuple);
     447             : 
     448             :     /*
     449             :      * Advance command counter so we can see new record; else tests in
     450             :      * AddRoleMems may fail.
     451             :      */
     452         113 :     if (addroleto || adminmembers || rolemembers)
     453           3 :         CommandCounterIncrement();
     454             : 
     455             :     /*
     456             :      * Add the new role to the specified existing roles.
     457             :      */
     458         115 :     foreach(item, addroleto)
     459             :     {
     460           2 :         RoleSpec   *oldrole = lfirst(item);
     461           2 :         HeapTuple   oldroletup = get_rolespec_tuple(oldrole);
     462           2 :         Oid         oldroleid = HeapTupleGetOid(oldroletup);
     463           2 :         char       *oldrolename = NameStr(((Form_pg_authid) GETSTRUCT(oldroletup))->rolname);
     464             : 
     465           4 :         AddRoleMems(oldrolename, oldroleid,
     466           2 :                     list_make1(makeString(stmt->role)),
     467             :                     list_make1_oid(roleid),
     468             :                     GetUserId(), false);
     469             : 
     470           2 :         ReleaseSysCache(oldroletup);
     471             :     }
     472             : 
     473             :     /*
     474             :      * Add the specified members to this new role. adminmembers get the admin
     475             :      * option, rolemembers don't.
     476             :      */
     477         113 :     AddRoleMems(stmt->role, roleid,
     478             :                 adminmembers, roleSpecsToIds(adminmembers),
     479             :                 GetUserId(), true);
     480         113 :     AddRoleMems(stmt->role, roleid,
     481             :                 rolemembers, roleSpecsToIds(rolemembers),
     482             :                 GetUserId(), false);
     483             : 
     484             :     /* Post creation hook for new role */
     485         113 :     InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
     486             : 
     487             :     /*
     488             :      * Close pg_authid, but keep lock till commit.
     489             :      */
     490         113 :     heap_close(pg_authid_rel, NoLock);
     491             : 
     492         113 :     return roleid;
     493             : }
     494             : 
     495             : 
     496             : /*
     497             :  * ALTER ROLE
     498             :  *
     499             :  * Note: the rolemembers option accepted here is intended to support the
     500             :  * backwards-compatible ALTER GROUP syntax.  Although it will work to say
     501             :  * "ALTER ROLE role ROLE rolenames", we don't document it.
     502             :  */
     503             : Oid
     504          52 : AlterRole(AlterRoleStmt *stmt)
     505             : {
     506             :     Datum       new_record[Natts_pg_authid];
     507             :     bool        new_record_nulls[Natts_pg_authid];
     508             :     bool        new_record_repl[Natts_pg_authid];
     509             :     Relation    pg_authid_rel;
     510             :     TupleDesc   pg_authid_dsc;
     511             :     HeapTuple   tuple,
     512             :                 new_tuple;
     513             :     Form_pg_authid authform;
     514             :     ListCell   *option;
     515          52 :     char       *rolename = NULL;
     516          52 :     char       *password = NULL;    /* user password */
     517          52 :     int         issuper = -1;   /* Make the user a superuser? */
     518          52 :     int         inherit = -1;   /* Auto inherit privileges? */
     519          52 :     int         createrole = -1;    /* Can this user create roles? */
     520          52 :     int         createdb = -1;  /* Can the user create databases? */
     521          52 :     int         canlogin = -1;  /* Can this user login? */
     522          52 :     int         isreplication = -1; /* Is this a replication role? */
     523          52 :     int         connlimit = -1; /* maximum connections allowed */
     524          52 :     List       *rolemembers = NIL;  /* roles to be added/removed */
     525          52 :     char       *validUntil = NULL;  /* time the login is valid until */
     526             :     Datum       validUntil_datum;   /* same, as timestamptz Datum */
     527             :     bool        validUntil_null;
     528          52 :     int         bypassrls = -1;
     529          52 :     DefElem    *dpassword = NULL;
     530          52 :     DefElem    *dissuper = NULL;
     531          52 :     DefElem    *dinherit = NULL;
     532          52 :     DefElem    *dcreaterole = NULL;
     533          52 :     DefElem    *dcreatedb = NULL;
     534          52 :     DefElem    *dcanlogin = NULL;
     535          52 :     DefElem    *disreplication = NULL;
     536          52 :     DefElem    *dconnlimit = NULL;
     537          52 :     DefElem    *drolemembers = NULL;
     538          52 :     DefElem    *dvalidUntil = NULL;
     539          52 :     DefElem    *dbypassRLS = NULL;
     540             :     Oid         roleid;
     541             : 
     542          52 :     check_rolespec_name(stmt->role,
     543             :                         "Cannot alter reserved roles.");
     544             : 
     545             :     /* Extract options from the statement node tree */
     546         104 :     foreach(option, stmt->options)
     547             :     {
     548          52 :         DefElem    *defel = (DefElem *) lfirst(option);
     549             : 
     550          52 :         if (strcmp(defel->defname, "password") == 0)
     551             :         {
     552           6 :             if (dpassword)
     553           0 :                 ereport(ERROR,
     554             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     555             :                          errmsg("conflicting or redundant options")));
     556           6 :             dpassword = defel;
     557             :         }
     558          46 :         else if (strcmp(defel->defname, "superuser") == 0)
     559             :         {
     560           5 :             if (dissuper)
     561           0 :                 ereport(ERROR,
     562             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     563             :                          errmsg("conflicting or redundant options")));
     564           5 :             dissuper = defel;
     565             :         }
     566          41 :         else if (strcmp(defel->defname, "inherit") == 0)
     567             :         {
     568           2 :             if (dinherit)
     569           0 :                 ereport(ERROR,
     570             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     571             :                          errmsg("conflicting or redundant options")));
     572           2 :             dinherit = defel;
     573             :         }
     574          39 :         else if (strcmp(defel->defname, "createrole") == 0)
     575             :         {
     576           2 :             if (dcreaterole)
     577           0 :                 ereport(ERROR,
     578             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     579             :                          errmsg("conflicting or redundant options")));
     580           2 :             dcreaterole = defel;
     581             :         }
     582          37 :         else if (strcmp(defel->defname, "createdb") == 0)
     583             :         {
     584           2 :             if (dcreatedb)
     585           0 :                 ereport(ERROR,
     586             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     587             :                          errmsg("conflicting or redundant options")));
     588           2 :             dcreatedb = defel;
     589             :         }
     590          35 :         else if (strcmp(defel->defname, "canlogin") == 0)
     591             :         {
     592           4 :             if (dcanlogin)
     593           0 :                 ereport(ERROR,
     594             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     595             :                          errmsg("conflicting or redundant options")));
     596           4 :             dcanlogin = defel;
     597             :         }
     598          31 :         else if (strcmp(defel->defname, "isreplication") == 0)
     599             :         {
     600          26 :             if (disreplication)
     601           0 :                 ereport(ERROR,
     602             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     603             :                          errmsg("conflicting or redundant options")));
     604          26 :             disreplication = defel;
     605             :         }
     606           5 :         else if (strcmp(defel->defname, "connectionlimit") == 0)
     607             :         {
     608           0 :             if (dconnlimit)
     609           0 :                 ereport(ERROR,
     610             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     611             :                          errmsg("conflicting or redundant options")));
     612           0 :             dconnlimit = defel;
     613             :         }
     614           8 :         else if (strcmp(defel->defname, "rolemembers") == 0 &&
     615           3 :                  stmt->action != 0)
     616             :         {
     617           3 :             if (drolemembers)
     618           0 :                 ereport(ERROR,
     619             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     620             :                          errmsg("conflicting or redundant options")));
     621           3 :             drolemembers = defel;
     622             :         }
     623           2 :         else if (strcmp(defel->defname, "validUntil") == 0)
     624             :         {
     625           0 :             if (dvalidUntil)
     626           0 :                 ereport(ERROR,
     627             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     628             :                          errmsg("conflicting or redundant options")));
     629           0 :             dvalidUntil = defel;
     630             :         }
     631           2 :         else if (strcmp(defel->defname, "bypassrls") == 0)
     632             :         {
     633           2 :             if (dbypassRLS)
     634           0 :                 ereport(ERROR,
     635             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     636             :                          errmsg("conflicting or redundant options")));
     637           2 :             dbypassRLS = defel;
     638             :         }
     639             :         else
     640           0 :             elog(ERROR, "option \"%s\" not recognized",
     641             :                  defel->defname);
     642             :     }
     643             : 
     644          52 :     if (dpassword && dpassword->arg)
     645           6 :         password = strVal(dpassword->arg);
     646          52 :     if (dissuper)
     647           5 :         issuper = intVal(dissuper->arg);
     648          52 :     if (dinherit)
     649           2 :         inherit = intVal(dinherit->arg);
     650          52 :     if (dcreaterole)
     651           2 :         createrole = intVal(dcreaterole->arg);
     652          52 :     if (dcreatedb)
     653           2 :         createdb = intVal(dcreatedb->arg);
     654          52 :     if (dcanlogin)
     655           4 :         canlogin = intVal(dcanlogin->arg);
     656          52 :     if (disreplication)
     657          26 :         isreplication = intVal(disreplication->arg);
     658          52 :     if (dconnlimit)
     659             :     {
     660           0 :         connlimit = intVal(dconnlimit->arg);
     661           0 :         if (connlimit < -1)
     662           0 :             ereport(ERROR,
     663             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     664             :                      errmsg("invalid connection limit: %d", connlimit)));
     665             :     }
     666          52 :     if (drolemembers)
     667           3 :         rolemembers = (List *) drolemembers->arg;
     668          52 :     if (dvalidUntil)
     669           0 :         validUntil = strVal(dvalidUntil->arg);
     670          52 :     if (dbypassRLS)
     671           2 :         bypassrls = intVal(dbypassRLS->arg);
     672             : 
     673             :     /*
     674             :      * Scan the pg_authid relation to be certain the user exists.
     675             :      */
     676          52 :     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
     677          52 :     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
     678             : 
     679          52 :     tuple = get_rolespec_tuple(stmt->role);
     680          44 :     authform = (Form_pg_authid) GETSTRUCT(tuple);
     681          44 :     rolename = pstrdup(NameStr(authform->rolname));
     682          44 :     roleid = HeapTupleGetOid(tuple);
     683             : 
     684             :     /*
     685             :      * To mess with a superuser you gotta be superuser; else you need
     686             :      * createrole, or just want to change your own password
     687             :      */
     688          44 :     if (authform->rolsuper || issuper >= 0)
     689             :     {
     690          26 :         if (!superuser())
     691           0 :             ereport(ERROR,
     692             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     693             :                      errmsg("must be superuser to alter superusers")));
     694             :     }
     695          31 :     else if (authform->rolreplication || isreplication >= 0)
     696             :     {
     697          20 :         if (!superuser())
     698           0 :             ereport(ERROR,
     699             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     700             :                      errmsg("must be superuser to alter replication users")));
     701             :     }
     702          21 :     else if (authform->rolbypassrls || bypassrls >= 0)
     703             :     {
     704           4 :         if (!superuser())
     705           0 :             ereport(ERROR,
     706             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     707             :                      errmsg("must be superuser to change bypassrls attribute")));
     708             :     }
     709          19 :     else if (!have_createrole_privilege())
     710             :     {
     711           0 :         if (!(inherit < 0 &&
     712           0 :               createrole < 0 &&
     713           0 :               createdb < 0 &&
     714           0 :               canlogin < 0 &&
     715           0 :               isreplication < 0 &&
     716           0 :               !dconnlimit &&
     717           0 :               !rolemembers &&
     718           0 :               !validUntil &&
     719             :               dpassword &&
     720           0 :               roleid == GetUserId()))
     721           0 :             ereport(ERROR,
     722             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     723             :                      errmsg("permission denied")));
     724             :     }
     725             : 
     726             :     /* Convert validuntil to internal form */
     727          44 :     if (validUntil)
     728             :     {
     729           0 :         validUntil_datum = DirectFunctionCall3(timestamptz_in,
     730             :                                                CStringGetDatum(validUntil),
     731             :                                                ObjectIdGetDatum(InvalidOid),
     732             :                                                Int32GetDatum(-1));
     733           0 :         validUntil_null = false;
     734             :     }
     735             :     else
     736             :     {
     737             :         /* fetch existing setting in case hook needs it */
     738          44 :         validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
     739             :                                            Anum_pg_authid_rolvaliduntil,
     740             :                                            &validUntil_null);
     741             :     }
     742             : 
     743             :     /*
     744             :      * Call the password checking hook if there is one defined
     745             :      */
     746          44 :     if (check_password_hook && password)
     747           0 :         (*check_password_hook) (rolename,
     748             :                                 password,
     749             :                                 get_password_type(password),
     750             :                                 validUntil_datum,
     751             :                                 validUntil_null);
     752             : 
     753             :     /*
     754             :      * Build an updated tuple, perusing the information just obtained
     755             :      */
     756          44 :     MemSet(new_record, 0, sizeof(new_record));
     757          44 :     MemSet(new_record_nulls, false, sizeof(new_record_nulls));
     758          44 :     MemSet(new_record_repl, false, sizeof(new_record_repl));
     759             : 
     760             :     /*
     761             :      * issuper/createrole/etc
     762             :      */
     763          44 :     if (issuper >= 0)
     764             :     {
     765           5 :         new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
     766           5 :         new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
     767             :     }
     768             : 
     769          44 :     if (inherit >= 0)
     770             :     {
     771           2 :         new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
     772           2 :         new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
     773             :     }
     774             : 
     775          44 :     if (createrole >= 0)
     776             :     {
     777           2 :         new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
     778           2 :         new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
     779             :     }
     780             : 
     781          44 :     if (createdb >= 0)
     782             :     {
     783           2 :         new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
     784           2 :         new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
     785             :     }
     786             : 
     787          44 :     if (canlogin >= 0)
     788             :     {
     789           4 :         new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
     790           4 :         new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
     791             :     }
     792             : 
     793          44 :     if (isreplication >= 0)
     794             :     {
     795          18 :         new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
     796          18 :         new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
     797             :     }
     798             : 
     799          44 :     if (dconnlimit)
     800             :     {
     801           0 :         new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
     802           0 :         new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
     803             :     }
     804             : 
     805             :     /* password */
     806          44 :     if (password)
     807             :     {
     808             :         char       *shadow_pass;
     809             :         char       *logdetail;
     810             : 
     811             :         /* Like in CREATE USER, don't allow an empty password. */
     812          12 :         if (password[0] == '\0' ||
     813           6 :             plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
     814             :         {
     815           2 :             ereport(NOTICE,
     816             :                     (errmsg("empty string is not a valid password, clearing password")));
     817           2 :             new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     818             :         }
     819             :         else
     820             :         {
     821             :             /* Encrypt the password to the requested format. */
     822           4 :             shadow_pass = encrypt_password(Password_encryption, rolename,
     823             :                                            password);
     824           4 :             new_record[Anum_pg_authid_rolpassword - 1] =
     825           4 :                 CStringGetTextDatum(shadow_pass);
     826             :         }
     827           6 :         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
     828             :     }
     829             : 
     830             :     /* unset password */
     831          44 :     if (dpassword && dpassword->arg == NULL)
     832             :     {
     833           0 :         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
     834           0 :         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
     835             :     }
     836             : 
     837             :     /* valid until */
     838          44 :     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
     839          44 :     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
     840          44 :     new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
     841             : 
     842          44 :     if (bypassrls >= 0)
     843             :     {
     844           2 :         new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
     845           2 :         new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
     846             :     }
     847             : 
     848          44 :     new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
     849             :                                   new_record_nulls, new_record_repl);
     850          44 :     CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
     851             : 
     852          44 :     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
     853             : 
     854          44 :     ReleaseSysCache(tuple);
     855          44 :     heap_freetuple(new_tuple);
     856             : 
     857             :     /*
     858             :      * Advance command counter so we can see new record; else tests in
     859             :      * AddRoleMems may fail.
     860             :      */
     861          44 :     if (rolemembers)
     862           3 :         CommandCounterIncrement();
     863             : 
     864          44 :     if (stmt->action == +1)      /* add members to role */
     865          43 :         AddRoleMems(rolename, roleid,
     866             :                     rolemembers, roleSpecsToIds(rolemembers),
     867             :                     GetUserId(), false);
     868           1 :     else if (stmt->action == -1) /* drop members from role */
     869           1 :         DelRoleMems(rolename, roleid,
     870             :                     rolemembers, roleSpecsToIds(rolemembers),
     871             :                     false);
     872             : 
     873             :     /*
     874             :      * Close pg_authid, but keep lock till commit.
     875             :      */
     876          44 :     heap_close(pg_authid_rel, NoLock);
     877             : 
     878          44 :     return roleid;
     879             : }
     880             : 
     881             : 
     882             : /*
     883             :  * ALTER ROLE ... SET
     884             :  */
     885             : Oid
     886          28 : AlterRoleSet(AlterRoleSetStmt *stmt)
     887             : {
     888             :     HeapTuple   roletuple;
     889          28 :     Oid         databaseid = InvalidOid;
     890          28 :     Oid         roleid = InvalidOid;
     891             : 
     892          28 :     if (stmt->role)
     893             :     {
     894          24 :         check_rolespec_name(stmt->role,
     895             :                             "Cannot alter reserved roles.");
     896             : 
     897          24 :         roletuple = get_rolespec_tuple(stmt->role);
     898          20 :         roleid = HeapTupleGetOid(roletuple);
     899             : 
     900             :         /*
     901             :          * Obtain a lock on the role and make sure it didn't go away in the
     902             :          * meantime.
     903             :          */
     904          20 :         shdepLockAndCheckObject(AuthIdRelationId, HeapTupleGetOid(roletuple));
     905             : 
     906             :         /*
     907             :          * To mess with a superuser you gotta be superuser; else you need
     908             :          * createrole, or just want to change your own settings
     909             :          */
     910          20 :         if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
     911             :         {
     912          11 :             if (!superuser())
     913           0 :                 ereport(ERROR,
     914             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     915             :                          errmsg("must be superuser to alter superusers")));
     916             :         }
     917             :         else
     918             :         {
     919           9 :             if (!have_createrole_privilege() &&
     920           0 :                 HeapTupleGetOid(roletuple) != GetUserId())
     921           0 :                 ereport(ERROR,
     922             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     923             :                          errmsg("permission denied")));
     924             :         }
     925             : 
     926          20 :         ReleaseSysCache(roletuple);
     927             :     }
     928             : 
     929             :     /* look up and lock the database, if specified */
     930          24 :     if (stmt->database != NULL)
     931             :     {
     932           0 :         databaseid = get_database_oid(stmt->database, false);
     933           0 :         shdepLockAndCheckObject(DatabaseRelationId, databaseid);
     934             : 
     935           0 :         if (!stmt->role)
     936             :         {
     937             :             /*
     938             :              * If no role is specified, then this is effectively the same as
     939             :              * ALTER DATABASE ... SET, so use the same permission check.
     940             :              */
     941           0 :             if (!pg_database_ownercheck(databaseid, GetUserId()))
     942           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
     943           0 :                                stmt->database);
     944             :         }
     945             :     }
     946             : 
     947          24 :     if (!stmt->role && !stmt->database)
     948             :     {
     949             :         /* Must be superuser to alter settings globally. */
     950           4 :         if (!superuser())
     951           0 :             ereport(ERROR,
     952             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     953             :                      errmsg("must be superuser to alter settings globally")));
     954             :     }
     955             : 
     956          24 :     AlterSetting(databaseid, roleid, stmt->setstmt);
     957             : 
     958          24 :     return roleid;
     959             : }
     960             : 
     961             : 
     962             : /*
     963             :  * DROP ROLE
     964             :  */
     965             : void
     966         141 : DropRole(DropRoleStmt *stmt)
     967             : {
     968             :     Relation    pg_authid_rel,
     969             :                 pg_auth_members_rel;
     970             :     ListCell   *item;
     971             : 
     972         141 :     if (!have_createrole_privilege())
     973           0 :         ereport(ERROR,
     974             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     975             :                  errmsg("permission denied to drop role")));
     976             : 
     977             :     /*
     978             :      * Scan the pg_authid relation to find the Oid of the role(s) to be
     979             :      * deleted.
     980             :      */
     981         141 :     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
     982         141 :     pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
     983             : 
     984         277 :     foreach(item, stmt->roles)
     985             :     {
     986         157 :         RoleSpec   *rolspec = lfirst(item);
     987             :         char       *role;
     988             :         HeapTuple   tuple,
     989             :                     tmp_tuple;
     990             :         ScanKeyData scankey;
     991             :         char       *detail;
     992             :         char       *detail_log;
     993             :         SysScanDesc sscan;
     994             :         Oid         roleid;
     995             : 
     996         157 :         if (rolspec->roletype != ROLESPEC_CSTRING)
     997           0 :             ereport(ERROR,
     998             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     999             :                      errmsg("cannot use special role specifier in DROP ROLE")));
    1000         157 :         role = rolspec->rolename;
    1001             : 
    1002         157 :         tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
    1003         157 :         if (!HeapTupleIsValid(tuple))
    1004             :         {
    1005          37 :             if (!stmt->missing_ok)
    1006             :             {
    1007           7 :                 ereport(ERROR,
    1008             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
    1009             :                          errmsg("role \"%s\" does not exist", role)));
    1010             :             }
    1011             :             else
    1012             :             {
    1013          30 :                 ereport(NOTICE,
    1014             :                         (errmsg("role \"%s\" does not exist, skipping",
    1015             :                                 role)));
    1016             :             }
    1017             : 
    1018          30 :             continue;
    1019             :         }
    1020             : 
    1021         120 :         roleid = HeapTupleGetOid(tuple);
    1022             : 
    1023         120 :         if (roleid == GetUserId())
    1024           0 :             ereport(ERROR,
    1025             :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1026             :                      errmsg("current user cannot be dropped")));
    1027         120 :         if (roleid == GetOuterUserId())
    1028           0 :             ereport(ERROR,
    1029             :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1030             :                      errmsg("current user cannot be dropped")));
    1031         120 :         if (roleid == GetSessionUserId())
    1032           0 :             ereport(ERROR,
    1033             :                     (errcode(ERRCODE_OBJECT_IN_USE),
    1034             :                      errmsg("session user cannot be dropped")));
    1035             : 
    1036             :         /*
    1037             :          * For safety's sake, we allow createrole holders to drop ordinary
    1038             :          * roles but not superuser roles.  This is mainly to avoid the
    1039             :          * scenario where you accidentally drop the last superuser.
    1040             :          */
    1041         133 :         if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
    1042          13 :             !superuser())
    1043           0 :             ereport(ERROR,
    1044             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1045             :                      errmsg("must be superuser to drop superusers")));
    1046             : 
    1047             :         /* DROP hook for the role being removed */
    1048         120 :         InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
    1049             : 
    1050             :         /*
    1051             :          * Lock the role, so nobody can add dependencies to her while we drop
    1052             :          * her.  We keep the lock until the end of transaction.
    1053             :          */
    1054         120 :         LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
    1055             : 
    1056             :         /* Check for pg_shdepend entries depending on this role */
    1057         120 :         if (checkSharedDependencies(AuthIdRelationId, roleid,
    1058             :                                     &detail, &detail_log))
    1059          14 :             ereport(ERROR,
    1060             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    1061             :                      errmsg("role \"%s\" cannot be dropped because some objects depend on it",
    1062             :                             role),
    1063             :                      errdetail_internal("%s", detail),
    1064             :                      errdetail_log("%s", detail_log)));
    1065             : 
    1066             :         /*
    1067             :          * Remove the role from the pg_authid table
    1068             :          */
    1069         106 :         CatalogTupleDelete(pg_authid_rel, &tuple->t_self);
    1070             : 
    1071         106 :         ReleaseSysCache(tuple);
    1072             : 
    1073             :         /*
    1074             :          * Remove role from the pg_auth_members table.  We have to remove all
    1075             :          * tuples that show it as either a role or a member.
    1076             :          *
    1077             :          * XXX what about grantor entries?  Maybe we should do one heap scan.
    1078             :          */
    1079         106 :         ScanKeyInit(&scankey,
    1080             :                     Anum_pg_auth_members_roleid,
    1081             :                     BTEqualStrategyNumber, F_OIDEQ,
    1082             :                     ObjectIdGetDatum(roleid));
    1083             : 
    1084         106 :         sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
    1085             :                                    true, NULL, 1, &scankey);
    1086             : 
    1087         218 :         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
    1088             :         {
    1089           6 :             CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
    1090             :         }
    1091             : 
    1092         106 :         systable_endscan(sscan);
    1093             : 
    1094         106 :         ScanKeyInit(&scankey,
    1095             :                     Anum_pg_auth_members_member,
    1096             :                     BTEqualStrategyNumber, F_OIDEQ,
    1097             :                     ObjectIdGetDatum(roleid));
    1098             : 
    1099         106 :         sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
    1100             :                                    true, NULL, 1, &scankey);
    1101             : 
    1102         217 :         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
    1103             :         {
    1104           5 :             CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
    1105             :         }
    1106             : 
    1107         106 :         systable_endscan(sscan);
    1108             : 
    1109             :         /*
    1110             :          * Remove any comments or security labels on this role.
    1111             :          */
    1112         106 :         DeleteSharedComments(roleid, AuthIdRelationId);
    1113         106 :         DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
    1114             : 
    1115             :         /*
    1116             :          * Remove settings for this role.
    1117             :          */
    1118         106 :         DropSetting(InvalidOid, roleid);
    1119             : 
    1120             :         /*
    1121             :          * Advance command counter so that later iterations of this loop will
    1122             :          * see the changes already made.  This is essential if, for example,
    1123             :          * we are trying to drop both a role and one of its direct members ---
    1124             :          * we'll get an error if we try to delete the linking pg_auth_members
    1125             :          * tuple twice.  (We do not need a CCI between the two delete loops
    1126             :          * above, because it's not allowed for a role to directly contain
    1127             :          * itself.)
    1128             :          */
    1129         106 :         CommandCounterIncrement();
    1130             :     }
    1131             : 
    1132             :     /*
    1133             :      * Now we can clean up; but keep locks until commit.
    1134             :      */
    1135         120 :     heap_close(pg_auth_members_rel, NoLock);
    1136         120 :     heap_close(pg_authid_rel, NoLock);
    1137         120 : }
    1138             : 
    1139             : /*
    1140             :  * Rename role
    1141             :  */
    1142             : ObjectAddress
    1143           3 : RenameRole(const char *oldname, const char *newname)
    1144             : {
    1145             :     HeapTuple   oldtuple,
    1146             :                 newtuple;
    1147             :     TupleDesc   dsc;
    1148             :     Relation    rel;
    1149             :     Datum       datum;
    1150             :     bool        isnull;
    1151             :     Datum       repl_val[Natts_pg_authid];
    1152             :     bool        repl_null[Natts_pg_authid];
    1153             :     bool        repl_repl[Natts_pg_authid];
    1154             :     int         i;
    1155             :     Oid         roleid;
    1156             :     ObjectAddress address;
    1157             :     Form_pg_authid authform;
    1158             : 
    1159           3 :     rel = heap_open(AuthIdRelationId, RowExclusiveLock);
    1160           3 :     dsc = RelationGetDescr(rel);
    1161             : 
    1162           3 :     oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
    1163           3 :     if (!HeapTupleIsValid(oldtuple))
    1164           0 :         ereport(ERROR,
    1165             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1166             :                  errmsg("role \"%s\" does not exist", oldname)));
    1167             : 
    1168             :     /*
    1169             :      * XXX Client applications probably store the session user somewhere, so
    1170             :      * renaming it could cause confusion.  On the other hand, there may not be
    1171             :      * an actual problem besides a little confusion, so think about this and
    1172             :      * decide.  Same for SET ROLE ... we don't restrict renaming the current
    1173             :      * effective userid, though.
    1174             :      */
    1175             : 
    1176           3 :     roleid = HeapTupleGetOid(oldtuple);
    1177           3 :     authform = (Form_pg_authid) GETSTRUCT(oldtuple);
    1178             : 
    1179           3 :     if (roleid == GetSessionUserId())
    1180           0 :         ereport(ERROR,
    1181             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1182             :                  errmsg("session user cannot be renamed")));
    1183           3 :     if (roleid == GetOuterUserId())
    1184           0 :         ereport(ERROR,
    1185             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1186             :                  errmsg("current user cannot be renamed")));
    1187             : 
    1188             :     /*
    1189             :      * Check that the user is not trying to rename a system role and not
    1190             :      * trying to rename a role into the reserved "pg_" namespace.
    1191             :      */
    1192           3 :     if (IsReservedName(NameStr(authform->rolname)))
    1193           0 :         ereport(ERROR,
    1194             :                 (errcode(ERRCODE_RESERVED_NAME),
    1195             :                  errmsg("role name \"%s\" is reserved",
    1196             :                         NameStr(authform->rolname)),
    1197             :                  errdetail("Role names starting with \"pg_\" are reserved.")));
    1198             : 
    1199           3 :     if (IsReservedName(newname))
    1200           0 :         ereport(ERROR,
    1201             :                 (errcode(ERRCODE_RESERVED_NAME),
    1202             :                  errmsg("role name \"%s\" is reserved",
    1203             :                         newname),
    1204             :                  errdetail("Role names starting with \"pg_\" are reserved.")));
    1205             : 
    1206             :     /* make sure the new name doesn't exist */
    1207           3 :     if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
    1208           0 :         ereport(ERROR,
    1209             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    1210             :                  errmsg("role \"%s\" already exists", newname)));
    1211             : 
    1212             :     /*
    1213             :      * createrole is enough privilege unless you want to mess with a superuser
    1214             :      */
    1215           3 :     if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
    1216             :     {
    1217           1 :         if (!superuser())
    1218           0 :             ereport(ERROR,
    1219             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1220             :                      errmsg("must be superuser to rename superusers")));
    1221             :     }
    1222             :     else
    1223             :     {
    1224           2 :         if (!have_createrole_privilege())
    1225           0 :             ereport(ERROR,
    1226             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1227             :                      errmsg("permission denied to rename role")));
    1228             :     }
    1229             : 
    1230             :     /* OK, construct the modified tuple */
    1231          36 :     for (i = 0; i < Natts_pg_authid; i++)
    1232          33 :         repl_repl[i] = false;
    1233             : 
    1234           3 :     repl_repl[Anum_pg_authid_rolname - 1] = true;
    1235           3 :     repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
    1236             :                                                                CStringGetDatum(newname));
    1237           3 :     repl_null[Anum_pg_authid_rolname - 1] = false;
    1238             : 
    1239           3 :     datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
    1240             : 
    1241           3 :     if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
    1242             :     {
    1243             :         /* MD5 uses the username as salt, so just clear it on a rename */
    1244           1 :         repl_repl[Anum_pg_authid_rolpassword - 1] = true;
    1245           1 :         repl_null[Anum_pg_authid_rolpassword - 1] = true;
    1246             : 
    1247           1 :         ereport(NOTICE,
    1248             :                 (errmsg("MD5 password cleared because of role rename")));
    1249             :     }
    1250             : 
    1251           3 :     newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
    1252           3 :     CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
    1253             : 
    1254           3 :     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
    1255             : 
    1256           3 :     ObjectAddressSet(address, AuthIdRelationId, roleid);
    1257             : 
    1258           3 :     ReleaseSysCache(oldtuple);
    1259             : 
    1260             :     /*
    1261             :      * Close pg_authid, but keep lock till commit.
    1262             :      */
    1263           3 :     heap_close(rel, NoLock);
    1264             : 
    1265           3 :     return address;
    1266             : }
    1267             : 
    1268             : /*
    1269             :  * GrantRoleStmt
    1270             :  *
    1271             :  * Grant/Revoke roles to/from roles
    1272             :  */
    1273             : void
    1274          20 : GrantRole(GrantRoleStmt *stmt)
    1275             : {
    1276             :     Relation    pg_authid_rel;
    1277             :     Oid         grantor;
    1278             :     List       *grantee_ids;
    1279             :     ListCell   *item;
    1280             : 
    1281          20 :     if (stmt->grantor)
    1282           0 :         grantor = get_rolespec_oid(stmt->grantor, false);
    1283             :     else
    1284          20 :         grantor = GetUserId();
    1285             : 
    1286          20 :     grantee_ids = roleSpecsToIds(stmt->grantee_roles);
    1287             : 
    1288             :     /* AccessShareLock is enough since we aren't modifying pg_authid */
    1289          20 :     pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
    1290             : 
    1291             :     /*
    1292             :      * Step through all of the granted roles and add/remove entries for the
    1293             :      * grantees, or, if admin_opt is set, then just add/remove the admin
    1294             :      * option.
    1295             :      *
    1296             :      * Note: Permissions checking is done by AddRoleMems/DelRoleMems
    1297             :      */
    1298          36 :     foreach(item, stmt->granted_roles)
    1299             :     {
    1300          20 :         AccessPriv *priv = (AccessPriv *) lfirst(item);
    1301          20 :         char       *rolename = priv->priv_name;
    1302             :         Oid         roleid;
    1303             : 
    1304             :         /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
    1305          20 :         if (rolename == NULL || priv->cols != NIL)
    1306           0 :             ereport(ERROR,
    1307             :                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1308             :                      errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
    1309             : 
    1310          20 :         roleid = get_role_oid(rolename, false);
    1311          20 :         if (stmt->is_grant)
    1312          18 :             AddRoleMems(rolename, roleid,
    1313             :                         stmt->grantee_roles, grantee_ids,
    1314          18 :                         grantor, stmt->admin_opt);
    1315             :         else
    1316           2 :             DelRoleMems(rolename, roleid,
    1317             :                         stmt->grantee_roles, grantee_ids,
    1318           2 :                         stmt->admin_opt);
    1319             :     }
    1320             : 
    1321             :     /*
    1322             :      * Close pg_authid, but keep lock till commit.
    1323             :      */
    1324          16 :     heap_close(pg_authid_rel, NoLock);
    1325          16 : }
    1326             : 
    1327             : /*
    1328             :  * DropOwnedObjects
    1329             :  *
    1330             :  * Drop the objects owned by a given list of roles.
    1331             :  */
    1332             : void
    1333          16 : DropOwnedObjects(DropOwnedStmt *stmt)
    1334             : {
    1335          16 :     List       *role_ids = roleSpecsToIds(stmt->roles);
    1336             :     ListCell   *cell;
    1337             : 
    1338             :     /* Check privileges */
    1339          37 :     foreach(cell, role_ids)
    1340             :     {
    1341          23 :         Oid         roleid = lfirst_oid(cell);
    1342             : 
    1343          23 :         if (!has_privs_of_role(GetUserId(), roleid))
    1344           2 :             ereport(ERROR,
    1345             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1346             :                      errmsg("permission denied to drop objects")));
    1347             :     }
    1348             : 
    1349             :     /* Ok, do it */
    1350          14 :     shdepDropOwned(role_ids, stmt->behavior);
    1351          13 : }
    1352             : 
    1353             : /*
    1354             :  * ReassignOwnedObjects
    1355             :  *
    1356             :  * Give the objects owned by a given list of roles away to another user.
    1357             :  */
    1358             : void
    1359           4 : ReassignOwnedObjects(ReassignOwnedStmt *stmt)
    1360             : {
    1361           4 :     List       *role_ids = roleSpecsToIds(stmt->roles);
    1362             :     ListCell   *cell;
    1363             :     Oid         newrole;
    1364             : 
    1365             :     /* Check privileges */
    1366           7 :     foreach(cell, role_ids)
    1367             :     {
    1368           4 :         Oid         roleid = lfirst_oid(cell);
    1369             : 
    1370           4 :         if (!has_privs_of_role(GetUserId(), roleid))
    1371           1 :             ereport(ERROR,
    1372             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1373             :                      errmsg("permission denied to reassign objects")));
    1374             :     }
    1375             : 
    1376             :     /* Must have privileges on the receiving side too */
    1377           3 :     newrole = get_rolespec_oid(stmt->newrole, false);
    1378             : 
    1379           3 :     if (!has_privs_of_role(GetUserId(), newrole))
    1380           1 :         ereport(ERROR,
    1381             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1382             :                  errmsg("permission denied to reassign objects")));
    1383             : 
    1384             :     /* Ok, do it */
    1385           2 :     shdepReassignOwned(role_ids, newrole);
    1386           2 : }
    1387             : 
    1388             : /*
    1389             :  * roleSpecsToIds
    1390             :  *
    1391             :  * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
    1392             :  *
    1393             :  * ROLESPEC_PUBLIC is not allowed.
    1394             :  */
    1395             : List *
    1396         313 : roleSpecsToIds(List *memberNames)
    1397             : {
    1398         313 :     List       *result = NIL;
    1399             :     ListCell   *l;
    1400             : 
    1401         365 :     foreach(l, memberNames)
    1402             :     {
    1403          52 :         RoleSpec   *rolespec = lfirst_node(RoleSpec, l);
    1404             :         Oid         roleid;
    1405             : 
    1406          52 :         roleid = get_rolespec_oid(rolespec, false);
    1407          52 :         result = lappend_oid(result, roleid);
    1408             :     }
    1409         313 :     return result;
    1410             : }
    1411             : 
    1412             : /*
    1413             :  * AddRoleMems -- Add given members to the specified role
    1414             :  *
    1415             :  * rolename: name of role to add to (used only for error messages)
    1416             :  * roleid: OID of role to add to
    1417             :  * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
    1418             :  * memberIds: OIDs of roles to add
    1419             :  * grantorId: who is granting the membership
    1420             :  * admin_opt: granting admin option?
    1421             :  *
    1422             :  * Note: caller is responsible for calling auth_file_update_needed().
    1423             :  */
    1424             : static void
    1425         289 : AddRoleMems(const char *rolename, Oid roleid,
    1426             :             List *memberSpecs, List *memberIds,
    1427             :             Oid grantorId, bool admin_opt)
    1428             : {
    1429             :     Relation    pg_authmem_rel;
    1430             :     TupleDesc   pg_authmem_dsc;
    1431             :     ListCell   *specitem;
    1432             :     ListCell   *iditem;
    1433             : 
    1434         289 :     Assert(list_length(memberSpecs) == list_length(memberIds));
    1435             : 
    1436             :     /* Skip permission check if nothing to do */
    1437         289 :     if (!memberIds)
    1438         551 :         return;
    1439             : 
    1440             :     /*
    1441             :      * Check permissions: must have createrole or admin option on the role to
    1442             :      * be changed.  To mess with a superuser role, you gotta be superuser.
    1443             :      */
    1444          23 :     if (superuser_arg(roleid))
    1445             :     {
    1446           3 :         if (!superuser())
    1447           0 :             ereport(ERROR,
    1448             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1449             :                      errmsg("must be superuser to alter superusers")));
    1450             :     }
    1451             :     else
    1452             :     {
    1453          27 :         if (!have_createrole_privilege() &&
    1454           7 :             !is_admin_of_role(grantorId, roleid))
    1455           4 :             ereport(ERROR,
    1456             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1457             :                      errmsg("must have admin option on role \"%s\"",
    1458             :                             rolename)));
    1459             :     }
    1460             : 
    1461             :     /*
    1462             :      * The role membership grantor of record has little significance at
    1463             :      * present.  Nonetheless, inasmuch as users might look to it for a crude
    1464             :      * audit trail, let only superusers impute the grant to a third party.
    1465             :      *
    1466             :      * Before lifting this restriction, give the member == role case of
    1467             :      * is_admin_of_role() a fresh look.  Ensure that the current role cannot
    1468             :      * use an explicit grantor specification to take advantage of the session
    1469             :      * user's self-admin right.
    1470             :      */
    1471          19 :     if (grantorId != GetUserId() && !superuser())
    1472           0 :         ereport(ERROR,
    1473             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1474             :                  errmsg("must be superuser to set grantor")));
    1475             : 
    1476          19 :     pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
    1477          19 :     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
    1478             : 
    1479          39 :     forboth(specitem, memberSpecs, iditem, memberIds)
    1480             :     {
    1481          20 :         RoleSpec   *memberRole = lfirst(specitem);
    1482          20 :         Oid         memberid = lfirst_oid(iditem);
    1483             :         HeapTuple   authmem_tuple;
    1484             :         HeapTuple   tuple;
    1485             :         Datum       new_record[Natts_pg_auth_members];
    1486             :         bool        new_record_nulls[Natts_pg_auth_members];
    1487             :         bool        new_record_repl[Natts_pg_auth_members];
    1488             : 
    1489             :         /*
    1490             :          * Refuse creation of membership loops, including the trivial case
    1491             :          * where a role is made a member of itself.  We do this by checking to
    1492             :          * see if the target role is already a member of the proposed member
    1493             :          * role.  We have to ignore possible superuserness, however, else we
    1494             :          * could never grant membership in a superuser-privileged role.
    1495             :          */
    1496          20 :         if (is_member_of_role_nosuper(roleid, memberid))
    1497           0 :             ereport(ERROR,
    1498             :                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1499             :                      (errmsg("role \"%s\" is a member of role \"%s\"",
    1500             :                              rolename, get_rolespec_name(memberRole)))));
    1501             : 
    1502             :         /*
    1503             :          * Check if entry for this role/member already exists; if so, give
    1504             :          * warning unless we are adding admin option.
    1505             :          */
    1506          20 :         authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
    1507             :                                         ObjectIdGetDatum(roleid),
    1508             :                                         ObjectIdGetDatum(memberid));
    1509          20 :         if (HeapTupleIsValid(authmem_tuple) &&
    1510           0 :             (!admin_opt ||
    1511           0 :              ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
    1512             :         {
    1513           3 :             ereport(NOTICE,
    1514             :                     (errmsg("role \"%s\" is already a member of role \"%s\"",
    1515             :                             get_rolespec_name(memberRole), rolename)));
    1516           3 :             ReleaseSysCache(authmem_tuple);
    1517           3 :             continue;
    1518             :         }
    1519             : 
    1520             :         /* Build a tuple to insert or update */
    1521          17 :         MemSet(new_record, 0, sizeof(new_record));
    1522          17 :         MemSet(new_record_nulls, false, sizeof(new_record_nulls));
    1523          17 :         MemSet(new_record_repl, false, sizeof(new_record_repl));
    1524             : 
    1525          17 :         new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
    1526          17 :         new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
    1527          17 :         new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
    1528          17 :         new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
    1529             : 
    1530          17 :         if (HeapTupleIsValid(authmem_tuple))
    1531             :         {
    1532           0 :             new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
    1533           0 :             new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
    1534           0 :             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
    1535             :                                       new_record,
    1536             :                                       new_record_nulls, new_record_repl);
    1537           0 :             CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
    1538           0 :             ReleaseSysCache(authmem_tuple);
    1539             :         }
    1540             :         else
    1541             :         {
    1542          17 :             tuple = heap_form_tuple(pg_authmem_dsc,
    1543             :                                     new_record, new_record_nulls);
    1544          17 :             CatalogTupleInsert(pg_authmem_rel, tuple);
    1545             :         }
    1546             : 
    1547             :         /* CCI after each change, in case there are duplicates in list */
    1548          17 :         CommandCounterIncrement();
    1549             :     }
    1550             : 
    1551             :     /*
    1552             :      * Close pg_authmem, but keep lock till commit.
    1553             :      */
    1554          19 :     heap_close(pg_authmem_rel, NoLock);
    1555             : }
    1556             : 
    1557             : /*
    1558             :  * DelRoleMems -- Remove given members from the specified role
    1559             :  *
    1560             :  * rolename: name of role to del from (used only for error messages)
    1561             :  * roleid: OID of role to del from
    1562             :  * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
    1563             :  * memberIds: OIDs of roles to del
    1564             :  * admin_opt: remove admin option only?
    1565             :  *
    1566             :  * Note: caller is responsible for calling auth_file_update_needed().
    1567             :  */
    1568             : static void
    1569           3 : DelRoleMems(const char *rolename, Oid roleid,
    1570             :             List *memberSpecs, List *memberIds,
    1571             :             bool admin_opt)
    1572             : {
    1573             :     Relation    pg_authmem_rel;
    1574             :     TupleDesc   pg_authmem_dsc;
    1575             :     ListCell   *specitem;
    1576             :     ListCell   *iditem;
    1577             : 
    1578           3 :     Assert(list_length(memberSpecs) == list_length(memberIds));
    1579             : 
    1580             :     /* Skip permission check if nothing to do */
    1581           3 :     if (!memberIds)
    1582           3 :         return;
    1583             : 
    1584             :     /*
    1585             :      * Check permissions: must have createrole or admin option on the role to
    1586             :      * be changed.  To mess with a superuser role, you gotta be superuser.
    1587             :      */
    1588           3 :     if (superuser_arg(roleid))
    1589             :     {
    1590           0 :         if (!superuser())
    1591           0 :             ereport(ERROR,
    1592             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1593             :                      errmsg("must be superuser to alter superusers")));
    1594             :     }
    1595             :     else
    1596             :     {
    1597           4 :         if (!have_createrole_privilege() &&
    1598           1 :             !is_admin_of_role(GetUserId(), roleid))
    1599           0 :             ereport(ERROR,
    1600             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1601             :                      errmsg("must have admin option on role \"%s\"",
    1602             :                             rolename)));
    1603             :     }
    1604             : 
    1605           3 :     pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
    1606           3 :     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
    1607             : 
    1608           6 :     forboth(specitem, memberSpecs, iditem, memberIds)
    1609             :     {
    1610           3 :         RoleSpec   *memberRole = lfirst(specitem);
    1611           3 :         Oid         memberid = lfirst_oid(iditem);
    1612             :         HeapTuple   authmem_tuple;
    1613             : 
    1614             :         /*
    1615             :          * Find entry for this role/member
    1616             :          */
    1617           3 :         authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
    1618             :                                         ObjectIdGetDatum(roleid),
    1619             :                                         ObjectIdGetDatum(memberid));
    1620           3 :         if (!HeapTupleIsValid(authmem_tuple))
    1621             :         {
    1622           0 :             ereport(WARNING,
    1623             :                     (errmsg("role \"%s\" is not a member of role \"%s\"",
    1624             :                             get_rolespec_name(memberRole), rolename)));
    1625           0 :             continue;
    1626             :         }
    1627             : 
    1628           3 :         if (!admin_opt)
    1629             :         {
    1630             :             /* Remove the entry altogether */
    1631           3 :             CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
    1632             :         }
    1633             :         else
    1634             :         {
    1635             :             /* Just turn off the admin option */
    1636             :             HeapTuple   tuple;
    1637             :             Datum       new_record[Natts_pg_auth_members];
    1638             :             bool        new_record_nulls[Natts_pg_auth_members];
    1639             :             bool        new_record_repl[Natts_pg_auth_members];
    1640             : 
    1641             :             /* Build a tuple to update with */
    1642           0 :             MemSet(new_record, 0, sizeof(new_record));
    1643           0 :             MemSet(new_record_nulls, false, sizeof(new_record_nulls));
    1644           0 :             MemSet(new_record_repl, false, sizeof(new_record_repl));
    1645             : 
    1646           0 :             new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
    1647           0 :             new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
    1648             : 
    1649           0 :             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
    1650             :                                       new_record,
    1651             :                                       new_record_nulls, new_record_repl);
    1652           0 :             CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
    1653             :         }
    1654             : 
    1655           3 :         ReleaseSysCache(authmem_tuple);
    1656             : 
    1657             :         /* CCI after each change, in case there are duplicates in list */
    1658           3 :         CommandCounterIncrement();
    1659             :     }
    1660             : 
    1661             :     /*
    1662             :      * Close pg_authmem, but keep lock till commit.
    1663             :      */
    1664           3 :     heap_close(pg_authmem_rel, NoLock);
    1665             : }

Generated by: LCOV version 1.11