1 /*-------------------------------------------------------------------------
4 * Commands for manipulating roles (formerly called users).
6 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * src/backend/commands/user.c
11 *-------------------------------------------------------------------------
15 #include "access/genam.h"
16 #include "access/htup_details.h"
17 #include "access/table.h"
18 #include "access/xact.h"
19 #include "catalog/binary_upgrade.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/objectaccess.h"
24 #include "catalog/pg_auth_members.h"
25 #include "catalog/pg_authid.h"
26 #include "catalog/pg_database.h"
27 #include "catalog/pg_db_role_setting.h"
28 #include "commands/comment.h"
29 #include "commands/dbcommands.h"
30 #include "commands/defrem.h"
31 #include "commands/seclabel.h"
32 #include "commands/user.h"
33 #include "libpq/crypt.h"
34 #include "miscadmin.h"
35 #include "storage/lmgr.h"
36 #include "utils/acl.h"
37 #include "utils/builtins.h"
38 #include "utils/fmgroids.h"
39 #include "utils/syscache.h"
40 #include "utils/timestamp.h"
42 /* Potentially set by pg_upgrade_support functions */
43 Oid binary_upgrade_next_pg_authid_oid
= InvalidOid
;
47 int Password_encryption
= PASSWORD_TYPE_SCRAM_SHA_256
;
49 /* Hook to check passwords in CreateRole() and AlterRole() */
50 check_password_hook_type check_password_hook
= NULL
;
52 static void AddRoleMems(const char *rolename
, Oid roleid
,
53 List
*memberSpecs
, List
*memberIds
,
54 Oid grantorId
, bool admin_opt
);
55 static void DelRoleMems(const char *rolename
, Oid roleid
,
56 List
*memberSpecs
, List
*memberIds
,
60 /* Check if current user has createrole privileges */
62 have_createrole_privilege(void)
64 return has_createrole_privilege(GetUserId());
72 CreateRole(ParseState
*pstate
, CreateRoleStmt
*stmt
)
74 Relation pg_authid_rel
;
75 TupleDesc pg_authid_dsc
;
77 Datum new_record
[Natts_pg_authid
];
78 bool new_record_nulls
[Natts_pg_authid
];
82 char *password
= NULL
; /* user password */
83 bool issuper
= false; /* Make the user a superuser? */
84 bool inherit
= true; /* Auto inherit privileges? */
85 bool createrole
= false; /* Can this user create roles? */
86 bool createdb
= false; /* Can the user create databases? */
87 bool canlogin
= false; /* Can this user login? */
88 bool isreplication
= false; /* Is this a replication role? */
89 bool bypassrls
= false; /* Is this a row security enabled role? */
90 int connlimit
= -1; /* maximum connections allowed */
91 List
*addroleto
= NIL
; /* roles to make this a member of */
92 List
*rolemembers
= NIL
; /* roles to be members of this role */
93 List
*adminmembers
= NIL
; /* roles to be admins of this role */
94 char *validUntil
= NULL
; /* time the login is valid until */
95 Datum validUntil_datum
; /* same, as timestamptz Datum */
97 DefElem
*dpassword
= NULL
;
98 DefElem
*dissuper
= NULL
;
99 DefElem
*dinherit
= NULL
;
100 DefElem
*dcreaterole
= NULL
;
101 DefElem
*dcreatedb
= NULL
;
102 DefElem
*dcanlogin
= NULL
;
103 DefElem
*disreplication
= NULL
;
104 DefElem
*dconnlimit
= NULL
;
105 DefElem
*daddroleto
= NULL
;
106 DefElem
*drolemembers
= NULL
;
107 DefElem
*dadminmembers
= NULL
;
108 DefElem
*dvalidUntil
= NULL
;
109 DefElem
*dbypassRLS
= NULL
;
111 /* The defaults can vary depending on the original statement type */
112 switch (stmt
->stmt_type
)
118 /* may eventually want inherit to default to false here */
124 /* Extract options from the statement node tree */
125 foreach(option
, stmt
->options
)
127 DefElem
*defel
= (DefElem
*) lfirst(option
);
129 if (strcmp(defel
->defname
, "password") == 0)
132 errorConflictingDefElem(defel
, pstate
);
135 else if (strcmp(defel
->defname
, "sysid") == 0)
138 (errmsg("SYSID can no longer be specified")));
140 else if (strcmp(defel
->defname
, "superuser") == 0)
143 errorConflictingDefElem(defel
, pstate
);
146 else if (strcmp(defel
->defname
, "inherit") == 0)
149 errorConflictingDefElem(defel
, pstate
);
152 else if (strcmp(defel
->defname
, "createrole") == 0)
155 errorConflictingDefElem(defel
, pstate
);
158 else if (strcmp(defel
->defname
, "createdb") == 0)
161 errorConflictingDefElem(defel
, pstate
);
164 else if (strcmp(defel
->defname
, "canlogin") == 0)
167 errorConflictingDefElem(defel
, pstate
);
170 else if (strcmp(defel
->defname
, "isreplication") == 0)
173 errorConflictingDefElem(defel
, pstate
);
174 disreplication
= defel
;
176 else if (strcmp(defel
->defname
, "connectionlimit") == 0)
179 errorConflictingDefElem(defel
, pstate
);
182 else if (strcmp(defel
->defname
, "addroleto") == 0)
185 errorConflictingDefElem(defel
, pstate
);
188 else if (strcmp(defel
->defname
, "rolemembers") == 0)
191 errorConflictingDefElem(defel
, pstate
);
192 drolemembers
= defel
;
194 else if (strcmp(defel
->defname
, "adminmembers") == 0)
197 errorConflictingDefElem(defel
, pstate
);
198 dadminmembers
= defel
;
200 else if (strcmp(defel
->defname
, "validUntil") == 0)
203 errorConflictingDefElem(defel
, pstate
);
206 else if (strcmp(defel
->defname
, "bypassrls") == 0)
209 errorConflictingDefElem(defel
, pstate
);
213 elog(ERROR
, "option \"%s\" not recognized",
217 if (dpassword
&& dpassword
->arg
)
218 password
= strVal(dpassword
->arg
);
220 issuper
= intVal(dissuper
->arg
) != 0;
222 inherit
= intVal(dinherit
->arg
) != 0;
224 createrole
= intVal(dcreaterole
->arg
) != 0;
226 createdb
= intVal(dcreatedb
->arg
) != 0;
228 canlogin
= intVal(dcanlogin
->arg
) != 0;
230 isreplication
= intVal(disreplication
->arg
) != 0;
233 connlimit
= intVal(dconnlimit
->arg
);
236 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
237 errmsg("invalid connection limit: %d", connlimit
)));
240 addroleto
= (List
*) daddroleto
->arg
;
242 rolemembers
= (List
*) drolemembers
->arg
;
244 adminmembers
= (List
*) dadminmembers
->arg
;
246 validUntil
= strVal(dvalidUntil
->arg
);
248 bypassrls
= intVal(dbypassRLS
->arg
) != 0;
250 /* Check some permissions first */
255 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
256 errmsg("must be superuser to create superusers")));
258 else if (isreplication
)
262 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
263 errmsg("must be superuser to create replication users")));
269 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
270 errmsg("must be superuser to create bypassrls users")));
274 if (!have_createrole_privilege())
276 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
277 errmsg("permission denied to create role")));
281 * Check that the user is not trying to create a role in the reserved
284 if (IsReservedName(stmt
->role
))
286 (errcode(ERRCODE_RESERVED_NAME
),
287 errmsg("role name \"%s\" is reserved",
289 errdetail("Role names starting with \"pg_\" are reserved.")));
292 * If built with appropriate switch, whine when regression-testing
293 * conventions for role names are violated.
295 #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
296 if (strncmp(stmt
->role
, "regress_", 8) != 0)
297 elog(WARNING
, "roles created by regression test cases should have names starting with \"regress_\"");
301 * Check the pg_authid relation to be certain the role doesn't already
304 pg_authid_rel
= table_open(AuthIdRelationId
, RowExclusiveLock
);
305 pg_authid_dsc
= RelationGetDescr(pg_authid_rel
);
307 if (OidIsValid(get_role_oid(stmt
->role
, true)))
309 (errcode(ERRCODE_DUPLICATE_OBJECT
),
310 errmsg("role \"%s\" already exists",
313 /* Convert validuntil to internal form */
316 validUntil_datum
= DirectFunctionCall3(timestamptz_in
,
317 CStringGetDatum(validUntil
),
318 ObjectIdGetDatum(InvalidOid
),
320 validUntil_null
= false;
324 validUntil_datum
= (Datum
) 0;
325 validUntil_null
= true;
329 * Call the password checking hook if there is one defined
331 if (check_password_hook
&& password
)
332 (*check_password_hook
) (stmt
->role
,
334 get_password_type(password
),
339 * Build a tuple to insert
341 MemSet(new_record
, 0, sizeof(new_record
));
342 MemSet(new_record_nulls
, false, sizeof(new_record_nulls
));
344 new_record
[Anum_pg_authid_rolname
- 1] =
345 DirectFunctionCall1(namein
, CStringGetDatum(stmt
->role
));
347 new_record
[Anum_pg_authid_rolsuper
- 1] = BoolGetDatum(issuper
);
348 new_record
[Anum_pg_authid_rolinherit
- 1] = BoolGetDatum(inherit
);
349 new_record
[Anum_pg_authid_rolcreaterole
- 1] = BoolGetDatum(createrole
);
350 new_record
[Anum_pg_authid_rolcreatedb
- 1] = BoolGetDatum(createdb
);
351 new_record
[Anum_pg_authid_rolcanlogin
- 1] = BoolGetDatum(canlogin
);
352 new_record
[Anum_pg_authid_rolreplication
- 1] = BoolGetDatum(isreplication
);
353 new_record
[Anum_pg_authid_rolconnlimit
- 1] = Int32GetDatum(connlimit
);
361 * Don't allow an empty password. Libpq treats an empty password the
362 * same as no password at all, and won't even try to authenticate. But
363 * other clients might, so allowing it would be confusing. By clearing
364 * the password when an empty string is specified, the account is
365 * consistently locked for all clients.
367 * Note that this only covers passwords stored in the database itself.
368 * There are also checks in the authentication code, to forbid an
369 * empty password from being used with authentication methods that
370 * fetch the password from an external system, like LDAP or PAM.
372 if (password
[0] == '\0' ||
373 plain_crypt_verify(stmt
->role
, password
, "", &logdetail
) == STATUS_OK
)
376 (errmsg("empty string is not a valid password, clearing password")));
377 new_record_nulls
[Anum_pg_authid_rolpassword
- 1] = true;
381 /* Encrypt the password to the requested format. */
382 shadow_pass
= encrypt_password(Password_encryption
, stmt
->role
,
384 new_record
[Anum_pg_authid_rolpassword
- 1] =
385 CStringGetTextDatum(shadow_pass
);
389 new_record_nulls
[Anum_pg_authid_rolpassword
- 1] = true;
391 new_record
[Anum_pg_authid_rolvaliduntil
- 1] = validUntil_datum
;
392 new_record_nulls
[Anum_pg_authid_rolvaliduntil
- 1] = validUntil_null
;
394 new_record
[Anum_pg_authid_rolbypassrls
- 1] = BoolGetDatum(bypassrls
);
397 * pg_largeobject_metadata contains pg_authid.oid's, so we use the
398 * binary-upgrade override.
402 if (!OidIsValid(binary_upgrade_next_pg_authid_oid
))
404 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
405 errmsg("pg_authid OID value not set when in binary upgrade mode")));
407 roleid
= binary_upgrade_next_pg_authid_oid
;
408 binary_upgrade_next_pg_authid_oid
= InvalidOid
;
412 roleid
= GetNewOidWithIndex(pg_authid_rel
, AuthIdOidIndexId
,
416 new_record
[Anum_pg_authid_oid
- 1] = ObjectIdGetDatum(roleid
);
418 tuple
= heap_form_tuple(pg_authid_dsc
, new_record
, new_record_nulls
);
421 * Insert new record in the pg_authid table
423 CatalogTupleInsert(pg_authid_rel
, tuple
);
426 * Advance command counter so we can see new record; else tests in
427 * AddRoleMems may fail.
429 if (addroleto
|| adminmembers
|| rolemembers
)
430 CommandCounterIncrement();
433 * Add the new role to the specified existing roles.
437 RoleSpec
*thisrole
= makeNode(RoleSpec
);
438 List
*thisrole_list
= list_make1(thisrole
);
439 List
*thisrole_oidlist
= list_make1_oid(roleid
);
441 thisrole
->roletype
= ROLESPEC_CSTRING
;
442 thisrole
->rolename
= stmt
->role
;
443 thisrole
->location
= -1;
445 foreach(item
, addroleto
)
447 RoleSpec
*oldrole
= lfirst(item
);
448 HeapTuple oldroletup
= get_rolespec_tuple(oldrole
);
449 Form_pg_authid oldroleform
= (Form_pg_authid
) GETSTRUCT(oldroletup
);
450 Oid oldroleid
= oldroleform
->oid
;
451 char *oldrolename
= NameStr(oldroleform
->rolname
);
453 AddRoleMems(oldrolename
, oldroleid
,
458 ReleaseSysCache(oldroletup
);
463 * Add the specified members to this new role. adminmembers get the admin
464 * option, rolemembers don't.
466 AddRoleMems(stmt
->role
, roleid
,
467 adminmembers
, roleSpecsToIds(adminmembers
),
469 AddRoleMems(stmt
->role
, roleid
,
470 rolemembers
, roleSpecsToIds(rolemembers
),
473 /* Post creation hook for new role */
474 InvokeObjectPostCreateHook(AuthIdRelationId
, roleid
, 0);
477 * Close pg_authid, but keep lock till commit.
479 table_close(pg_authid_rel
, NoLock
);
488 * Note: the rolemembers option accepted here is intended to support the
489 * backwards-compatible ALTER GROUP syntax. Although it will work to say
490 * "ALTER ROLE role ROLE rolenames", we don't document it.
493 AlterRole(ParseState
*pstate
, AlterRoleStmt
*stmt
)
495 Datum new_record
[Natts_pg_authid
];
496 bool new_record_nulls
[Natts_pg_authid
];
497 bool new_record_repl
[Natts_pg_authid
];
498 Relation pg_authid_rel
;
499 TupleDesc pg_authid_dsc
;
502 Form_pg_authid authform
;
504 char *rolename
= NULL
;
505 char *password
= NULL
; /* user password */
506 int issuper
= -1; /* Make the user a superuser? */
507 int inherit
= -1; /* Auto inherit privileges? */
508 int createrole
= -1; /* Can this user create roles? */
509 int createdb
= -1; /* Can the user create databases? */
510 int canlogin
= -1; /* Can this user login? */
511 int isreplication
= -1; /* Is this a replication role? */
512 int connlimit
= -1; /* maximum connections allowed */
513 List
*rolemembers
= NIL
; /* roles to be added/removed */
514 char *validUntil
= NULL
; /* time the login is valid until */
515 Datum validUntil_datum
; /* same, as timestamptz Datum */
516 bool validUntil_null
;
518 DefElem
*dpassword
= NULL
;
519 DefElem
*dissuper
= NULL
;
520 DefElem
*dinherit
= NULL
;
521 DefElem
*dcreaterole
= NULL
;
522 DefElem
*dcreatedb
= NULL
;
523 DefElem
*dcanlogin
= NULL
;
524 DefElem
*disreplication
= NULL
;
525 DefElem
*dconnlimit
= NULL
;
526 DefElem
*drolemembers
= NULL
;
527 DefElem
*dvalidUntil
= NULL
;
528 DefElem
*dbypassRLS
= NULL
;
531 check_rolespec_name(stmt
->role
,
532 "Cannot alter reserved roles.");
534 /* Extract options from the statement node tree */
535 foreach(option
, stmt
->options
)
537 DefElem
*defel
= (DefElem
*) lfirst(option
);
539 if (strcmp(defel
->defname
, "password") == 0)
542 errorConflictingDefElem(defel
, pstate
);
545 else if (strcmp(defel
->defname
, "superuser") == 0)
548 errorConflictingDefElem(defel
, pstate
);
551 else if (strcmp(defel
->defname
, "inherit") == 0)
554 errorConflictingDefElem(defel
, pstate
);
557 else if (strcmp(defel
->defname
, "createrole") == 0)
560 errorConflictingDefElem(defel
, pstate
);
563 else if (strcmp(defel
->defname
, "createdb") == 0)
566 errorConflictingDefElem(defel
, pstate
);
569 else if (strcmp(defel
->defname
, "canlogin") == 0)
572 errorConflictingDefElem(defel
, pstate
);
575 else if (strcmp(defel
->defname
, "isreplication") == 0)
578 errorConflictingDefElem(defel
, pstate
);
579 disreplication
= defel
;
581 else if (strcmp(defel
->defname
, "connectionlimit") == 0)
584 errorConflictingDefElem(defel
, pstate
);
587 else if (strcmp(defel
->defname
, "rolemembers") == 0 &&
591 errorConflictingDefElem(defel
, pstate
);
592 drolemembers
= defel
;
594 else if (strcmp(defel
->defname
, "validUntil") == 0)
597 errorConflictingDefElem(defel
, pstate
);
600 else if (strcmp(defel
->defname
, "bypassrls") == 0)
603 errorConflictingDefElem(defel
, pstate
);
607 elog(ERROR
, "option \"%s\" not recognized",
611 if (dpassword
&& dpassword
->arg
)
612 password
= strVal(dpassword
->arg
);
614 issuper
= intVal(dissuper
->arg
);
616 inherit
= intVal(dinherit
->arg
);
618 createrole
= intVal(dcreaterole
->arg
);
620 createdb
= intVal(dcreatedb
->arg
);
622 canlogin
= intVal(dcanlogin
->arg
);
624 isreplication
= intVal(disreplication
->arg
);
627 connlimit
= intVal(dconnlimit
->arg
);
630 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
631 errmsg("invalid connection limit: %d", connlimit
)));
634 rolemembers
= (List
*) drolemembers
->arg
;
636 validUntil
= strVal(dvalidUntil
->arg
);
638 bypassrls
= intVal(dbypassRLS
->arg
);
641 * Scan the pg_authid relation to be certain the user exists.
643 pg_authid_rel
= table_open(AuthIdRelationId
, RowExclusiveLock
);
644 pg_authid_dsc
= RelationGetDescr(pg_authid_rel
);
646 tuple
= get_rolespec_tuple(stmt
->role
);
647 authform
= (Form_pg_authid
) GETSTRUCT(tuple
);
648 rolename
= pstrdup(NameStr(authform
->rolname
));
649 roleid
= authform
->oid
;
652 * To mess with a superuser or replication role in any way you gotta be
653 * superuser. We also insist on superuser to change the BYPASSRLS
654 * property. Otherwise, if you don't have createrole, you're only allowed
655 * to change your own password.
657 if (authform
->rolsuper
|| issuper
>= 0)
661 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
662 errmsg("must be superuser to alter superuser roles or change superuser attribute")));
664 else if (authform
->rolreplication
|| isreplication
>= 0)
668 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
669 errmsg("must be superuser to alter replication roles or change replication attribute")));
671 else if (bypassrls
>= 0)
675 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
676 errmsg("must be superuser to change bypassrls attribute")));
678 else if (!have_createrole_privilege())
680 /* We already checked issuper, isreplication, and bypassrls */
689 roleid
== GetUserId()))
691 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
692 errmsg("permission denied")));
695 /* Convert validuntil to internal form */
698 validUntil_datum
= DirectFunctionCall3(timestamptz_in
,
699 CStringGetDatum(validUntil
),
700 ObjectIdGetDatum(InvalidOid
),
702 validUntil_null
= false;
706 /* fetch existing setting in case hook needs it */
707 validUntil_datum
= SysCacheGetAttr(AUTHNAME
, tuple
,
708 Anum_pg_authid_rolvaliduntil
,
713 * Call the password checking hook if there is one defined
715 if (check_password_hook
&& password
)
716 (*check_password_hook
) (rolename
,
718 get_password_type(password
),
723 * Build an updated tuple, perusing the information just obtained
725 MemSet(new_record
, 0, sizeof(new_record
));
726 MemSet(new_record_nulls
, false, sizeof(new_record_nulls
));
727 MemSet(new_record_repl
, false, sizeof(new_record_repl
));
730 * issuper/createrole/etc
734 new_record
[Anum_pg_authid_rolsuper
- 1] = BoolGetDatum(issuper
> 0);
735 new_record_repl
[Anum_pg_authid_rolsuper
- 1] = true;
740 new_record
[Anum_pg_authid_rolinherit
- 1] = BoolGetDatum(inherit
> 0);
741 new_record_repl
[Anum_pg_authid_rolinherit
- 1] = true;
746 new_record
[Anum_pg_authid_rolcreaterole
- 1] = BoolGetDatum(createrole
> 0);
747 new_record_repl
[Anum_pg_authid_rolcreaterole
- 1] = true;
752 new_record
[Anum_pg_authid_rolcreatedb
- 1] = BoolGetDatum(createdb
> 0);
753 new_record_repl
[Anum_pg_authid_rolcreatedb
- 1] = true;
758 new_record
[Anum_pg_authid_rolcanlogin
- 1] = BoolGetDatum(canlogin
> 0);
759 new_record_repl
[Anum_pg_authid_rolcanlogin
- 1] = true;
762 if (isreplication
>= 0)
764 new_record
[Anum_pg_authid_rolreplication
- 1] = BoolGetDatum(isreplication
> 0);
765 new_record_repl
[Anum_pg_authid_rolreplication
- 1] = true;
770 new_record
[Anum_pg_authid_rolconnlimit
- 1] = Int32GetDatum(connlimit
);
771 new_record_repl
[Anum_pg_authid_rolconnlimit
- 1] = true;
780 /* Like in CREATE USER, don't allow an empty password. */
781 if (password
[0] == '\0' ||
782 plain_crypt_verify(rolename
, password
, "", &logdetail
) == STATUS_OK
)
785 (errmsg("empty string is not a valid password, clearing password")));
786 new_record_nulls
[Anum_pg_authid_rolpassword
- 1] = true;
790 /* Encrypt the password to the requested format. */
791 shadow_pass
= encrypt_password(Password_encryption
, rolename
,
793 new_record
[Anum_pg_authid_rolpassword
- 1] =
794 CStringGetTextDatum(shadow_pass
);
796 new_record_repl
[Anum_pg_authid_rolpassword
- 1] = true;
800 if (dpassword
&& dpassword
->arg
== NULL
)
802 new_record_repl
[Anum_pg_authid_rolpassword
- 1] = true;
803 new_record_nulls
[Anum_pg_authid_rolpassword
- 1] = true;
807 new_record
[Anum_pg_authid_rolvaliduntil
- 1] = validUntil_datum
;
808 new_record_nulls
[Anum_pg_authid_rolvaliduntil
- 1] = validUntil_null
;
809 new_record_repl
[Anum_pg_authid_rolvaliduntil
- 1] = true;
813 new_record
[Anum_pg_authid_rolbypassrls
- 1] = BoolGetDatum(bypassrls
> 0);
814 new_record_repl
[Anum_pg_authid_rolbypassrls
- 1] = true;
817 new_tuple
= heap_modify_tuple(tuple
, pg_authid_dsc
, new_record
,
818 new_record_nulls
, new_record_repl
);
819 CatalogTupleUpdate(pg_authid_rel
, &tuple
->t_self
, new_tuple
);
821 InvokeObjectPostAlterHook(AuthIdRelationId
, roleid
, 0);
823 ReleaseSysCache(tuple
);
824 heap_freetuple(new_tuple
);
827 * Advance command counter so we can see new record; else tests in
828 * AddRoleMems may fail.
831 CommandCounterIncrement();
833 if (stmt
->action
== +1) /* add members to role */
834 AddRoleMems(rolename
, roleid
,
835 rolemembers
, roleSpecsToIds(rolemembers
),
837 else if (stmt
->action
== -1) /* drop members from role */
838 DelRoleMems(rolename
, roleid
,
839 rolemembers
, roleSpecsToIds(rolemembers
),
843 * Close pg_authid, but keep lock till commit.
845 table_close(pg_authid_rel
, NoLock
);
855 AlterRoleSet(AlterRoleSetStmt
*stmt
)
858 Form_pg_authid roleform
;
859 Oid databaseid
= InvalidOid
;
860 Oid roleid
= InvalidOid
;
864 check_rolespec_name(stmt
->role
,
865 "Cannot alter reserved roles.");
867 roletuple
= get_rolespec_tuple(stmt
->role
);
868 roleform
= (Form_pg_authid
) GETSTRUCT(roletuple
);
869 roleid
= roleform
->oid
;
872 * Obtain a lock on the role and make sure it didn't go away in the
875 shdepLockAndCheckObject(AuthIdRelationId
, roleid
);
878 * To mess with a superuser you gotta be superuser; else you need
879 * createrole, or just want to change your own settings
881 if (roleform
->rolsuper
)
885 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
886 errmsg("must be superuser to alter superusers")));
890 if (!have_createrole_privilege() && roleid
!= GetUserId())
892 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
893 errmsg("permission denied")));
896 ReleaseSysCache(roletuple
);
899 /* look up and lock the database, if specified */
900 if (stmt
->database
!= NULL
)
902 databaseid
= get_database_oid(stmt
->database
, false);
903 shdepLockAndCheckObject(DatabaseRelationId
, databaseid
);
908 * If no role is specified, then this is effectively the same as
909 * ALTER DATABASE ... SET, so use the same permission check.
911 if (!pg_database_ownercheck(databaseid
, GetUserId()))
912 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_DATABASE
,
917 if (!stmt
->role
&& !stmt
->database
)
919 /* Must be superuser to alter settings globally. */
922 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
923 errmsg("must be superuser to alter settings globally")));
926 AlterSetting(databaseid
, roleid
, stmt
->setstmt
);
936 DropRole(DropRoleStmt
*stmt
)
938 Relation pg_authid_rel
,
942 if (!have_createrole_privilege())
944 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
945 errmsg("permission denied to drop role")));
948 * Scan the pg_authid relation to find the Oid of the role(s) to be
951 pg_authid_rel
= table_open(AuthIdRelationId
, RowExclusiveLock
);
952 pg_auth_members_rel
= table_open(AuthMemRelationId
, RowExclusiveLock
);
954 foreach(item
, stmt
->roles
)
956 RoleSpec
*rolspec
= lfirst(item
);
960 Form_pg_authid roleform
;
967 if (rolspec
->roletype
!= ROLESPEC_CSTRING
)
969 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
970 errmsg("cannot use special role specifier in DROP ROLE")));
971 role
= rolspec
->rolename
;
973 tuple
= SearchSysCache1(AUTHNAME
, PointerGetDatum(role
));
974 if (!HeapTupleIsValid(tuple
))
976 if (!stmt
->missing_ok
)
979 (errcode(ERRCODE_UNDEFINED_OBJECT
),
980 errmsg("role \"%s\" does not exist", role
)));
985 (errmsg("role \"%s\" does not exist, skipping",
992 roleform
= (Form_pg_authid
) GETSTRUCT(tuple
);
993 roleid
= roleform
->oid
;
995 if (roleid
== GetUserId())
997 (errcode(ERRCODE_OBJECT_IN_USE
),
998 errmsg("current user cannot be dropped")));
999 if (roleid
== GetOuterUserId())
1001 (errcode(ERRCODE_OBJECT_IN_USE
),
1002 errmsg("current user cannot be dropped")));
1003 if (roleid
== GetSessionUserId())
1005 (errcode(ERRCODE_OBJECT_IN_USE
),
1006 errmsg("session user cannot be dropped")));
1009 * For safety's sake, we allow createrole holders to drop ordinary
1010 * roles but not superuser roles. This is mainly to avoid the
1011 * scenario where you accidentally drop the last superuser.
1013 if (roleform
->rolsuper
&& !superuser())
1015 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1016 errmsg("must be superuser to drop superusers")));
1018 /* DROP hook for the role being removed */
1019 InvokeObjectDropHook(AuthIdRelationId
, roleid
, 0);
1022 * Lock the role, so nobody can add dependencies to her while we drop
1023 * her. We keep the lock until the end of transaction.
1025 LockSharedObject(AuthIdRelationId
, roleid
, 0, AccessExclusiveLock
);
1027 /* Check for pg_shdepend entries depending on this role */
1028 if (checkSharedDependencies(AuthIdRelationId
, roleid
,
1029 &detail
, &detail_log
))
1031 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
1032 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
1034 errdetail_internal("%s", detail
),
1035 errdetail_log("%s", detail_log
)));
1038 * Remove the role from the pg_authid table
1040 CatalogTupleDelete(pg_authid_rel
, &tuple
->t_self
);
1042 ReleaseSysCache(tuple
);
1045 * Remove role from the pg_auth_members table. We have to remove all
1046 * tuples that show it as either a role or a member.
1048 * XXX what about grantor entries? Maybe we should do one heap scan.
1050 ScanKeyInit(&scankey
,
1051 Anum_pg_auth_members_roleid
,
1052 BTEqualStrategyNumber
, F_OIDEQ
,
1053 ObjectIdGetDatum(roleid
));
1055 sscan
= systable_beginscan(pg_auth_members_rel
, AuthMemRoleMemIndexId
,
1056 true, NULL
, 1, &scankey
);
1058 while (HeapTupleIsValid(tmp_tuple
= systable_getnext(sscan
)))
1060 CatalogTupleDelete(pg_auth_members_rel
, &tmp_tuple
->t_self
);
1063 systable_endscan(sscan
);
1065 ScanKeyInit(&scankey
,
1066 Anum_pg_auth_members_member
,
1067 BTEqualStrategyNumber
, F_OIDEQ
,
1068 ObjectIdGetDatum(roleid
));
1070 sscan
= systable_beginscan(pg_auth_members_rel
, AuthMemMemRoleIndexId
,
1071 true, NULL
, 1, &scankey
);
1073 while (HeapTupleIsValid(tmp_tuple
= systable_getnext(sscan
)))
1075 CatalogTupleDelete(pg_auth_members_rel
, &tmp_tuple
->t_self
);
1078 systable_endscan(sscan
);
1081 * Remove any comments or security labels on this role.
1083 DeleteSharedComments(roleid
, AuthIdRelationId
);
1084 DeleteSharedSecurityLabel(roleid
, AuthIdRelationId
);
1087 * Remove settings for this role.
1089 DropSetting(InvalidOid
, roleid
);
1092 * Advance command counter so that later iterations of this loop will
1093 * see the changes already made. This is essential if, for example,
1094 * we are trying to drop both a role and one of its direct members ---
1095 * we'll get an error if we try to delete the linking pg_auth_members
1096 * tuple twice. (We do not need a CCI between the two delete loops
1097 * above, because it's not allowed for a role to directly contain
1100 CommandCounterIncrement();
1104 * Now we can clean up; but keep locks until commit.
1106 table_close(pg_auth_members_rel
, NoLock
);
1107 table_close(pg_authid_rel
, NoLock
);
1114 RenameRole(const char *oldname
, const char *newname
)
1122 Datum repl_val
[Natts_pg_authid
];
1123 bool repl_null
[Natts_pg_authid
];
1124 bool repl_repl
[Natts_pg_authid
];
1127 ObjectAddress address
;
1128 Form_pg_authid authform
;
1130 rel
= table_open(AuthIdRelationId
, RowExclusiveLock
);
1131 dsc
= RelationGetDescr(rel
);
1133 oldtuple
= SearchSysCache1(AUTHNAME
, CStringGetDatum(oldname
));
1134 if (!HeapTupleIsValid(oldtuple
))
1136 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1137 errmsg("role \"%s\" does not exist", oldname
)));
1140 * XXX Client applications probably store the session user somewhere, so
1141 * renaming it could cause confusion. On the other hand, there may not be
1142 * an actual problem besides a little confusion, so think about this and
1143 * decide. Same for SET ROLE ... we don't restrict renaming the current
1144 * effective userid, though.
1147 authform
= (Form_pg_authid
) GETSTRUCT(oldtuple
);
1148 roleid
= authform
->oid
;
1150 if (roleid
== GetSessionUserId())
1152 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1153 errmsg("session user cannot be renamed")));
1154 if (roleid
== GetOuterUserId())
1156 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1157 errmsg("current user cannot be renamed")));
1160 * Check that the user is not trying to rename a system role and not
1161 * trying to rename a role into the reserved "pg_" namespace.
1163 if (IsReservedName(NameStr(authform
->rolname
)))
1165 (errcode(ERRCODE_RESERVED_NAME
),
1166 errmsg("role name \"%s\" is reserved",
1167 NameStr(authform
->rolname
)),
1168 errdetail("Role names starting with \"pg_\" are reserved.")));
1170 if (IsReservedName(newname
))
1172 (errcode(ERRCODE_RESERVED_NAME
),
1173 errmsg("role name \"%s\" is reserved",
1175 errdetail("Role names starting with \"pg_\" are reserved.")));
1178 * If built with appropriate switch, whine when regression-testing
1179 * conventions for role names are violated.
1181 #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
1182 if (strncmp(newname
, "regress_", 8) != 0)
1183 elog(WARNING
, "roles created by regression test cases should have names starting with \"regress_\"");
1186 /* make sure the new name doesn't exist */
1187 if (SearchSysCacheExists1(AUTHNAME
, CStringGetDatum(newname
)))
1189 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1190 errmsg("role \"%s\" already exists", newname
)));
1193 * createrole is enough privilege unless you want to mess with a superuser
1195 if (((Form_pg_authid
) GETSTRUCT(oldtuple
))->rolsuper
)
1199 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1200 errmsg("must be superuser to rename superusers")));
1204 if (!have_createrole_privilege())
1206 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1207 errmsg("permission denied to rename role")));
1210 /* OK, construct the modified tuple */
1211 for (i
= 0; i
< Natts_pg_authid
; i
++)
1212 repl_repl
[i
] = false;
1214 repl_repl
[Anum_pg_authid_rolname
- 1] = true;
1215 repl_val
[Anum_pg_authid_rolname
- 1] = DirectFunctionCall1(namein
,
1216 CStringGetDatum(newname
));
1217 repl_null
[Anum_pg_authid_rolname
- 1] = false;
1219 datum
= heap_getattr(oldtuple
, Anum_pg_authid_rolpassword
, dsc
, &isnull
);
1221 if (!isnull
&& get_password_type(TextDatumGetCString(datum
)) == PASSWORD_TYPE_MD5
)
1223 /* MD5 uses the username as salt, so just clear it on a rename */
1224 repl_repl
[Anum_pg_authid_rolpassword
- 1] = true;
1225 repl_null
[Anum_pg_authid_rolpassword
- 1] = true;
1228 (errmsg("MD5 password cleared because of role rename")));
1231 newtuple
= heap_modify_tuple(oldtuple
, dsc
, repl_val
, repl_null
, repl_repl
);
1232 CatalogTupleUpdate(rel
, &oldtuple
->t_self
, newtuple
);
1234 InvokeObjectPostAlterHook(AuthIdRelationId
, roleid
, 0);
1236 ObjectAddressSet(address
, AuthIdRelationId
, roleid
);
1238 ReleaseSysCache(oldtuple
);
1241 * Close pg_authid, but keep lock till commit.
1243 table_close(rel
, NoLock
);
1251 * Grant/Revoke roles to/from roles
1254 GrantRole(GrantRoleStmt
*stmt
)
1256 Relation pg_authid_rel
;
1262 grantor
= get_rolespec_oid(stmt
->grantor
, false);
1264 grantor
= GetUserId();
1266 grantee_ids
= roleSpecsToIds(stmt
->grantee_roles
);
1268 /* AccessShareLock is enough since we aren't modifying pg_authid */
1269 pg_authid_rel
= table_open(AuthIdRelationId
, AccessShareLock
);
1272 * Step through all of the granted roles and add/remove entries for the
1273 * grantees, or, if admin_opt is set, then just add/remove the admin
1276 * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1278 foreach(item
, stmt
->granted_roles
)
1280 AccessPriv
*priv
= (AccessPriv
*) lfirst(item
);
1281 char *rolename
= priv
->priv_name
;
1284 /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
1285 if (rolename
== NULL
|| priv
->cols
!= NIL
)
1287 (errcode(ERRCODE_INVALID_GRANT_OPERATION
),
1288 errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
1290 roleid
= get_role_oid(rolename
, false);
1292 AddRoleMems(rolename
, roleid
,
1293 stmt
->grantee_roles
, grantee_ids
,
1294 grantor
, stmt
->admin_opt
);
1296 DelRoleMems(rolename
, roleid
,
1297 stmt
->grantee_roles
, grantee_ids
,
1302 * Close pg_authid, but keep lock till commit.
1304 table_close(pg_authid_rel
, NoLock
);
1310 * Drop the objects owned by a given list of roles.
1313 DropOwnedObjects(DropOwnedStmt
*stmt
)
1315 List
*role_ids
= roleSpecsToIds(stmt
->roles
);
1318 /* Check privileges */
1319 foreach(cell
, role_ids
)
1321 Oid roleid
= lfirst_oid(cell
);
1323 if (!has_privs_of_role(GetUserId(), roleid
))
1325 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1326 errmsg("permission denied to drop objects")));
1330 shdepDropOwned(role_ids
, stmt
->behavior
);
1334 * ReassignOwnedObjects
1336 * Give the objects owned by a given list of roles away to another user.
1339 ReassignOwnedObjects(ReassignOwnedStmt
*stmt
)
1341 List
*role_ids
= roleSpecsToIds(stmt
->roles
);
1345 /* Check privileges */
1346 foreach(cell
, role_ids
)
1348 Oid roleid
= lfirst_oid(cell
);
1350 if (!has_privs_of_role(GetUserId(), roleid
))
1352 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1353 errmsg("permission denied to reassign objects")));
1356 /* Must have privileges on the receiving side too */
1357 newrole
= get_rolespec_oid(stmt
->newrole
, false);
1359 if (!has_privs_of_role(GetUserId(), newrole
))
1361 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1362 errmsg("permission denied to reassign objects")));
1365 shdepReassignOwned(role_ids
, newrole
);
1371 * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
1373 * ROLESPEC_PUBLIC is not allowed.
1376 roleSpecsToIds(List
*memberNames
)
1381 foreach(l
, memberNames
)
1383 RoleSpec
*rolespec
= lfirst_node(RoleSpec
, l
);
1386 roleid
= get_rolespec_oid(rolespec
, false);
1387 result
= lappend_oid(result
, roleid
);
1393 * AddRoleMems -- Add given members to the specified role
1395 * rolename: name of role to add to (used only for error messages)
1396 * roleid: OID of role to add to
1397 * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
1398 * memberIds: OIDs of roles to add
1399 * grantorId: who is granting the membership
1400 * admin_opt: granting admin option?
1403 AddRoleMems(const char *rolename
, Oid roleid
,
1404 List
*memberSpecs
, List
*memberIds
,
1405 Oid grantorId
, bool admin_opt
)
1407 Relation pg_authmem_rel
;
1408 TupleDesc pg_authmem_dsc
;
1412 Assert(list_length(memberSpecs
) == list_length(memberIds
));
1414 /* Skip permission check if nothing to do */
1419 * Check permissions: must have createrole or admin option on the role to
1420 * be changed. To mess with a superuser role, you gotta be superuser.
1422 if (superuser_arg(roleid
))
1426 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1427 errmsg("must be superuser to alter superusers")));
1431 if (!have_createrole_privilege() &&
1432 !is_admin_of_role(grantorId
, roleid
))
1434 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1435 errmsg("must have admin option on role \"%s\"",
1440 * The charter of pg_database_owner is to have exactly one, implicit,
1441 * situation-dependent member. There's no technical need for this
1442 * restriction. (One could lift it and take the further step of making
1443 * pg_database_ownercheck() equivalent to has_privs_of_role(roleid,
1444 * ROLE_PG_DATABASE_OWNER), in which case explicit, situation-independent
1445 * members could act as the owner of any database.)
1447 if (roleid
== ROLE_PG_DATABASE_OWNER
)
1449 errmsg("role \"%s\" cannot have explicit members", rolename
));
1452 * The role membership grantor of record has little significance at
1453 * present. Nonetheless, inasmuch as users might look to it for a crude
1454 * audit trail, let only superusers impute the grant to a third party.
1456 * Before lifting this restriction, give the member == role case of
1457 * is_admin_of_role() a fresh look. Ensure that the current role cannot
1458 * use an explicit grantor specification to take advantage of the session
1459 * user's self-admin right.
1461 if (grantorId
!= GetUserId() && !superuser())
1463 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1464 errmsg("must be superuser to set grantor")));
1466 pg_authmem_rel
= table_open(AuthMemRelationId
, RowExclusiveLock
);
1467 pg_authmem_dsc
= RelationGetDescr(pg_authmem_rel
);
1469 forboth(specitem
, memberSpecs
, iditem
, memberIds
)
1471 RoleSpec
*memberRole
= lfirst_node(RoleSpec
, specitem
);
1472 Oid memberid
= lfirst_oid(iditem
);
1473 HeapTuple authmem_tuple
;
1475 Datum new_record
[Natts_pg_auth_members
];
1476 bool new_record_nulls
[Natts_pg_auth_members
];
1477 bool new_record_repl
[Natts_pg_auth_members
];
1480 * pg_database_owner is never a role member. Lifting this restriction
1481 * would require a policy decision about membership loops. One could
1482 * prevent loops, which would include making "ALTER DATABASE x OWNER
1483 * TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
1484 * proposed_datdba). Hence, gaining a membership could reduce what a
1485 * role could do. Alternately, one could allow these memberships to
1486 * complete loops. A role could then have actual WITH ADMIN OPTION on
1487 * itself, prompting a decision about is_admin_of_role() treatment of
1490 * Lifting this restriction also has policy implications for ownership
1491 * of shared objects (databases and tablespaces). We allow such
1492 * ownership, but we might find cause to ban it in the future.
1493 * Designing such a ban would more troublesome if the design had to
1494 * address pg_database_owner being a member of role FOO that owns a
1495 * shared object. (The effect of such ownership is that any owner of
1496 * another database can act as the owner of affected shared objects.)
1498 if (memberid
== ROLE_PG_DATABASE_OWNER
)
1500 errmsg("role \"%s\" cannot be a member of any role",
1501 get_rolespec_name(memberRole
)));
1504 * Refuse creation of membership loops, including the trivial case
1505 * where a role is made a member of itself. We do this by checking to
1506 * see if the target role is already a member of the proposed member
1507 * role. We have to ignore possible superuserness, however, else we
1508 * could never grant membership in a superuser-privileged role.
1510 if (is_member_of_role_nosuper(roleid
, memberid
))
1512 (errcode(ERRCODE_INVALID_GRANT_OPERATION
),
1513 errmsg("role \"%s\" is a member of role \"%s\"",
1514 rolename
, get_rolespec_name(memberRole
))));
1517 * Check if entry for this role/member already exists; if so, give
1518 * warning unless we are adding admin option.
1520 authmem_tuple
= SearchSysCache2(AUTHMEMROLEMEM
,
1521 ObjectIdGetDatum(roleid
),
1522 ObjectIdGetDatum(memberid
));
1523 if (HeapTupleIsValid(authmem_tuple
) &&
1525 ((Form_pg_auth_members
) GETSTRUCT(authmem_tuple
))->admin_option
))
1528 (errmsg("role \"%s\" is already a member of role \"%s\"",
1529 get_rolespec_name(memberRole
), rolename
)));
1530 ReleaseSysCache(authmem_tuple
);
1534 /* Build a tuple to insert or update */
1535 MemSet(new_record
, 0, sizeof(new_record
));
1536 MemSet(new_record_nulls
, false, sizeof(new_record_nulls
));
1537 MemSet(new_record_repl
, false, sizeof(new_record_repl
));
1539 new_record
[Anum_pg_auth_members_roleid
- 1] = ObjectIdGetDatum(roleid
);
1540 new_record
[Anum_pg_auth_members_member
- 1] = ObjectIdGetDatum(memberid
);
1541 new_record
[Anum_pg_auth_members_grantor
- 1] = ObjectIdGetDatum(grantorId
);
1542 new_record
[Anum_pg_auth_members_admin_option
- 1] = BoolGetDatum(admin_opt
);
1544 if (HeapTupleIsValid(authmem_tuple
))
1546 new_record_repl
[Anum_pg_auth_members_grantor
- 1] = true;
1547 new_record_repl
[Anum_pg_auth_members_admin_option
- 1] = true;
1548 tuple
= heap_modify_tuple(authmem_tuple
, pg_authmem_dsc
,
1550 new_record_nulls
, new_record_repl
);
1551 CatalogTupleUpdate(pg_authmem_rel
, &tuple
->t_self
, tuple
);
1552 ReleaseSysCache(authmem_tuple
);
1556 tuple
= heap_form_tuple(pg_authmem_dsc
,
1557 new_record
, new_record_nulls
);
1558 CatalogTupleInsert(pg_authmem_rel
, tuple
);
1561 /* CCI after each change, in case there are duplicates in list */
1562 CommandCounterIncrement();
1566 * Close pg_authmem, but keep lock till commit.
1568 table_close(pg_authmem_rel
, NoLock
);
1572 * DelRoleMems -- Remove given members from the specified role
1574 * rolename: name of role to del from (used only for error messages)
1575 * roleid: OID of role to del from
1576 * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
1577 * memberIds: OIDs of roles to del
1578 * admin_opt: remove admin option only?
1581 DelRoleMems(const char *rolename
, Oid roleid
,
1582 List
*memberSpecs
, List
*memberIds
,
1585 Relation pg_authmem_rel
;
1586 TupleDesc pg_authmem_dsc
;
1590 Assert(list_length(memberSpecs
) == list_length(memberIds
));
1592 /* Skip permission check if nothing to do */
1597 * Check permissions: must have createrole or admin option on the role to
1598 * be changed. To mess with a superuser role, you gotta be superuser.
1600 if (superuser_arg(roleid
))
1604 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1605 errmsg("must be superuser to alter superusers")));
1609 if (!have_createrole_privilege() &&
1610 !is_admin_of_role(GetUserId(), roleid
))
1612 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1613 errmsg("must have admin option on role \"%s\"",
1617 pg_authmem_rel
= table_open(AuthMemRelationId
, RowExclusiveLock
);
1618 pg_authmem_dsc
= RelationGetDescr(pg_authmem_rel
);
1620 forboth(specitem
, memberSpecs
, iditem
, memberIds
)
1622 RoleSpec
*memberRole
= lfirst(specitem
);
1623 Oid memberid
= lfirst_oid(iditem
);
1624 HeapTuple authmem_tuple
;
1627 * Find entry for this role/member
1629 authmem_tuple
= SearchSysCache2(AUTHMEMROLEMEM
,
1630 ObjectIdGetDatum(roleid
),
1631 ObjectIdGetDatum(memberid
));
1632 if (!HeapTupleIsValid(authmem_tuple
))
1635 (errmsg("role \"%s\" is not a member of role \"%s\"",
1636 get_rolespec_name(memberRole
), rolename
)));
1642 /* Remove the entry altogether */
1643 CatalogTupleDelete(pg_authmem_rel
, &authmem_tuple
->t_self
);
1647 /* Just turn off the admin option */
1649 Datum new_record
[Natts_pg_auth_members
];
1650 bool new_record_nulls
[Natts_pg_auth_members
];
1651 bool new_record_repl
[Natts_pg_auth_members
];
1653 /* Build a tuple to update with */
1654 MemSet(new_record
, 0, sizeof(new_record
));
1655 MemSet(new_record_nulls
, false, sizeof(new_record_nulls
));
1656 MemSet(new_record_repl
, false, sizeof(new_record_repl
));
1658 new_record
[Anum_pg_auth_members_admin_option
- 1] = BoolGetDatum(false);
1659 new_record_repl
[Anum_pg_auth_members_admin_option
- 1] = true;
1661 tuple
= heap_modify_tuple(authmem_tuple
, pg_authmem_dsc
,
1663 new_record_nulls
, new_record_repl
);
1664 CatalogTupleUpdate(pg_authmem_rel
, &tuple
->t_self
, tuple
);
1667 ReleaseSysCache(authmem_tuple
);
1669 /* CCI after each change, in case there are duplicates in list */
1670 CommandCounterIncrement();
1674 * Close pg_authmem, but keep lock till commit.
1676 table_close(pg_authmem_rel
, NoLock
);