Move the HTSU_Result enum definition into snapshot.h, to avoid including
[PostgreSQL.git] / src / backend / commands / user.c
blob7166ce6919c020dde8994ac7d4ef1b2cdafe3dc7
1 /*-------------------------------------------------------------------------
3 * user.c
4 * Commands for manipulating roles (formerly called users).
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $PostgreSQL$
11 *-------------------------------------------------------------------------
13 #include "postgres.h"
15 #include "access/genam.h"
16 #include "access/heapam.h"
17 #include "access/xact.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_auth_members.h"
21 #include "catalog/pg_authid.h"
22 #include "commands/comment.h"
23 #include "commands/user.h"
24 #include "libpq/md5.h"
25 #include "miscadmin.h"
26 #include "utils/acl.h"
27 #include "utils/builtins.h"
28 #include "utils/flatfiles.h"
29 #include "utils/fmgroids.h"
30 #include "utils/guc.h"
31 #include "utils/lsyscache.h"
32 #include "utils/syscache.h"
33 #include "utils/tqual.h"
36 extern bool Password_encryption;
38 static List *roleNamesToIds(List *memberNames);
39 static void AddRoleMems(const char *rolename, Oid roleid,
40 List *memberNames, List *memberIds,
41 Oid grantorId, bool admin_opt);
42 static void DelRoleMems(const char *rolename, Oid roleid,
43 List *memberNames, List *memberIds,
44 bool admin_opt);
47 /* Check if current user has createrole privileges */
48 static bool
49 have_createrole_privilege(void)
51 bool result = false;
52 HeapTuple utup;
54 /* Superusers can always do everything */
55 if (superuser())
56 return true;
58 utup = SearchSysCache(AUTHOID,
59 ObjectIdGetDatum(GetUserId()),
60 0, 0, 0);
61 if (HeapTupleIsValid(utup))
63 result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
64 ReleaseSysCache(utup);
66 return result;
71 * CREATE ROLE
73 void
74 CreateRole(CreateRoleStmt *stmt)
76 Relation pg_authid_rel;
77 TupleDesc pg_authid_dsc;
78 HeapTuple tuple;
79 Datum new_record[Natts_pg_authid];
80 char new_record_nulls[Natts_pg_authid];
81 Oid roleid;
82 ListCell *item;
83 ListCell *option;
84 char *password = NULL; /* user password */
85 bool encrypt_password = Password_encryption; /* encrypt password? */
86 char encrypted_password[MD5_PASSWD_LEN + 1];
87 bool issuper = false; /* Make the user a superuser? */
88 bool inherit = true; /* Auto inherit privileges? */
89 bool createrole = false; /* Can this user create roles? */
90 bool createdb = false; /* Can the user create databases? */
91 bool canlogin = false; /* Can this user login? */
92 int connlimit = -1; /* maximum connections allowed */
93 List *addroleto = NIL; /* roles to make this a member of */
94 List *rolemembers = NIL; /* roles to be members of this role */
95 List *adminmembers = NIL; /* roles to be admins of this role */
96 char *validUntil = NULL; /* time the login is valid until */
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 *dconnlimit = NULL;
104 DefElem *daddroleto = NULL;
105 DefElem *drolemembers = NULL;
106 DefElem *dadminmembers = NULL;
107 DefElem *dvalidUntil = NULL;
109 /* The defaults can vary depending on the original statement type */
110 switch (stmt->stmt_type)
112 case ROLESTMT_ROLE:
113 break;
114 case ROLESTMT_USER:
115 canlogin = true;
116 /* may eventually want inherit to default to false here */
117 break;
118 case ROLESTMT_GROUP:
119 break;
122 /* Extract options from the statement node tree */
123 foreach(option, stmt->options)
125 DefElem *defel = (DefElem *) lfirst(option);
127 if (strcmp(defel->defname, "password") == 0 ||
128 strcmp(defel->defname, "encryptedPassword") == 0 ||
129 strcmp(defel->defname, "unencryptedPassword") == 0)
131 if (dpassword)
132 ereport(ERROR,
133 (errcode(ERRCODE_SYNTAX_ERROR),
134 errmsg("conflicting or redundant options")));
135 dpassword = defel;
136 if (strcmp(defel->defname, "encryptedPassword") == 0)
137 encrypt_password = true;
138 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
139 encrypt_password = false;
141 else if (strcmp(defel->defname, "sysid") == 0)
143 ereport(NOTICE,
144 (errmsg("SYSID can no longer be specified")));
146 else if (strcmp(defel->defname, "superuser") == 0)
148 if (dissuper)
149 ereport(ERROR,
150 (errcode(ERRCODE_SYNTAX_ERROR),
151 errmsg("conflicting or redundant options")));
152 dissuper = defel;
154 else if (strcmp(defel->defname, "inherit") == 0)
156 if (dinherit)
157 ereport(ERROR,
158 (errcode(ERRCODE_SYNTAX_ERROR),
159 errmsg("conflicting or redundant options")));
160 dinherit = defel;
162 else if (strcmp(defel->defname, "createrole") == 0)
164 if (dcreaterole)
165 ereport(ERROR,
166 (errcode(ERRCODE_SYNTAX_ERROR),
167 errmsg("conflicting or redundant options")));
168 dcreaterole = defel;
170 else if (strcmp(defel->defname, "createdb") == 0)
172 if (dcreatedb)
173 ereport(ERROR,
174 (errcode(ERRCODE_SYNTAX_ERROR),
175 errmsg("conflicting or redundant options")));
176 dcreatedb = defel;
178 else if (strcmp(defel->defname, "canlogin") == 0)
180 if (dcanlogin)
181 ereport(ERROR,
182 (errcode(ERRCODE_SYNTAX_ERROR),
183 errmsg("conflicting or redundant options")));
184 dcanlogin = defel;
186 else if (strcmp(defel->defname, "connectionlimit") == 0)
188 if (dconnlimit)
189 ereport(ERROR,
190 (errcode(ERRCODE_SYNTAX_ERROR),
191 errmsg("conflicting or redundant options")));
192 dconnlimit = defel;
194 else if (strcmp(defel->defname, "addroleto") == 0)
196 if (daddroleto)
197 ereport(ERROR,
198 (errcode(ERRCODE_SYNTAX_ERROR),
199 errmsg("conflicting or redundant options")));
200 daddroleto = defel;
202 else if (strcmp(defel->defname, "rolemembers") == 0)
204 if (drolemembers)
205 ereport(ERROR,
206 (errcode(ERRCODE_SYNTAX_ERROR),
207 errmsg("conflicting or redundant options")));
208 drolemembers = defel;
210 else if (strcmp(defel->defname, "adminmembers") == 0)
212 if (dadminmembers)
213 ereport(ERROR,
214 (errcode(ERRCODE_SYNTAX_ERROR),
215 errmsg("conflicting or redundant options")));
216 dadminmembers = defel;
218 else if (strcmp(defel->defname, "validUntil") == 0)
220 if (dvalidUntil)
221 ereport(ERROR,
222 (errcode(ERRCODE_SYNTAX_ERROR),
223 errmsg("conflicting or redundant options")));
224 dvalidUntil = defel;
226 else
227 elog(ERROR, "option \"%s\" not recognized",
228 defel->defname);
231 if (dpassword && dpassword->arg)
232 password = strVal(dpassword->arg);
233 if (dissuper)
234 issuper = intVal(dissuper->arg) != 0;
235 if (dinherit)
236 inherit = intVal(dinherit->arg) != 0;
237 if (dcreaterole)
238 createrole = intVal(dcreaterole->arg) != 0;
239 if (dcreatedb)
240 createdb = intVal(dcreatedb->arg) != 0;
241 if (dcanlogin)
242 canlogin = intVal(dcanlogin->arg) != 0;
243 if (dconnlimit)
244 connlimit = intVal(dconnlimit->arg);
245 if (daddroleto)
246 addroleto = (List *) daddroleto->arg;
247 if (drolemembers)
248 rolemembers = (List *) drolemembers->arg;
249 if (dadminmembers)
250 adminmembers = (List *) dadminmembers->arg;
251 if (dvalidUntil)
252 validUntil = strVal(dvalidUntil->arg);
254 /* Check some permissions first */
255 if (issuper)
257 if (!superuser())
258 ereport(ERROR,
259 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
260 errmsg("must be superuser to create superusers")));
262 else
264 if (!have_createrole_privilege())
265 ereport(ERROR,
266 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
267 errmsg("permission denied to create role")));
270 if (strcmp(stmt->role, "public") == 0 ||
271 strcmp(stmt->role, "none") == 0)
272 ereport(ERROR,
273 (errcode(ERRCODE_RESERVED_NAME),
274 errmsg("role name \"%s\" is reserved",
275 stmt->role)));
278 * Check the pg_authid relation to be certain the role doesn't already
279 * exist.
281 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
282 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
284 tuple = SearchSysCache(AUTHNAME,
285 PointerGetDatum(stmt->role),
286 0, 0, 0);
287 if (HeapTupleIsValid(tuple))
288 ereport(ERROR,
289 (errcode(ERRCODE_DUPLICATE_OBJECT),
290 errmsg("role \"%s\" already exists",
291 stmt->role)));
294 * Build a tuple to insert
296 MemSet(new_record, 0, sizeof(new_record));
297 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
299 new_record[Anum_pg_authid_rolname - 1] =
300 DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
302 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
303 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
304 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
305 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
306 /* superuser gets catupdate right by default */
307 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
308 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
309 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
311 if (password)
313 if (!encrypt_password || isMD5(password))
314 new_record[Anum_pg_authid_rolpassword - 1] =
315 CStringGetTextDatum(password);
316 else
318 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
319 encrypted_password))
320 elog(ERROR, "password encryption failed");
321 new_record[Anum_pg_authid_rolpassword - 1] =
322 CStringGetTextDatum(encrypted_password);
325 else
326 new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n';
328 if (validUntil)
329 new_record[Anum_pg_authid_rolvaliduntil - 1] =
330 DirectFunctionCall3(timestamptz_in,
331 CStringGetDatum(validUntil),
332 ObjectIdGetDatum(InvalidOid),
333 Int32GetDatum(-1));
335 else
336 new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = 'n';
338 new_record_nulls[Anum_pg_authid_rolconfig - 1] = 'n';
340 tuple = heap_formtuple(pg_authid_dsc, new_record, new_record_nulls);
343 * Insert new record in the pg_authid table
345 roleid = simple_heap_insert(pg_authid_rel, tuple);
346 CatalogUpdateIndexes(pg_authid_rel, tuple);
349 * Advance command counter so we can see new record; else tests in
350 * AddRoleMems may fail.
352 if (addroleto || adminmembers || rolemembers)
353 CommandCounterIncrement();
356 * Add the new role to the specified existing roles.
358 foreach(item, addroleto)
360 char *oldrolename = strVal(lfirst(item));
361 Oid oldroleid = get_roleid_checked(oldrolename);
363 AddRoleMems(oldrolename, oldroleid,
364 list_make1(makeString(stmt->role)),
365 list_make1_oid(roleid),
366 GetUserId(), false);
370 * Add the specified members to this new role. adminmembers get the admin
371 * option, rolemembers don't.
373 AddRoleMems(stmt->role, roleid,
374 adminmembers, roleNamesToIds(adminmembers),
375 GetUserId(), true);
376 AddRoleMems(stmt->role, roleid,
377 rolemembers, roleNamesToIds(rolemembers),
378 GetUserId(), false);
381 * Close pg_authid, but keep lock till commit (this is important to
382 * prevent any risk of deadlock failure while updating flat file)
384 heap_close(pg_authid_rel, NoLock);
387 * Set flag to update flat auth file at commit.
389 auth_file_update_needed();
394 * ALTER ROLE
396 * Note: the rolemembers option accepted here is intended to support the
397 * backwards-compatible ALTER GROUP syntax. Although it will work to say
398 * "ALTER ROLE role ROLE rolenames", we don't document it.
400 void
401 AlterRole(AlterRoleStmt *stmt)
403 Datum new_record[Natts_pg_authid];
404 char new_record_nulls[Natts_pg_authid];
405 char new_record_repl[Natts_pg_authid];
406 Relation pg_authid_rel;
407 TupleDesc pg_authid_dsc;
408 HeapTuple tuple,
409 new_tuple;
410 ListCell *option;
411 char *password = NULL; /* user password */
412 bool encrypt_password = Password_encryption; /* encrypt password? */
413 char encrypted_password[MD5_PASSWD_LEN + 1];
414 int issuper = -1; /* Make the user a superuser? */
415 int inherit = -1; /* Auto inherit privileges? */
416 int createrole = -1; /* Can this user create roles? */
417 int createdb = -1; /* Can the user create databases? */
418 int canlogin = -1; /* Can this user login? */
419 int connlimit = -1; /* maximum connections allowed */
420 List *rolemembers = NIL; /* roles to be added/removed */
421 char *validUntil = NULL; /* time the login is valid until */
422 DefElem *dpassword = NULL;
423 DefElem *dissuper = NULL;
424 DefElem *dinherit = NULL;
425 DefElem *dcreaterole = NULL;
426 DefElem *dcreatedb = NULL;
427 DefElem *dcanlogin = NULL;
428 DefElem *dconnlimit = NULL;
429 DefElem *drolemembers = NULL;
430 DefElem *dvalidUntil = NULL;
431 Oid roleid;
433 /* Extract options from the statement node tree */
434 foreach(option, stmt->options)
436 DefElem *defel = (DefElem *) lfirst(option);
438 if (strcmp(defel->defname, "password") == 0 ||
439 strcmp(defel->defname, "encryptedPassword") == 0 ||
440 strcmp(defel->defname, "unencryptedPassword") == 0)
442 if (dpassword)
443 ereport(ERROR,
444 (errcode(ERRCODE_SYNTAX_ERROR),
445 errmsg("conflicting or redundant options")));
446 dpassword = defel;
447 if (strcmp(defel->defname, "encryptedPassword") == 0)
448 encrypt_password = true;
449 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
450 encrypt_password = false;
452 else if (strcmp(defel->defname, "superuser") == 0)
454 if (dissuper)
455 ereport(ERROR,
456 (errcode(ERRCODE_SYNTAX_ERROR),
457 errmsg("conflicting or redundant options")));
458 dissuper = defel;
460 else if (strcmp(defel->defname, "inherit") == 0)
462 if (dinherit)
463 ereport(ERROR,
464 (errcode(ERRCODE_SYNTAX_ERROR),
465 errmsg("conflicting or redundant options")));
466 dinherit = defel;
468 else if (strcmp(defel->defname, "createrole") == 0)
470 if (dcreaterole)
471 ereport(ERROR,
472 (errcode(ERRCODE_SYNTAX_ERROR),
473 errmsg("conflicting or redundant options")));
474 dcreaterole = defel;
476 else if (strcmp(defel->defname, "createdb") == 0)
478 if (dcreatedb)
479 ereport(ERROR,
480 (errcode(ERRCODE_SYNTAX_ERROR),
481 errmsg("conflicting or redundant options")));
482 dcreatedb = defel;
484 else if (strcmp(defel->defname, "canlogin") == 0)
486 if (dcanlogin)
487 ereport(ERROR,
488 (errcode(ERRCODE_SYNTAX_ERROR),
489 errmsg("conflicting or redundant options")));
490 dcanlogin = defel;
492 else if (strcmp(defel->defname, "connectionlimit") == 0)
494 if (dconnlimit)
495 ereport(ERROR,
496 (errcode(ERRCODE_SYNTAX_ERROR),
497 errmsg("conflicting or redundant options")));
498 dconnlimit = defel;
500 else if (strcmp(defel->defname, "rolemembers") == 0 &&
501 stmt->action != 0)
503 if (drolemembers)
504 ereport(ERROR,
505 (errcode(ERRCODE_SYNTAX_ERROR),
506 errmsg("conflicting or redundant options")));
507 drolemembers = defel;
509 else if (strcmp(defel->defname, "validUntil") == 0)
511 if (dvalidUntil)
512 ereport(ERROR,
513 (errcode(ERRCODE_SYNTAX_ERROR),
514 errmsg("conflicting or redundant options")));
515 dvalidUntil = defel;
517 else
518 elog(ERROR, "option \"%s\" not recognized",
519 defel->defname);
522 if (dpassword && dpassword->arg)
523 password = strVal(dpassword->arg);
524 if (dissuper)
525 issuper = intVal(dissuper->arg);
526 if (dinherit)
527 inherit = intVal(dinherit->arg);
528 if (dcreaterole)
529 createrole = intVal(dcreaterole->arg);
530 if (dcreatedb)
531 createdb = intVal(dcreatedb->arg);
532 if (dcanlogin)
533 canlogin = intVal(dcanlogin->arg);
534 if (dconnlimit)
535 connlimit = intVal(dconnlimit->arg);
536 if (drolemembers)
537 rolemembers = (List *) drolemembers->arg;
538 if (dvalidUntil)
539 validUntil = strVal(dvalidUntil->arg);
542 * Scan the pg_authid relation to be certain the user exists.
544 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
545 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
547 tuple = SearchSysCache(AUTHNAME,
548 PointerGetDatum(stmt->role),
549 0, 0, 0);
550 if (!HeapTupleIsValid(tuple))
551 ereport(ERROR,
552 (errcode(ERRCODE_UNDEFINED_OBJECT),
553 errmsg("role \"%s\" does not exist", stmt->role)));
555 roleid = HeapTupleGetOid(tuple);
558 * To mess with a superuser you gotta be superuser; else you need
559 * createrole, or just want to change your own password
561 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
563 if (!superuser())
564 ereport(ERROR,
565 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
566 errmsg("must be superuser to alter superusers")));
568 else if (!have_createrole_privilege())
570 if (!(inherit < 0 &&
571 createrole < 0 &&
572 createdb < 0 &&
573 canlogin < 0 &&
574 !dconnlimit &&
575 !rolemembers &&
576 !validUntil &&
577 dpassword &&
578 roleid == GetUserId()))
579 ereport(ERROR,
580 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
581 errmsg("permission denied")));
585 * Build an updated tuple, perusing the information just obtained
587 MemSet(new_record, 0, sizeof(new_record));
588 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
589 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
592 * issuper/createrole/catupdate/etc
594 * XXX It's rather unclear how to handle catupdate. It's probably best to
595 * keep it equal to the superuser status, otherwise you could end up with
596 * a situation where no existing superuser can alter the catalogs,
597 * including pg_authid!
599 if (issuper >= 0)
601 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
602 new_record_repl[Anum_pg_authid_rolsuper - 1] = 'r';
604 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
605 new_record_repl[Anum_pg_authid_rolcatupdate - 1] = 'r';
608 if (inherit >= 0)
610 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
611 new_record_repl[Anum_pg_authid_rolinherit - 1] = 'r';
614 if (createrole >= 0)
616 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
617 new_record_repl[Anum_pg_authid_rolcreaterole - 1] = 'r';
620 if (createdb >= 0)
622 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
623 new_record_repl[Anum_pg_authid_rolcreatedb - 1] = 'r';
626 if (canlogin >= 0)
628 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
629 new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r';
632 if (dconnlimit)
634 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
635 new_record_repl[Anum_pg_authid_rolconnlimit - 1] = 'r';
638 /* password */
639 if (password)
641 if (!encrypt_password || isMD5(password))
642 new_record[Anum_pg_authid_rolpassword - 1] =
643 CStringGetTextDatum(password);
644 else
646 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
647 encrypted_password))
648 elog(ERROR, "password encryption failed");
649 new_record[Anum_pg_authid_rolpassword - 1] =
650 CStringGetTextDatum(encrypted_password);
652 new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r';
655 /* unset password */
656 if (dpassword && dpassword->arg == NULL)
658 new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r';
659 new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n';
662 /* valid until */
663 if (validUntil)
665 new_record[Anum_pg_authid_rolvaliduntil - 1] =
666 DirectFunctionCall3(timestamptz_in,
667 CStringGetDatum(validUntil),
668 ObjectIdGetDatum(InvalidOid),
669 Int32GetDatum(-1));
670 new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = 'r';
673 new_tuple = heap_modifytuple(tuple, pg_authid_dsc, new_record,
674 new_record_nulls, new_record_repl);
675 simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
677 /* Update indexes */
678 CatalogUpdateIndexes(pg_authid_rel, new_tuple);
680 ReleaseSysCache(tuple);
681 heap_freetuple(new_tuple);
684 * Advance command counter so we can see new record; else tests in
685 * AddRoleMems may fail.
687 if (rolemembers)
688 CommandCounterIncrement();
690 if (stmt->action == +1) /* add members to role */
691 AddRoleMems(stmt->role, roleid,
692 rolemembers, roleNamesToIds(rolemembers),
693 GetUserId(), false);
694 else if (stmt->action == -1) /* drop members from role */
695 DelRoleMems(stmt->role, roleid,
696 rolemembers, roleNamesToIds(rolemembers),
697 false);
700 * Close pg_authid, but keep lock till commit (this is important to
701 * prevent any risk of deadlock failure while updating flat file)
703 heap_close(pg_authid_rel, NoLock);
706 * Set flag to update flat auth file at commit.
708 auth_file_update_needed();
713 * ALTER ROLE ... SET
715 void
716 AlterRoleSet(AlterRoleSetStmt *stmt)
718 char *valuestr;
719 HeapTuple oldtuple,
720 newtuple;
721 Relation rel;
722 Datum repl_val[Natts_pg_authid];
723 char repl_null[Natts_pg_authid];
724 char repl_repl[Natts_pg_authid];
726 valuestr = ExtractSetVariableArgs(stmt->setstmt);
728 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
729 oldtuple = SearchSysCache(AUTHNAME,
730 PointerGetDatum(stmt->role),
731 0, 0, 0);
732 if (!HeapTupleIsValid(oldtuple))
733 ereport(ERROR,
734 (errcode(ERRCODE_UNDEFINED_OBJECT),
735 errmsg("role \"%s\" does not exist", stmt->role)));
738 * To mess with a superuser you gotta be superuser; else you need
739 * createrole, or just want to change your own settings
741 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
743 if (!superuser())
744 ereport(ERROR,
745 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
746 errmsg("must be superuser to alter superusers")));
748 else
750 if (!have_createrole_privilege() &&
751 HeapTupleGetOid(oldtuple) != GetUserId())
752 ereport(ERROR,
753 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
754 errmsg("permission denied")));
757 memset(repl_repl, ' ', sizeof(repl_repl));
758 repl_repl[Anum_pg_authid_rolconfig - 1] = 'r';
760 if (stmt->setstmt->kind == VAR_RESET_ALL)
762 /* RESET ALL, so just set rolconfig to null */
763 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
764 repl_val[Anum_pg_authid_rolconfig - 1] = (Datum) 0;
766 else
768 Datum datum;
769 bool isnull;
770 ArrayType *array;
772 repl_null[Anum_pg_authid_rolconfig - 1] = ' ';
774 /* Extract old value of rolconfig */
775 datum = SysCacheGetAttr(AUTHNAME, oldtuple,
776 Anum_pg_authid_rolconfig, &isnull);
777 array = isnull ? NULL : DatumGetArrayTypeP(datum);
779 /* Update (valuestr is NULL in RESET cases) */
780 if (valuestr)
781 array = GUCArrayAdd(array, stmt->setstmt->name, valuestr);
782 else
783 array = GUCArrayDelete(array, stmt->setstmt->name);
785 if (array)
786 repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array);
787 else
788 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
791 newtuple = heap_modifytuple(oldtuple, RelationGetDescr(rel),
792 repl_val, repl_null, repl_repl);
794 simple_heap_update(rel, &oldtuple->t_self, newtuple);
795 CatalogUpdateIndexes(rel, newtuple);
797 ReleaseSysCache(oldtuple);
798 /* needn't keep lock since we won't be updating the flat file */
799 heap_close(rel, RowExclusiveLock);
804 * DROP ROLE
806 void
807 DropRole(DropRoleStmt *stmt)
809 Relation pg_authid_rel,
810 pg_auth_members_rel;
811 ListCell *item;
813 if (!have_createrole_privilege())
814 ereport(ERROR,
815 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
816 errmsg("permission denied to drop role")));
819 * Scan the pg_authid relation to find the Oid of the role(s) to be
820 * deleted.
822 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
823 pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
825 foreach(item, stmt->roles)
827 const char *role = strVal(lfirst(item));
828 HeapTuple tuple,
829 tmp_tuple;
830 ScanKeyData scankey;
831 char *detail;
832 char *detail_log;
833 SysScanDesc sscan;
834 Oid roleid;
836 tuple = SearchSysCache(AUTHNAME,
837 PointerGetDatum(role),
838 0, 0, 0);
839 if (!HeapTupleIsValid(tuple))
841 if (!stmt->missing_ok)
843 ereport(ERROR,
844 (errcode(ERRCODE_UNDEFINED_OBJECT),
845 errmsg("role \"%s\" does not exist", role)));
847 else
849 ereport(NOTICE,
850 (errmsg("role \"%s\" does not exist, skipping",
851 role)));
854 continue;
857 roleid = HeapTupleGetOid(tuple);
859 if (roleid == GetUserId())
860 ereport(ERROR,
861 (errcode(ERRCODE_OBJECT_IN_USE),
862 errmsg("current user cannot be dropped")));
863 if (roleid == GetOuterUserId())
864 ereport(ERROR,
865 (errcode(ERRCODE_OBJECT_IN_USE),
866 errmsg("current user cannot be dropped")));
867 if (roleid == GetSessionUserId())
868 ereport(ERROR,
869 (errcode(ERRCODE_OBJECT_IN_USE),
870 errmsg("session user cannot be dropped")));
873 * For safety's sake, we allow createrole holders to drop ordinary
874 * roles but not superuser roles. This is mainly to avoid the
875 * scenario where you accidentally drop the last superuser.
877 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
878 !superuser())
879 ereport(ERROR,
880 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
881 errmsg("must be superuser to drop superusers")));
884 * Lock the role, so nobody can add dependencies to her while we drop
885 * her. We keep the lock until the end of transaction.
887 LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
889 /* Check for pg_shdepend entries depending on this role */
890 if (checkSharedDependencies(AuthIdRelationId, roleid,
891 &detail, &detail_log))
892 ereport(ERROR,
893 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
894 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
895 role),
896 errdetail("%s", detail),
897 errdetail_log("%s", detail_log)));
900 * Remove the role from the pg_authid table
902 simple_heap_delete(pg_authid_rel, &tuple->t_self);
904 ReleaseSysCache(tuple);
907 * Remove role from the pg_auth_members table. We have to remove all
908 * tuples that show it as either a role or a member.
910 * XXX what about grantor entries? Maybe we should do one heap scan.
912 ScanKeyInit(&scankey,
913 Anum_pg_auth_members_roleid,
914 BTEqualStrategyNumber, F_OIDEQ,
915 ObjectIdGetDatum(roleid));
917 sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
918 true, SnapshotNow, 1, &scankey);
920 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
922 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
925 systable_endscan(sscan);
927 ScanKeyInit(&scankey,
928 Anum_pg_auth_members_member,
929 BTEqualStrategyNumber, F_OIDEQ,
930 ObjectIdGetDatum(roleid));
932 sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
933 true, SnapshotNow, 1, &scankey);
935 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
937 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
940 systable_endscan(sscan);
943 * Remove any comments on this role.
945 DeleteSharedComments(roleid, AuthIdRelationId);
948 * Advance command counter so that later iterations of this loop will
949 * see the changes already made. This is essential if, for example,
950 * we are trying to drop both a role and one of its direct members ---
951 * we'll get an error if we try to delete the linking pg_auth_members
952 * tuple twice. (We do not need a CCI between the two delete loops
953 * above, because it's not allowed for a role to directly contain
954 * itself.)
956 CommandCounterIncrement();
960 * Now we can clean up; but keep locks until commit (to avoid possible
961 * deadlock failure while updating flat file)
963 heap_close(pg_auth_members_rel, NoLock);
964 heap_close(pg_authid_rel, NoLock);
967 * Set flag to update flat auth file at commit.
969 auth_file_update_needed();
973 * Rename role
975 void
976 RenameRole(const char *oldname, const char *newname)
978 HeapTuple oldtuple,
979 newtuple;
980 TupleDesc dsc;
981 Relation rel;
982 Datum datum;
983 bool isnull;
984 Datum repl_val[Natts_pg_authid];
985 char repl_null[Natts_pg_authid];
986 char repl_repl[Natts_pg_authid];
987 int i;
988 Oid roleid;
990 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
991 dsc = RelationGetDescr(rel);
993 oldtuple = SearchSysCache(AUTHNAME,
994 CStringGetDatum(oldname),
995 0, 0, 0);
996 if (!HeapTupleIsValid(oldtuple))
997 ereport(ERROR,
998 (errcode(ERRCODE_UNDEFINED_OBJECT),
999 errmsg("role \"%s\" does not exist", oldname)));
1002 * XXX Client applications probably store the session user somewhere, so
1003 * renaming it could cause confusion. On the other hand, there may not be
1004 * an actual problem besides a little confusion, so think about this and
1005 * decide. Same for SET ROLE ... we don't restrict renaming the current
1006 * effective userid, though.
1009 roleid = HeapTupleGetOid(oldtuple);
1011 if (roleid == GetSessionUserId())
1012 ereport(ERROR,
1013 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1014 errmsg("session user cannot be renamed")));
1015 if (roleid == GetOuterUserId())
1016 ereport(ERROR,
1017 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1018 errmsg("current user cannot be renamed")));
1020 /* make sure the new name doesn't exist */
1021 if (SearchSysCacheExists(AUTHNAME,
1022 CStringGetDatum(newname),
1023 0, 0, 0))
1024 ereport(ERROR,
1025 (errcode(ERRCODE_DUPLICATE_OBJECT),
1026 errmsg("role \"%s\" already exists", newname)));
1028 if (strcmp(newname, "public") == 0 ||
1029 strcmp(newname, "none") == 0)
1030 ereport(ERROR,
1031 (errcode(ERRCODE_RESERVED_NAME),
1032 errmsg("role name \"%s\" is reserved",
1033 newname)));
1036 * createrole is enough privilege unless you want to mess with a superuser
1038 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
1040 if (!superuser())
1041 ereport(ERROR,
1042 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1043 errmsg("must be superuser to rename superusers")));
1045 else
1047 if (!have_createrole_privilege())
1048 ereport(ERROR,
1049 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1050 errmsg("permission denied to rename role")));
1053 /* OK, construct the modified tuple */
1054 for (i = 0; i < Natts_pg_authid; i++)
1055 repl_repl[i] = ' ';
1057 repl_repl[Anum_pg_authid_rolname - 1] = 'r';
1058 repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1059 CStringGetDatum(newname));
1060 repl_null[Anum_pg_authid_rolname - 1] = ' ';
1062 datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1064 if (!isnull && isMD5(TextDatumGetCString(datum)))
1066 /* MD5 uses the username as salt, so just clear it on a rename */
1067 repl_repl[Anum_pg_authid_rolpassword - 1] = 'r';
1068 repl_null[Anum_pg_authid_rolpassword - 1] = 'n';
1070 ereport(NOTICE,
1071 (errmsg("MD5 password cleared because of role rename")));
1074 newtuple = heap_modifytuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1075 simple_heap_update(rel, &oldtuple->t_self, newtuple);
1077 CatalogUpdateIndexes(rel, newtuple);
1079 ReleaseSysCache(oldtuple);
1082 * Close pg_authid, but keep lock till commit (this is important to
1083 * prevent any risk of deadlock failure while updating flat file)
1085 heap_close(rel, NoLock);
1088 * Set flag to update flat auth file at commit.
1090 auth_file_update_needed();
1094 * GrantRoleStmt
1096 * Grant/Revoke roles to/from roles
1098 void
1099 GrantRole(GrantRoleStmt *stmt)
1101 Relation pg_authid_rel;
1102 Oid grantor;
1103 List *grantee_ids;
1104 ListCell *item;
1106 if (stmt->grantor)
1107 grantor = get_roleid_checked(stmt->grantor);
1108 else
1109 grantor = GetUserId();
1111 grantee_ids = roleNamesToIds(stmt->grantee_roles);
1113 /* AccessShareLock is enough since we aren't modifying pg_authid */
1114 pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
1117 * Step through all of the granted roles and add/remove entries for the
1118 * grantees, or, if admin_opt is set, then just add/remove the admin
1119 * option.
1121 * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1123 foreach(item, stmt->granted_roles)
1125 char *rolename = strVal(lfirst(item));
1126 Oid roleid = get_roleid_checked(rolename);
1128 if (stmt->is_grant)
1129 AddRoleMems(rolename, roleid,
1130 stmt->grantee_roles, grantee_ids,
1131 grantor, stmt->admin_opt);
1132 else
1133 DelRoleMems(rolename, roleid,
1134 stmt->grantee_roles, grantee_ids,
1135 stmt->admin_opt);
1139 * Close pg_authid, but keep lock till commit (this is important to
1140 * prevent any risk of deadlock failure while updating flat file)
1142 heap_close(pg_authid_rel, NoLock);
1145 * Set flag to update flat auth file at commit.
1147 auth_file_update_needed();
1151 * DropOwnedObjects
1153 * Drop the objects owned by a given list of roles.
1155 void
1156 DropOwnedObjects(DropOwnedStmt *stmt)
1158 List *role_ids = roleNamesToIds(stmt->roles);
1159 ListCell *cell;
1161 /* Check privileges */
1162 foreach(cell, role_ids)
1164 Oid roleid = lfirst_oid(cell);
1166 if (!has_privs_of_role(GetUserId(), roleid))
1167 ereport(ERROR,
1168 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1169 errmsg("permission denied to drop objects")));
1172 /* Ok, do it */
1173 shdepDropOwned(role_ids, stmt->behavior);
1177 * ReassignOwnedObjects
1179 * Give the objects owned by a given list of roles away to another user.
1181 void
1182 ReassignOwnedObjects(ReassignOwnedStmt *stmt)
1184 List *role_ids = roleNamesToIds(stmt->roles);
1185 ListCell *cell;
1186 Oid newrole;
1188 /* Check privileges */
1189 foreach(cell, role_ids)
1191 Oid roleid = lfirst_oid(cell);
1193 if (!has_privs_of_role(GetUserId(), roleid))
1194 ereport(ERROR,
1195 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1196 errmsg("permission denied to reassign objects")));
1199 /* Must have privileges on the receiving side too */
1200 newrole = get_roleid_checked(stmt->newrole);
1202 if (!has_privs_of_role(GetUserId(), newrole))
1203 ereport(ERROR,
1204 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1205 errmsg("permission denied to reassign objects")));
1207 /* Ok, do it */
1208 shdepReassignOwned(role_ids, newrole);
1212 * roleNamesToIds
1214 * Given a list of role names (as String nodes), generate a list of role OIDs
1215 * in the same order.
1217 static List *
1218 roleNamesToIds(List *memberNames)
1220 List *result = NIL;
1221 ListCell *l;
1223 foreach(l, memberNames)
1225 char *rolename = strVal(lfirst(l));
1226 Oid roleid = get_roleid_checked(rolename);
1228 result = lappend_oid(result, roleid);
1230 return result;
1234 * AddRoleMems -- Add given members to the specified role
1236 * rolename: name of role to add to (used only for error messages)
1237 * roleid: OID of role to add to
1238 * memberNames: list of names of roles to add (used only for error messages)
1239 * memberIds: OIDs of roles to add
1240 * grantorId: who is granting the membership
1241 * admin_opt: granting admin option?
1243 * Note: caller is responsible for calling auth_file_update_needed().
1245 static void
1246 AddRoleMems(const char *rolename, Oid roleid,
1247 List *memberNames, List *memberIds,
1248 Oid grantorId, bool admin_opt)
1250 Relation pg_authmem_rel;
1251 TupleDesc pg_authmem_dsc;
1252 ListCell *nameitem;
1253 ListCell *iditem;
1255 Assert(list_length(memberNames) == list_length(memberIds));
1257 /* Skip permission check if nothing to do */
1258 if (!memberIds)
1259 return;
1262 * Check permissions: must have createrole or admin option on the role to
1263 * be changed. To mess with a superuser role, you gotta be superuser.
1265 if (superuser_arg(roleid))
1267 if (!superuser())
1268 ereport(ERROR,
1269 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1270 errmsg("must be superuser to alter superusers")));
1272 else
1274 if (!have_createrole_privilege() &&
1275 !is_admin_of_role(grantorId, roleid))
1276 ereport(ERROR,
1277 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1278 errmsg("must have admin option on role \"%s\"",
1279 rolename)));
1282 /* XXX not sure about this check */
1283 if (grantorId != GetUserId() && !superuser())
1284 ereport(ERROR,
1285 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1286 errmsg("must be superuser to set grantor")));
1288 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1289 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1291 forboth(nameitem, memberNames, iditem, memberIds)
1293 const char *membername = strVal(lfirst(nameitem));
1294 Oid memberid = lfirst_oid(iditem);
1295 HeapTuple authmem_tuple;
1296 HeapTuple tuple;
1297 Datum new_record[Natts_pg_auth_members];
1298 char new_record_nulls[Natts_pg_auth_members];
1299 char new_record_repl[Natts_pg_auth_members];
1302 * Refuse creation of membership loops, including the trivial case
1303 * where a role is made a member of itself. We do this by checking to
1304 * see if the target role is already a member of the proposed member
1305 * role. We have to ignore possible superuserness, however, else we
1306 * could never grant membership in a superuser-privileged role.
1308 if (is_member_of_role_nosuper(roleid, memberid))
1309 ereport(ERROR,
1310 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1311 (errmsg("role \"%s\" is a member of role \"%s\"",
1312 rolename, membername))));
1315 * Check if entry for this role/member already exists; if so, give
1316 * warning unless we are adding admin option.
1318 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1319 ObjectIdGetDatum(roleid),
1320 ObjectIdGetDatum(memberid),
1321 0, 0);
1322 if (HeapTupleIsValid(authmem_tuple) &&
1323 (!admin_opt ||
1324 ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1326 ereport(NOTICE,
1327 (errmsg("role \"%s\" is already a member of role \"%s\"",
1328 membername, rolename)));
1329 ReleaseSysCache(authmem_tuple);
1330 continue;
1333 /* Build a tuple to insert or update */
1334 MemSet(new_record, 0, sizeof(new_record));
1335 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1336 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1338 new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1339 new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1340 new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1341 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1343 if (HeapTupleIsValid(authmem_tuple))
1345 new_record_repl[Anum_pg_auth_members_grantor - 1] = 'r';
1346 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1347 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1348 new_record,
1349 new_record_nulls, new_record_repl);
1350 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1351 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1352 ReleaseSysCache(authmem_tuple);
1354 else
1356 tuple = heap_formtuple(pg_authmem_dsc,
1357 new_record, new_record_nulls);
1358 simple_heap_insert(pg_authmem_rel, tuple);
1359 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1362 /* CCI after each change, in case there are duplicates in list */
1363 CommandCounterIncrement();
1367 * Close pg_authmem, but keep lock till commit (this is important to
1368 * prevent any risk of deadlock failure while updating flat file)
1370 heap_close(pg_authmem_rel, NoLock);
1374 * DelRoleMems -- Remove given members from the specified role
1376 * rolename: name of role to del from (used only for error messages)
1377 * roleid: OID of role to del from
1378 * memberNames: list of names of roles to del (used only for error messages)
1379 * memberIds: OIDs of roles to del
1380 * admin_opt: remove admin option only?
1382 * Note: caller is responsible for calling auth_file_update_needed().
1384 static void
1385 DelRoleMems(const char *rolename, Oid roleid,
1386 List *memberNames, List *memberIds,
1387 bool admin_opt)
1389 Relation pg_authmem_rel;
1390 TupleDesc pg_authmem_dsc;
1391 ListCell *nameitem;
1392 ListCell *iditem;
1394 Assert(list_length(memberNames) == list_length(memberIds));
1396 /* Skip permission check if nothing to do */
1397 if (!memberIds)
1398 return;
1401 * Check permissions: must have createrole or admin option on the role to
1402 * be changed. To mess with a superuser role, you gotta be superuser.
1404 if (superuser_arg(roleid))
1406 if (!superuser())
1407 ereport(ERROR,
1408 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1409 errmsg("must be superuser to alter superusers")));
1411 else
1413 if (!have_createrole_privilege() &&
1414 !is_admin_of_role(GetUserId(), roleid))
1415 ereport(ERROR,
1416 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1417 errmsg("must have admin option on role \"%s\"",
1418 rolename)));
1421 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1422 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1424 forboth(nameitem, memberNames, iditem, memberIds)
1426 const char *membername = strVal(lfirst(nameitem));
1427 Oid memberid = lfirst_oid(iditem);
1428 HeapTuple authmem_tuple;
1431 * Find entry for this role/member
1433 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1434 ObjectIdGetDatum(roleid),
1435 ObjectIdGetDatum(memberid),
1436 0, 0);
1437 if (!HeapTupleIsValid(authmem_tuple))
1439 ereport(WARNING,
1440 (errmsg("role \"%s\" is not a member of role \"%s\"",
1441 membername, rolename)));
1442 continue;
1445 if (!admin_opt)
1447 /* Remove the entry altogether */
1448 simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
1450 else
1452 /* Just turn off the admin option */
1453 HeapTuple tuple;
1454 Datum new_record[Natts_pg_auth_members];
1455 char new_record_nulls[Natts_pg_auth_members];
1456 char new_record_repl[Natts_pg_auth_members];
1458 /* Build a tuple to update with */
1459 MemSet(new_record, 0, sizeof(new_record));
1460 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1461 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1463 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1464 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1466 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1467 new_record,
1468 new_record_nulls, new_record_repl);
1469 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1470 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1473 ReleaseSysCache(authmem_tuple);
1475 /* CCI after each change, in case there are duplicates in list */
1476 CommandCounterIncrement();
1480 * Close pg_authmem, but keep lock till commit (this is important to
1481 * prevent any risk of deadlock failure while updating flat file)
1483 heap_close(pg_authmem_rel, NoLock);