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

Generated by: LCOV version 1.13