Don't reset pg_class.reltuples and relpages in VACUUM, if any pages were
[PostgreSQL.git] / src / backend / commands / user.c
blob6f800b2a34b5a37d8afc0c17c0f3c7b3c2a77624
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 "storage/lmgr.h"
27 #include "utils/acl.h"
28 #include "utils/builtins.h"
29 #include "utils/flatfiles.h"
30 #include "utils/fmgroids.h"
31 #include "utils/guc.h"
32 #include "utils/lsyscache.h"
33 #include "utils/syscache.h"
34 #include "utils/tqual.h"
37 extern bool Password_encryption;
39 static List *roleNamesToIds(List *memberNames);
40 static void AddRoleMems(const char *rolename, Oid roleid,
41 List *memberNames, List *memberIds,
42 Oid grantorId, bool admin_opt);
43 static void DelRoleMems(const char *rolename, Oid roleid,
44 List *memberNames, List *memberIds,
45 bool admin_opt);
48 /* Check if current user has createrole privileges */
49 static bool
50 have_createrole_privilege(void)
52 bool result = false;
53 HeapTuple utup;
55 /* Superusers can always do everything */
56 if (superuser())
57 return true;
59 utup = SearchSysCache(AUTHOID,
60 ObjectIdGetDatum(GetUserId()),
61 0, 0, 0);
62 if (HeapTupleIsValid(utup))
64 result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
65 ReleaseSysCache(utup);
67 return result;
72 * CREATE ROLE
74 void
75 CreateRole(CreateRoleStmt *stmt)
77 Relation pg_authid_rel;
78 TupleDesc pg_authid_dsc;
79 HeapTuple tuple;
80 Datum new_record[Natts_pg_authid];
81 bool new_record_nulls[Natts_pg_authid];
82 Oid roleid;
83 ListCell *item;
84 ListCell *option;
85 char *password = NULL; /* user password */
86 bool encrypt_password = Password_encryption; /* encrypt password? */
87 char encrypted_password[MD5_PASSWD_LEN + 1];
88 bool issuper = false; /* Make the user a superuser? */
89 bool inherit = true; /* Auto inherit privileges? */
90 bool createrole = false; /* Can this user create roles? */
91 bool createdb = false; /* Can the user create databases? */
92 bool canlogin = false; /* Can this user login? */
93 int connlimit = -1; /* maximum connections allowed */
94 List *addroleto = NIL; /* roles to make this a member of */
95 List *rolemembers = NIL; /* roles to be members of this role */
96 List *adminmembers = NIL; /* roles to be admins of this role */
97 char *validUntil = NULL; /* time the login is valid until */
98 DefElem *dpassword = NULL;
99 DefElem *dissuper = NULL;
100 DefElem *dinherit = NULL;
101 DefElem *dcreaterole = NULL;
102 DefElem *dcreatedb = NULL;
103 DefElem *dcanlogin = NULL;
104 DefElem *dconnlimit = NULL;
105 DefElem *daddroleto = NULL;
106 DefElem *drolemembers = NULL;
107 DefElem *dadminmembers = NULL;
108 DefElem *dvalidUntil = NULL;
110 /* The defaults can vary depending on the original statement type */
111 switch (stmt->stmt_type)
113 case ROLESTMT_ROLE:
114 break;
115 case ROLESTMT_USER:
116 canlogin = true;
117 /* may eventually want inherit to default to false here */
118 break;
119 case ROLESTMT_GROUP:
120 break;
123 /* Extract options from the statement node tree */
124 foreach(option, stmt->options)
126 DefElem *defel = (DefElem *) lfirst(option);
128 if (strcmp(defel->defname, "password") == 0 ||
129 strcmp(defel->defname, "encryptedPassword") == 0 ||
130 strcmp(defel->defname, "unencryptedPassword") == 0)
132 if (dpassword)
133 ereport(ERROR,
134 (errcode(ERRCODE_SYNTAX_ERROR),
135 errmsg("conflicting or redundant options")));
136 dpassword = defel;
137 if (strcmp(defel->defname, "encryptedPassword") == 0)
138 encrypt_password = true;
139 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
140 encrypt_password = false;
142 else if (strcmp(defel->defname, "sysid") == 0)
144 ereport(NOTICE,
145 (errmsg("SYSID can no longer be specified")));
147 else if (strcmp(defel->defname, "superuser") == 0)
149 if (dissuper)
150 ereport(ERROR,
151 (errcode(ERRCODE_SYNTAX_ERROR),
152 errmsg("conflicting or redundant options")));
153 dissuper = defel;
155 else if (strcmp(defel->defname, "inherit") == 0)
157 if (dinherit)
158 ereport(ERROR,
159 (errcode(ERRCODE_SYNTAX_ERROR),
160 errmsg("conflicting or redundant options")));
161 dinherit = defel;
163 else if (strcmp(defel->defname, "createrole") == 0)
165 if (dcreaterole)
166 ereport(ERROR,
167 (errcode(ERRCODE_SYNTAX_ERROR),
168 errmsg("conflicting or redundant options")));
169 dcreaterole = defel;
171 else if (strcmp(defel->defname, "createdb") == 0)
173 if (dcreatedb)
174 ereport(ERROR,
175 (errcode(ERRCODE_SYNTAX_ERROR),
176 errmsg("conflicting or redundant options")));
177 dcreatedb = defel;
179 else if (strcmp(defel->defname, "canlogin") == 0)
181 if (dcanlogin)
182 ereport(ERROR,
183 (errcode(ERRCODE_SYNTAX_ERROR),
184 errmsg("conflicting or redundant options")));
185 dcanlogin = defel;
187 else if (strcmp(defel->defname, "connectionlimit") == 0)
189 if (dconnlimit)
190 ereport(ERROR,
191 (errcode(ERRCODE_SYNTAX_ERROR),
192 errmsg("conflicting or redundant options")));
193 dconnlimit = defel;
195 else if (strcmp(defel->defname, "addroleto") == 0)
197 if (daddroleto)
198 ereport(ERROR,
199 (errcode(ERRCODE_SYNTAX_ERROR),
200 errmsg("conflicting or redundant options")));
201 daddroleto = defel;
203 else if (strcmp(defel->defname, "rolemembers") == 0)
205 if (drolemembers)
206 ereport(ERROR,
207 (errcode(ERRCODE_SYNTAX_ERROR),
208 errmsg("conflicting or redundant options")));
209 drolemembers = defel;
211 else if (strcmp(defel->defname, "adminmembers") == 0)
213 if (dadminmembers)
214 ereport(ERROR,
215 (errcode(ERRCODE_SYNTAX_ERROR),
216 errmsg("conflicting or redundant options")));
217 dadminmembers = defel;
219 else if (strcmp(defel->defname, "validUntil") == 0)
221 if (dvalidUntil)
222 ereport(ERROR,
223 (errcode(ERRCODE_SYNTAX_ERROR),
224 errmsg("conflicting or redundant options")));
225 dvalidUntil = defel;
227 else
228 elog(ERROR, "option \"%s\" not recognized",
229 defel->defname);
232 if (dpassword && dpassword->arg)
233 password = strVal(dpassword->arg);
234 if (dissuper)
235 issuper = intVal(dissuper->arg) != 0;
236 if (dinherit)
237 inherit = intVal(dinherit->arg) != 0;
238 if (dcreaterole)
239 createrole = intVal(dcreaterole->arg) != 0;
240 if (dcreatedb)
241 createdb = intVal(dcreatedb->arg) != 0;
242 if (dcanlogin)
243 canlogin = intVal(dcanlogin->arg) != 0;
244 if (dconnlimit)
245 connlimit = intVal(dconnlimit->arg);
246 if (daddroleto)
247 addroleto = (List *) daddroleto->arg;
248 if (drolemembers)
249 rolemembers = (List *) drolemembers->arg;
250 if (dadminmembers)
251 adminmembers = (List *) dadminmembers->arg;
252 if (dvalidUntil)
253 validUntil = strVal(dvalidUntil->arg);
255 /* Check some permissions first */
256 if (issuper)
258 if (!superuser())
259 ereport(ERROR,
260 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
261 errmsg("must be superuser to create superusers")));
263 else
265 if (!have_createrole_privilege())
266 ereport(ERROR,
267 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
268 errmsg("permission denied to create role")));
271 if (strcmp(stmt->role, "public") == 0 ||
272 strcmp(stmt->role, "none") == 0)
273 ereport(ERROR,
274 (errcode(ERRCODE_RESERVED_NAME),
275 errmsg("role name \"%s\" is reserved",
276 stmt->role)));
279 * Check the pg_authid relation to be certain the role doesn't already
280 * exist.
282 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
283 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
285 tuple = SearchSysCache(AUTHNAME,
286 PointerGetDatum(stmt->role),
287 0, 0, 0);
288 if (HeapTupleIsValid(tuple))
289 ereport(ERROR,
290 (errcode(ERRCODE_DUPLICATE_OBJECT),
291 errmsg("role \"%s\" already exists",
292 stmt->role)));
295 * Build a tuple to insert
297 MemSet(new_record, 0, sizeof(new_record));
298 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
300 new_record[Anum_pg_authid_rolname - 1] =
301 DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
303 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
304 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
305 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
306 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
307 /* superuser gets catupdate right by default */
308 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
309 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
310 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
312 if (password)
314 if (!encrypt_password || isMD5(password))
315 new_record[Anum_pg_authid_rolpassword - 1] =
316 CStringGetTextDatum(password);
317 else
319 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
320 encrypted_password))
321 elog(ERROR, "password encryption failed");
322 new_record[Anum_pg_authid_rolpassword - 1] =
323 CStringGetTextDatum(encrypted_password);
326 else
327 new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
329 if (validUntil)
330 new_record[Anum_pg_authid_rolvaliduntil - 1] =
331 DirectFunctionCall3(timestamptz_in,
332 CStringGetDatum(validUntil),
333 ObjectIdGetDatum(InvalidOid),
334 Int32GetDatum(-1));
336 else
337 new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = true;
339 new_record_nulls[Anum_pg_authid_rolconfig - 1] = true;
341 tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
344 * Insert new record in the pg_authid table
346 roleid = simple_heap_insert(pg_authid_rel, tuple);
347 CatalogUpdateIndexes(pg_authid_rel, tuple);
350 * Advance command counter so we can see new record; else tests in
351 * AddRoleMems may fail.
353 if (addroleto || adminmembers || rolemembers)
354 CommandCounterIncrement();
357 * Add the new role to the specified existing roles.
359 foreach(item, addroleto)
361 char *oldrolename = strVal(lfirst(item));
362 Oid oldroleid = get_roleid_checked(oldrolename);
364 AddRoleMems(oldrolename, oldroleid,
365 list_make1(makeString(stmt->role)),
366 list_make1_oid(roleid),
367 GetUserId(), false);
371 * Add the specified members to this new role. adminmembers get the admin
372 * option, rolemembers don't.
374 AddRoleMems(stmt->role, roleid,
375 adminmembers, roleNamesToIds(adminmembers),
376 GetUserId(), true);
377 AddRoleMems(stmt->role, roleid,
378 rolemembers, roleNamesToIds(rolemembers),
379 GetUserId(), false);
382 * Close pg_authid, but keep lock till commit (this is important to
383 * prevent any risk of deadlock failure while updating flat file)
385 heap_close(pg_authid_rel, NoLock);
388 * Set flag to update flat auth file at commit.
390 auth_file_update_needed();
395 * ALTER ROLE
397 * Note: the rolemembers option accepted here is intended to support the
398 * backwards-compatible ALTER GROUP syntax. Although it will work to say
399 * "ALTER ROLE role ROLE rolenames", we don't document it.
401 void
402 AlterRole(AlterRoleStmt *stmt)
404 Datum new_record[Natts_pg_authid];
405 bool new_record_nulls[Natts_pg_authid];
406 bool new_record_repl[Natts_pg_authid];
407 Relation pg_authid_rel;
408 TupleDesc pg_authid_dsc;
409 HeapTuple tuple,
410 new_tuple;
411 ListCell *option;
412 char *password = NULL; /* user password */
413 bool encrypt_password = Password_encryption; /* encrypt password? */
414 char encrypted_password[MD5_PASSWD_LEN + 1];
415 int issuper = -1; /* Make the user a superuser? */
416 int inherit = -1; /* Auto inherit privileges? */
417 int createrole = -1; /* Can this user create roles? */
418 int createdb = -1; /* Can the user create databases? */
419 int canlogin = -1; /* Can this user login? */
420 int connlimit = -1; /* maximum connections allowed */
421 List *rolemembers = NIL; /* roles to be added/removed */
422 char *validUntil = NULL; /* time the login is valid until */
423 DefElem *dpassword = NULL;
424 DefElem *dissuper = NULL;
425 DefElem *dinherit = NULL;
426 DefElem *dcreaterole = NULL;
427 DefElem *dcreatedb = NULL;
428 DefElem *dcanlogin = NULL;
429 DefElem *dconnlimit = NULL;
430 DefElem *drolemembers = NULL;
431 DefElem *dvalidUntil = NULL;
432 Oid roleid;
434 /* Extract options from the statement node tree */
435 foreach(option, stmt->options)
437 DefElem *defel = (DefElem *) lfirst(option);
439 if (strcmp(defel->defname, "password") == 0 ||
440 strcmp(defel->defname, "encryptedPassword") == 0 ||
441 strcmp(defel->defname, "unencryptedPassword") == 0)
443 if (dpassword)
444 ereport(ERROR,
445 (errcode(ERRCODE_SYNTAX_ERROR),
446 errmsg("conflicting or redundant options")));
447 dpassword = defel;
448 if (strcmp(defel->defname, "encryptedPassword") == 0)
449 encrypt_password = true;
450 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
451 encrypt_password = false;
453 else if (strcmp(defel->defname, "superuser") == 0)
455 if (dissuper)
456 ereport(ERROR,
457 (errcode(ERRCODE_SYNTAX_ERROR),
458 errmsg("conflicting or redundant options")));
459 dissuper = defel;
461 else if (strcmp(defel->defname, "inherit") == 0)
463 if (dinherit)
464 ereport(ERROR,
465 (errcode(ERRCODE_SYNTAX_ERROR),
466 errmsg("conflicting or redundant options")));
467 dinherit = defel;
469 else if (strcmp(defel->defname, "createrole") == 0)
471 if (dcreaterole)
472 ereport(ERROR,
473 (errcode(ERRCODE_SYNTAX_ERROR),
474 errmsg("conflicting or redundant options")));
475 dcreaterole = defel;
477 else if (strcmp(defel->defname, "createdb") == 0)
479 if (dcreatedb)
480 ereport(ERROR,
481 (errcode(ERRCODE_SYNTAX_ERROR),
482 errmsg("conflicting or redundant options")));
483 dcreatedb = defel;
485 else if (strcmp(defel->defname, "canlogin") == 0)
487 if (dcanlogin)
488 ereport(ERROR,
489 (errcode(ERRCODE_SYNTAX_ERROR),
490 errmsg("conflicting or redundant options")));
491 dcanlogin = defel;
493 else if (strcmp(defel->defname, "connectionlimit") == 0)
495 if (dconnlimit)
496 ereport(ERROR,
497 (errcode(ERRCODE_SYNTAX_ERROR),
498 errmsg("conflicting or redundant options")));
499 dconnlimit = defel;
501 else if (strcmp(defel->defname, "rolemembers") == 0 &&
502 stmt->action != 0)
504 if (drolemembers)
505 ereport(ERROR,
506 (errcode(ERRCODE_SYNTAX_ERROR),
507 errmsg("conflicting or redundant options")));
508 drolemembers = defel;
510 else if (strcmp(defel->defname, "validUntil") == 0)
512 if (dvalidUntil)
513 ereport(ERROR,
514 (errcode(ERRCODE_SYNTAX_ERROR),
515 errmsg("conflicting or redundant options")));
516 dvalidUntil = defel;
518 else
519 elog(ERROR, "option \"%s\" not recognized",
520 defel->defname);
523 if (dpassword && dpassword->arg)
524 password = strVal(dpassword->arg);
525 if (dissuper)
526 issuper = intVal(dissuper->arg);
527 if (dinherit)
528 inherit = intVal(dinherit->arg);
529 if (dcreaterole)
530 createrole = intVal(dcreaterole->arg);
531 if (dcreatedb)
532 createdb = intVal(dcreatedb->arg);
533 if (dcanlogin)
534 canlogin = intVal(dcanlogin->arg);
535 if (dconnlimit)
536 connlimit = intVal(dconnlimit->arg);
537 if (drolemembers)
538 rolemembers = (List *) drolemembers->arg;
539 if (dvalidUntil)
540 validUntil = strVal(dvalidUntil->arg);
543 * Scan the pg_authid relation to be certain the user exists.
545 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
546 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
548 tuple = SearchSysCache(AUTHNAME,
549 PointerGetDatum(stmt->role),
550 0, 0, 0);
551 if (!HeapTupleIsValid(tuple))
552 ereport(ERROR,
553 (errcode(ERRCODE_UNDEFINED_OBJECT),
554 errmsg("role \"%s\" does not exist", stmt->role)));
556 roleid = HeapTupleGetOid(tuple);
559 * To mess with a superuser you gotta be superuser; else you need
560 * createrole, or just want to change your own password
562 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
564 if (!superuser())
565 ereport(ERROR,
566 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
567 errmsg("must be superuser to alter superusers")));
569 else if (!have_createrole_privilege())
571 if (!(inherit < 0 &&
572 createrole < 0 &&
573 createdb < 0 &&
574 canlogin < 0 &&
575 !dconnlimit &&
576 !rolemembers &&
577 !validUntil &&
578 dpassword &&
579 roleid == GetUserId()))
580 ereport(ERROR,
581 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
582 errmsg("permission denied")));
586 * Build an updated tuple, perusing the information just obtained
588 MemSet(new_record, 0, sizeof(new_record));
589 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
590 MemSet(new_record_repl, false, sizeof(new_record_repl));
593 * issuper/createrole/catupdate/etc
595 * XXX It's rather unclear how to handle catupdate. It's probably best to
596 * keep it equal to the superuser status, otherwise you could end up with
597 * a situation where no existing superuser can alter the catalogs,
598 * including pg_authid!
600 if (issuper >= 0)
602 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
603 new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
605 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
606 new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
609 if (inherit >= 0)
611 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
612 new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
615 if (createrole >= 0)
617 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
618 new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
621 if (createdb >= 0)
623 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
624 new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
627 if (canlogin >= 0)
629 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
630 new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
633 if (dconnlimit)
635 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
636 new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
639 /* password */
640 if (password)
642 if (!encrypt_password || isMD5(password))
643 new_record[Anum_pg_authid_rolpassword - 1] =
644 CStringGetTextDatum(password);
645 else
647 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
648 encrypted_password))
649 elog(ERROR, "password encryption failed");
650 new_record[Anum_pg_authid_rolpassword - 1] =
651 CStringGetTextDatum(encrypted_password);
653 new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
656 /* unset password */
657 if (dpassword && dpassword->arg == NULL)
659 new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
660 new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
663 /* valid until */
664 if (validUntil)
666 new_record[Anum_pg_authid_rolvaliduntil - 1] =
667 DirectFunctionCall3(timestamptz_in,
668 CStringGetDatum(validUntil),
669 ObjectIdGetDatum(InvalidOid),
670 Int32GetDatum(-1));
671 new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
674 new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
675 new_record_nulls, new_record_repl);
676 simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
678 /* Update indexes */
679 CatalogUpdateIndexes(pg_authid_rel, new_tuple);
681 ReleaseSysCache(tuple);
682 heap_freetuple(new_tuple);
685 * Advance command counter so we can see new record; else tests in
686 * AddRoleMems may fail.
688 if (rolemembers)
689 CommandCounterIncrement();
691 if (stmt->action == +1) /* add members to role */
692 AddRoleMems(stmt->role, roleid,
693 rolemembers, roleNamesToIds(rolemembers),
694 GetUserId(), false);
695 else if (stmt->action == -1) /* drop members from role */
696 DelRoleMems(stmt->role, roleid,
697 rolemembers, roleNamesToIds(rolemembers),
698 false);
701 * Close pg_authid, but keep lock till commit (this is important to
702 * prevent any risk of deadlock failure while updating flat file)
704 heap_close(pg_authid_rel, NoLock);
707 * Set flag to update flat auth file at commit.
709 auth_file_update_needed();
714 * ALTER ROLE ... SET
716 void
717 AlterRoleSet(AlterRoleSetStmt *stmt)
719 char *valuestr;
720 HeapTuple oldtuple,
721 newtuple;
722 Relation rel;
723 Datum repl_val[Natts_pg_authid];
724 bool repl_null[Natts_pg_authid];
725 bool repl_repl[Natts_pg_authid];
727 valuestr = ExtractSetVariableArgs(stmt->setstmt);
729 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
730 oldtuple = SearchSysCache(AUTHNAME,
731 PointerGetDatum(stmt->role),
732 0, 0, 0);
733 if (!HeapTupleIsValid(oldtuple))
734 ereport(ERROR,
735 (errcode(ERRCODE_UNDEFINED_OBJECT),
736 errmsg("role \"%s\" does not exist", stmt->role)));
739 * To mess with a superuser you gotta be superuser; else you need
740 * createrole, or just want to change your own settings
742 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
744 if (!superuser())
745 ereport(ERROR,
746 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
747 errmsg("must be superuser to alter superusers")));
749 else
751 if (!have_createrole_privilege() &&
752 HeapTupleGetOid(oldtuple) != GetUserId())
753 ereport(ERROR,
754 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
755 errmsg("permission denied")));
758 memset(repl_repl, false, sizeof(repl_repl));
759 repl_repl[Anum_pg_authid_rolconfig - 1] = true;
761 if (stmt->setstmt->kind == VAR_RESET_ALL)
763 /* RESET ALL, so just set rolconfig to null */
764 repl_null[Anum_pg_authid_rolconfig - 1] = true;
765 repl_val[Anum_pg_authid_rolconfig - 1] = (Datum) 0;
767 else
769 Datum datum;
770 bool isnull;
771 ArrayType *array;
773 repl_null[Anum_pg_authid_rolconfig - 1] = false;
775 /* Extract old value of rolconfig */
776 datum = SysCacheGetAttr(AUTHNAME, oldtuple,
777 Anum_pg_authid_rolconfig, &isnull);
778 array = isnull ? NULL : DatumGetArrayTypeP(datum);
780 /* Update (valuestr is NULL in RESET cases) */
781 if (valuestr)
782 array = GUCArrayAdd(array, stmt->setstmt->name, valuestr);
783 else
784 array = GUCArrayDelete(array, stmt->setstmt->name);
786 if (array)
787 repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array);
788 else
789 repl_null[Anum_pg_authid_rolconfig - 1] = true;
792 newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
793 repl_val, repl_null, repl_repl);
795 simple_heap_update(rel, &oldtuple->t_self, newtuple);
796 CatalogUpdateIndexes(rel, newtuple);
798 ReleaseSysCache(oldtuple);
799 /* needn't keep lock since we won't be updating the flat file */
800 heap_close(rel, RowExclusiveLock);
805 * DROP ROLE
807 void
808 DropRole(DropRoleStmt *stmt)
810 Relation pg_authid_rel,
811 pg_auth_members_rel;
812 ListCell *item;
814 if (!have_createrole_privilege())
815 ereport(ERROR,
816 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
817 errmsg("permission denied to drop role")));
820 * Scan the pg_authid relation to find the Oid of the role(s) to be
821 * deleted.
823 pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
824 pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
826 foreach(item, stmt->roles)
828 const char *role = strVal(lfirst(item));
829 HeapTuple tuple,
830 tmp_tuple;
831 ScanKeyData scankey;
832 char *detail;
833 char *detail_log;
834 SysScanDesc sscan;
835 Oid roleid;
837 tuple = SearchSysCache(AUTHNAME,
838 PointerGetDatum(role),
839 0, 0, 0);
840 if (!HeapTupleIsValid(tuple))
842 if (!stmt->missing_ok)
844 ereport(ERROR,
845 (errcode(ERRCODE_UNDEFINED_OBJECT),
846 errmsg("role \"%s\" does not exist", role)));
848 else
850 ereport(NOTICE,
851 (errmsg("role \"%s\" does not exist, skipping",
852 role)));
855 continue;
858 roleid = HeapTupleGetOid(tuple);
860 if (roleid == GetUserId())
861 ereport(ERROR,
862 (errcode(ERRCODE_OBJECT_IN_USE),
863 errmsg("current user cannot be dropped")));
864 if (roleid == GetOuterUserId())
865 ereport(ERROR,
866 (errcode(ERRCODE_OBJECT_IN_USE),
867 errmsg("current user cannot be dropped")));
868 if (roleid == GetSessionUserId())
869 ereport(ERROR,
870 (errcode(ERRCODE_OBJECT_IN_USE),
871 errmsg("session user cannot be dropped")));
874 * For safety's sake, we allow createrole holders to drop ordinary
875 * roles but not superuser roles. This is mainly to avoid the
876 * scenario where you accidentally drop the last superuser.
878 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
879 !superuser())
880 ereport(ERROR,
881 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
882 errmsg("must be superuser to drop superusers")));
885 * Lock the role, so nobody can add dependencies to her while we drop
886 * her. We keep the lock until the end of transaction.
888 LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
890 /* Check for pg_shdepend entries depending on this role */
891 if (checkSharedDependencies(AuthIdRelationId, roleid,
892 &detail, &detail_log))
893 ereport(ERROR,
894 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
895 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
896 role),
897 errdetail("%s", detail),
898 errdetail_log("%s", detail_log)));
901 * Remove the role from the pg_authid table
903 simple_heap_delete(pg_authid_rel, &tuple->t_self);
905 ReleaseSysCache(tuple);
908 * Remove role from the pg_auth_members table. We have to remove all
909 * tuples that show it as either a role or a member.
911 * XXX what about grantor entries? Maybe we should do one heap scan.
913 ScanKeyInit(&scankey,
914 Anum_pg_auth_members_roleid,
915 BTEqualStrategyNumber, F_OIDEQ,
916 ObjectIdGetDatum(roleid));
918 sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
919 true, SnapshotNow, 1, &scankey);
921 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
923 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
926 systable_endscan(sscan);
928 ScanKeyInit(&scankey,
929 Anum_pg_auth_members_member,
930 BTEqualStrategyNumber, F_OIDEQ,
931 ObjectIdGetDatum(roleid));
933 sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
934 true, SnapshotNow, 1, &scankey);
936 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
938 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
941 systable_endscan(sscan);
944 * Remove any comments on this role.
946 DeleteSharedComments(roleid, AuthIdRelationId);
949 * Advance command counter so that later iterations of this loop will
950 * see the changes already made. This is essential if, for example,
951 * we are trying to drop both a role and one of its direct members ---
952 * we'll get an error if we try to delete the linking pg_auth_members
953 * tuple twice. (We do not need a CCI between the two delete loops
954 * above, because it's not allowed for a role to directly contain
955 * itself.)
957 CommandCounterIncrement();
961 * Now we can clean up; but keep locks until commit (to avoid possible
962 * deadlock failure while updating flat file)
964 heap_close(pg_auth_members_rel, NoLock);
965 heap_close(pg_authid_rel, NoLock);
968 * Set flag to update flat auth file at commit.
970 auth_file_update_needed();
974 * Rename role
976 void
977 RenameRole(const char *oldname, const char *newname)
979 HeapTuple oldtuple,
980 newtuple;
981 TupleDesc dsc;
982 Relation rel;
983 Datum datum;
984 bool isnull;
985 Datum repl_val[Natts_pg_authid];
986 bool repl_null[Natts_pg_authid];
987 bool repl_repl[Natts_pg_authid];
988 int i;
989 Oid roleid;
991 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
992 dsc = RelationGetDescr(rel);
994 oldtuple = SearchSysCache(AUTHNAME,
995 CStringGetDatum(oldname),
996 0, 0, 0);
997 if (!HeapTupleIsValid(oldtuple))
998 ereport(ERROR,
999 (errcode(ERRCODE_UNDEFINED_OBJECT),
1000 errmsg("role \"%s\" does not exist", oldname)));
1003 * XXX Client applications probably store the session user somewhere, so
1004 * renaming it could cause confusion. On the other hand, there may not be
1005 * an actual problem besides a little confusion, so think about this and
1006 * decide. Same for SET ROLE ... we don't restrict renaming the current
1007 * effective userid, though.
1010 roleid = HeapTupleGetOid(oldtuple);
1012 if (roleid == GetSessionUserId())
1013 ereport(ERROR,
1014 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1015 errmsg("session user cannot be renamed")));
1016 if (roleid == GetOuterUserId())
1017 ereport(ERROR,
1018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1019 errmsg("current user cannot be renamed")));
1021 /* make sure the new name doesn't exist */
1022 if (SearchSysCacheExists(AUTHNAME,
1023 CStringGetDatum(newname),
1024 0, 0, 0))
1025 ereport(ERROR,
1026 (errcode(ERRCODE_DUPLICATE_OBJECT),
1027 errmsg("role \"%s\" already exists", newname)));
1029 if (strcmp(newname, "public") == 0 ||
1030 strcmp(newname, "none") == 0)
1031 ereport(ERROR,
1032 (errcode(ERRCODE_RESERVED_NAME),
1033 errmsg("role name \"%s\" is reserved",
1034 newname)));
1037 * createrole is enough privilege unless you want to mess with a superuser
1039 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
1041 if (!superuser())
1042 ereport(ERROR,
1043 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1044 errmsg("must be superuser to rename superusers")));
1046 else
1048 if (!have_createrole_privilege())
1049 ereport(ERROR,
1050 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1051 errmsg("permission denied to rename role")));
1054 /* OK, construct the modified tuple */
1055 for (i = 0; i < Natts_pg_authid; i++)
1056 repl_repl[i] = false;
1058 repl_repl[Anum_pg_authid_rolname - 1] = true;
1059 repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1060 CStringGetDatum(newname));
1061 repl_null[Anum_pg_authid_rolname - 1] = false;
1063 datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1065 if (!isnull && isMD5(TextDatumGetCString(datum)))
1067 /* MD5 uses the username as salt, so just clear it on a rename */
1068 repl_repl[Anum_pg_authid_rolpassword - 1] = true;
1069 repl_null[Anum_pg_authid_rolpassword - 1] = true;
1071 ereport(NOTICE,
1072 (errmsg("MD5 password cleared because of role rename")));
1075 newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1076 simple_heap_update(rel, &oldtuple->t_self, newtuple);
1078 CatalogUpdateIndexes(rel, newtuple);
1080 ReleaseSysCache(oldtuple);
1083 * Close pg_authid, but keep lock till commit (this is important to
1084 * prevent any risk of deadlock failure while updating flat file)
1086 heap_close(rel, NoLock);
1089 * Set flag to update flat auth file at commit.
1091 auth_file_update_needed();
1095 * GrantRoleStmt
1097 * Grant/Revoke roles to/from roles
1099 void
1100 GrantRole(GrantRoleStmt *stmt)
1102 Relation pg_authid_rel;
1103 Oid grantor;
1104 List *grantee_ids;
1105 ListCell *item;
1107 if (stmt->grantor)
1108 grantor = get_roleid_checked(stmt->grantor);
1109 else
1110 grantor = GetUserId();
1112 grantee_ids = roleNamesToIds(stmt->grantee_roles);
1114 /* AccessShareLock is enough since we aren't modifying pg_authid */
1115 pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
1118 * Step through all of the granted roles and add/remove entries for the
1119 * grantees, or, if admin_opt is set, then just add/remove the admin
1120 * option.
1122 * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1124 foreach(item, stmt->granted_roles)
1126 char *rolename = strVal(lfirst(item));
1127 Oid roleid = get_roleid_checked(rolename);
1129 if (stmt->is_grant)
1130 AddRoleMems(rolename, roleid,
1131 stmt->grantee_roles, grantee_ids,
1132 grantor, stmt->admin_opt);
1133 else
1134 DelRoleMems(rolename, roleid,
1135 stmt->grantee_roles, grantee_ids,
1136 stmt->admin_opt);
1140 * Close pg_authid, but keep lock till commit (this is important to
1141 * prevent any risk of deadlock failure while updating flat file)
1143 heap_close(pg_authid_rel, NoLock);
1146 * Set flag to update flat auth file at commit.
1148 auth_file_update_needed();
1152 * DropOwnedObjects
1154 * Drop the objects owned by a given list of roles.
1156 void
1157 DropOwnedObjects(DropOwnedStmt *stmt)
1159 List *role_ids = roleNamesToIds(stmt->roles);
1160 ListCell *cell;
1162 /* Check privileges */
1163 foreach(cell, role_ids)
1165 Oid roleid = lfirst_oid(cell);
1167 if (!has_privs_of_role(GetUserId(), roleid))
1168 ereport(ERROR,
1169 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1170 errmsg("permission denied to drop objects")));
1173 /* Ok, do it */
1174 shdepDropOwned(role_ids, stmt->behavior);
1178 * ReassignOwnedObjects
1180 * Give the objects owned by a given list of roles away to another user.
1182 void
1183 ReassignOwnedObjects(ReassignOwnedStmt *stmt)
1185 List *role_ids = roleNamesToIds(stmt->roles);
1186 ListCell *cell;
1187 Oid newrole;
1189 /* Check privileges */
1190 foreach(cell, role_ids)
1192 Oid roleid = lfirst_oid(cell);
1194 if (!has_privs_of_role(GetUserId(), roleid))
1195 ereport(ERROR,
1196 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1197 errmsg("permission denied to reassign objects")));
1200 /* Must have privileges on the receiving side too */
1201 newrole = get_roleid_checked(stmt->newrole);
1203 if (!has_privs_of_role(GetUserId(), newrole))
1204 ereport(ERROR,
1205 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1206 errmsg("permission denied to reassign objects")));
1208 /* Ok, do it */
1209 shdepReassignOwned(role_ids, newrole);
1213 * roleNamesToIds
1215 * Given a list of role names (as String nodes), generate a list of role OIDs
1216 * in the same order.
1218 static List *
1219 roleNamesToIds(List *memberNames)
1221 List *result = NIL;
1222 ListCell *l;
1224 foreach(l, memberNames)
1226 char *rolename = strVal(lfirst(l));
1227 Oid roleid = get_roleid_checked(rolename);
1229 result = lappend_oid(result, roleid);
1231 return result;
1235 * AddRoleMems -- Add given members to the specified role
1237 * rolename: name of role to add to (used only for error messages)
1238 * roleid: OID of role to add to
1239 * memberNames: list of names of roles to add (used only for error messages)
1240 * memberIds: OIDs of roles to add
1241 * grantorId: who is granting the membership
1242 * admin_opt: granting admin option?
1244 * Note: caller is responsible for calling auth_file_update_needed().
1246 static void
1247 AddRoleMems(const char *rolename, Oid roleid,
1248 List *memberNames, List *memberIds,
1249 Oid grantorId, bool admin_opt)
1251 Relation pg_authmem_rel;
1252 TupleDesc pg_authmem_dsc;
1253 ListCell *nameitem;
1254 ListCell *iditem;
1256 Assert(list_length(memberNames) == list_length(memberIds));
1258 /* Skip permission check if nothing to do */
1259 if (!memberIds)
1260 return;
1263 * Check permissions: must have createrole or admin option on the role to
1264 * be changed. To mess with a superuser role, you gotta be superuser.
1266 if (superuser_arg(roleid))
1268 if (!superuser())
1269 ereport(ERROR,
1270 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1271 errmsg("must be superuser to alter superusers")));
1273 else
1275 if (!have_createrole_privilege() &&
1276 !is_admin_of_role(grantorId, roleid))
1277 ereport(ERROR,
1278 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1279 errmsg("must have admin option on role \"%s\"",
1280 rolename)));
1283 /* XXX not sure about this check */
1284 if (grantorId != GetUserId() && !superuser())
1285 ereport(ERROR,
1286 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1287 errmsg("must be superuser to set grantor")));
1289 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1290 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1292 forboth(nameitem, memberNames, iditem, memberIds)
1294 const char *membername = strVal(lfirst(nameitem));
1295 Oid memberid = lfirst_oid(iditem);
1296 HeapTuple authmem_tuple;
1297 HeapTuple tuple;
1298 Datum new_record[Natts_pg_auth_members];
1299 bool new_record_nulls[Natts_pg_auth_members];
1300 bool new_record_repl[Natts_pg_auth_members];
1303 * Refuse creation of membership loops, including the trivial case
1304 * where a role is made a member of itself. We do this by checking to
1305 * see if the target role is already a member of the proposed member
1306 * role. We have to ignore possible superuserness, however, else we
1307 * could never grant membership in a superuser-privileged role.
1309 if (is_member_of_role_nosuper(roleid, memberid))
1310 ereport(ERROR,
1311 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1312 (errmsg("role \"%s\" is a member of role \"%s\"",
1313 rolename, membername))));
1316 * Check if entry for this role/member already exists; if so, give
1317 * warning unless we are adding admin option.
1319 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1320 ObjectIdGetDatum(roleid),
1321 ObjectIdGetDatum(memberid),
1322 0, 0);
1323 if (HeapTupleIsValid(authmem_tuple) &&
1324 (!admin_opt ||
1325 ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1327 ereport(NOTICE,
1328 (errmsg("role \"%s\" is already a member of role \"%s\"",
1329 membername, rolename)));
1330 ReleaseSysCache(authmem_tuple);
1331 continue;
1334 /* Build a tuple to insert or update */
1335 MemSet(new_record, 0, sizeof(new_record));
1336 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
1337 MemSet(new_record_repl, false, sizeof(new_record_repl));
1339 new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1340 new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1341 new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1342 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1344 if (HeapTupleIsValid(authmem_tuple))
1346 new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
1347 new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
1348 tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1349 new_record,
1350 new_record_nulls, new_record_repl);
1351 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1352 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1353 ReleaseSysCache(authmem_tuple);
1355 else
1357 tuple = heap_form_tuple(pg_authmem_dsc,
1358 new_record, new_record_nulls);
1359 simple_heap_insert(pg_authmem_rel, tuple);
1360 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1363 /* CCI after each change, in case there are duplicates in list */
1364 CommandCounterIncrement();
1368 * Close pg_authmem, but keep lock till commit (this is important to
1369 * prevent any risk of deadlock failure while updating flat file)
1371 heap_close(pg_authmem_rel, NoLock);
1375 * DelRoleMems -- Remove given members from the specified role
1377 * rolename: name of role to del from (used only for error messages)
1378 * roleid: OID of role to del from
1379 * memberNames: list of names of roles to del (used only for error messages)
1380 * memberIds: OIDs of roles to del
1381 * admin_opt: remove admin option only?
1383 * Note: caller is responsible for calling auth_file_update_needed().
1385 static void
1386 DelRoleMems(const char *rolename, Oid roleid,
1387 List *memberNames, List *memberIds,
1388 bool admin_opt)
1390 Relation pg_authmem_rel;
1391 TupleDesc pg_authmem_dsc;
1392 ListCell *nameitem;
1393 ListCell *iditem;
1395 Assert(list_length(memberNames) == list_length(memberIds));
1397 /* Skip permission check if nothing to do */
1398 if (!memberIds)
1399 return;
1402 * Check permissions: must have createrole or admin option on the role to
1403 * be changed. To mess with a superuser role, you gotta be superuser.
1405 if (superuser_arg(roleid))
1407 if (!superuser())
1408 ereport(ERROR,
1409 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1410 errmsg("must be superuser to alter superusers")));
1412 else
1414 if (!have_createrole_privilege() &&
1415 !is_admin_of_role(GetUserId(), roleid))
1416 ereport(ERROR,
1417 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1418 errmsg("must have admin option on role \"%s\"",
1419 rolename)));
1422 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1423 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1425 forboth(nameitem, memberNames, iditem, memberIds)
1427 const char *membername = strVal(lfirst(nameitem));
1428 Oid memberid = lfirst_oid(iditem);
1429 HeapTuple authmem_tuple;
1432 * Find entry for this role/member
1434 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1435 ObjectIdGetDatum(roleid),
1436 ObjectIdGetDatum(memberid),
1437 0, 0);
1438 if (!HeapTupleIsValid(authmem_tuple))
1440 ereport(WARNING,
1441 (errmsg("role \"%s\" is not a member of role \"%s\"",
1442 membername, rolename)));
1443 continue;
1446 if (!admin_opt)
1448 /* Remove the entry altogether */
1449 simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
1451 else
1453 /* Just turn off the admin option */
1454 HeapTuple tuple;
1455 Datum new_record[Natts_pg_auth_members];
1456 bool new_record_nulls[Natts_pg_auth_members];
1457 bool new_record_repl[Natts_pg_auth_members];
1459 /* Build a tuple to update with */
1460 MemSet(new_record, 0, sizeof(new_record));
1461 MemSet(new_record_nulls, false, sizeof(new_record_nulls));
1462 MemSet(new_record_repl, false, sizeof(new_record_repl));
1464 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1465 new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
1467 tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1468 new_record,
1469 new_record_nulls, new_record_repl);
1470 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1471 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1474 ReleaseSysCache(authmem_tuple);
1476 /* CCI after each change, in case there are duplicates in list */
1477 CommandCounterIncrement();
1481 * Close pg_authmem, but keep lock till commit (this is important to
1482 * prevent any risk of deadlock failure while updating flat file)
1484 heap_close(pg_authmem_rel, NoLock);