1 /*-------------------------------------------------------------------------
4 * Commands for manipulating users and groups.
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.77 2001/06/14 01:09:22 tgl Exp $
11 *-------------------------------------------------------------------------
15 #include <sys/types.h>
20 #include "access/heapam.h"
21 #include "catalog/catname.h"
22 #include "catalog/pg_database.h"
23 #include "catalog/pg_shadow.h"
24 #include "catalog/pg_group.h"
25 #include "catalog/indexing.h"
26 #include "commands/user.h"
27 #include "libpq/crypt.h"
28 #include "miscadmin.h"
29 #include "utils/array.h"
30 #include "utils/builtins.h"
31 #include "utils/fmgroids.h"
32 #include "utils/lsyscache.h"
33 #include "utils/syscache.h"
36 static void CheckPgUserAclNotNull(void);
39 /*---------------------------------------------------------------------
40 * write_password_file / update_pg_pwd
42 * copy the modified contents of pg_shadow to a file used by the postmaster
43 * for user authentication. The file is stored as $PGDATA/pg_pwd.
45 * This function set is both a trigger function for direct updates to pg_shadow
46 * as well as being called directly from create/alter/drop user.
47 *---------------------------------------------------------------------
50 write_password_file(Relation rel)
60 TupleDesc dsc = RelationGetDescr(rel);
63 * Create a temporary filename to be renamed later. This prevents the
64 * backend from clobbering the pg_pwd file while the postmaster might
67 filename = crypt_getpwdfilename();
68 bufsize = strlen(filename) + 12;
69 tempname = (char *) palloc(bufsize);
71 snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
72 oumask = umask((mode_t) 077);
73 fp = AllocateFile(tempname, "w");
76 elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
79 scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
80 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
89 datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
91 continue; /* don't allow empty users */
92 datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
95 * It could be argued that people having a null password shouldn't
96 * be allowed to connect, because they need to have a password set
97 * up first. If you think assuming an empty password in that case
98 * is better, erase the following line.
102 datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
105 * These fake entries are not really necessary. To remove them,
106 * the parser in backend/libpq/crypt.c would need to be adjusted.
107 * Initdb might also need adjustments.
111 CRYPT_PWD_FILE_SEPSTR
113 CRYPT_PWD_FILE_SEPSTR
115 CRYPT_PWD_FILE_SEPSTR
117 CRYPT_PWD_FILE_SEPSTR
119 CRYPT_PWD_FILE_SEPSTR
121 CRYPT_PWD_FILE_SEPSTR
123 CRYPT_PWD_FILE_SEPSTR
125 DatumGetCString(DirectFunctionCall1(nameout, datum_n)),
127 DatumGetCString(DirectFunctionCall1(textout, datum_p)),
129 DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v))
136 elog(ERROR, "%s: %m", tempname);
140 * And rename the temp file to its final name, deleting the old
143 if (rename(tempname, filename))
144 elog(ERROR, "rename %s to %s: %m", tempname, filename);
146 pfree((void *) tempname);
147 pfree((void *) filename);
150 * Create a flag file the postmaster will detect the next time it
151 * tries to authenticate a user. The postmaster will know to reload
152 * the pg_pwd file contents. Note: we used to elog(ERROR) if the file
153 * creation failed, but it's a little silly to abort the transaction
154 * at this point, so let's just make it a NOTICE.
156 filename = crypt_getpwdreloadfilename();
157 flagfd = BasicOpenFile(filename, O_WRONLY | O_CREAT, 0600);
159 elog(NOTICE, "write_password_file: unable to write %s: %m", filename);
162 pfree((void *) filename);
167 /* This is the wrapper for triggers. */
169 update_pg_pwd(PG_FUNCTION_ARGS)
172 * ExclusiveLock ensures no one modifies pg_shadow while we read it,
173 * and that only one backend rewrites the flat file at a time. It's
174 * OK to allow normal reads of pg_shadow in parallel, however.
176 Relation rel = heap_openr(ShadowRelationName, ExclusiveLock);
178 write_password_file(rel);
179 /* OK to release lock, since we did not modify the relation */
180 heap_close(rel, ExclusiveLock);
181 return PointerGetDatum(NULL);
190 CreateUser(CreateUserStmt *stmt)
192 Relation pg_shadow_rel;
193 TupleDesc pg_shadow_dsc;
196 Datum new_record[Natts_pg_shadow];
197 char new_record_nulls[Natts_pg_shadow];
198 bool user_exists = false,
199 sysid_exists = false,
204 havesysid = stmt->sysid > 0;
206 /* Check some permissions first */
208 CheckPgUserAclNotNull();
211 elog(ERROR, "CREATE USER: permission denied");
214 * Scan the pg_shadow relation to be certain the user or id doesn't
215 * already exist. Note we secure exclusive lock, because we also need
216 * to be sure of what the next usesysid should be, and we need to
217 * protect our update of the flat password file.
219 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
220 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
222 scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
223 while (!user_exists && !sysid_exists &&
224 HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
229 datum = heap_getattr(tuple, Anum_pg_shadow_usename,
230 pg_shadow_dsc, &null);
232 user_exists = (strcmp((char *) DatumGetName(datum), stmt->user) == 0);
234 datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
235 pg_shadow_dsc, &null);
237 if (havesysid) /* customized id wanted */
238 sysid_exists = (DatumGetInt32(datum) == stmt->sysid);
242 if (DatumGetInt32(datum) > max_id)
243 max_id = DatumGetInt32(datum);
249 elog(ERROR, "CREATE USER: user name \"%s\" already exists",
252 elog(ERROR, "CREATE USER: sysid %d is already assigned",
256 * Build a tuple to insert
258 new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
259 CStringGetDatum(stmt->user));
260 new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(havesysid ? stmt->sysid : max_id + 1);
262 AssertState(BoolIsValid(stmt->createdb));
263 new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(stmt->createdb);
264 new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
265 AssertState(BoolIsValid(stmt->createuser));
266 new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(stmt->createuser);
267 /* superuser gets catupd right by default */
268 new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(stmt->createuser);
271 new_record[Anum_pg_shadow_passwd - 1] =
272 DirectFunctionCall1(textin, CStringGetDatum(stmt->password));
273 if (stmt->validUntil)
274 new_record[Anum_pg_shadow_valuntil - 1] =
275 DirectFunctionCall1(nabstimein, CStringGetDatum(stmt->validUntil));
277 new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
278 new_record_nulls[Anum_pg_shadow_usesysid - 1] = ' ';
280 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
281 new_record_nulls[Anum_pg_shadow_usetrace - 1] = ' ';
282 new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
283 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
285 new_record_nulls[Anum_pg_shadow_passwd - 1] = stmt->password ? ' ' : 'n';
286 new_record_nulls[Anum_pg_shadow_valuntil - 1] = stmt->validUntil ? ' ' : 'n';
288 tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
291 * Insert new record in the pg_shadow table
293 heap_insert(pg_shadow_rel, tuple);
298 if (RelationGetForm(pg_shadow_rel)->relhasindex)
300 Relation idescs[Num_pg_shadow_indices];
302 CatalogOpenIndices(Num_pg_shadow_indices,
303 Name_pg_shadow_indices, idescs);
304 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
306 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
310 * Add the user to the groups specified. We'll just call the below
311 * AlterGroup for this.
313 foreach(item, stmt->groupElts)
317 ags.name = strVal(lfirst(item)); /* the group name to add
320 ags.listUsers = makeList1(makeInteger(havesysid ?
321 stmt->sysid : max_id + 1));
322 AlterGroup(&ags, "CREATE USER");
326 * Write the updated pg_shadow data to the flat password file.
328 write_password_file(pg_shadow_rel);
331 * Now we can clean up; but keep lock until commit.
333 heap_close(pg_shadow_rel, NoLock);
342 AlterUser(AlterUserStmt *stmt)
344 Datum new_record[Natts_pg_shadow];
345 char new_record_nulls[Natts_pg_shadow];
346 Relation pg_shadow_rel;
347 TupleDesc pg_shadow_dsc;
353 CheckPgUserAclNotNull();
355 /* must be superuser or just want to change your own password */
357 !(stmt->createdb == 0 &&
358 stmt->createuser == 0 &&
361 strcmp(GetUserName(GetUserId()), stmt->user) == 0))
362 elog(ERROR, "ALTER USER: permission denied");
364 /* changes to the flat password file cannot be rolled back */
365 if (IsTransactionBlock() && stmt->password)
366 elog(NOTICE, "ALTER USER: password changes cannot be rolled back");
369 * Scan the pg_shadow relation to be certain the user exists. Note we
370 * secure exclusive lock to protect our update of the flat password
373 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
374 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
376 tuple = SearchSysCache(SHADOWNAME,
377 PointerGetDatum(stmt->user),
379 if (!HeapTupleIsValid(tuple))
380 elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
383 * Build a tuple to update, perusing the information just obtained
385 new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
386 CStringGetDatum(stmt->user));
387 new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
389 /* sysid - leave as is */
390 new_record[Anum_pg_shadow_usesysid - 1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
391 new_record_nulls[Anum_pg_shadow_usesysid - 1] = null ? 'n' : ' ';
394 if (stmt->createdb == 0)
397 new_record[Anum_pg_shadow_usecreatedb - 1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
398 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = null ? 'n' : ' ';
402 new_record[Anum_pg_shadow_usecreatedb - 1] = (Datum) (stmt->createdb > 0 ? true : false);
403 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
406 /* trace - leave as is */
407 new_record[Anum_pg_shadow_usetrace - 1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
408 new_record_nulls[Anum_pg_shadow_usetrace - 1] = null ? 'n' : ' ';
410 /* createuser (superuser) */
411 if (stmt->createuser == 0)
414 new_record[Anum_pg_shadow_usesuper - 1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
415 new_record_nulls[Anum_pg_shadow_usesuper - 1] = null ? 'n' : ' ';
419 new_record[Anum_pg_shadow_usesuper - 1] = (Datum) (stmt->createuser > 0 ? true : false);
420 new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
423 /* catupd - set to false if someone's superuser priv is being yanked */
424 if (stmt->createuser < 0)
426 new_record[Anum_pg_shadow_usecatupd - 1] = (Datum) (false);
427 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
432 new_record[Anum_pg_shadow_usecatupd - 1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
433 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = null ? 'n' : ' ';
439 new_record[Anum_pg_shadow_passwd - 1] =
440 DirectFunctionCall1(textin, CStringGetDatum(stmt->password));
441 new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
446 new_record[Anum_pg_shadow_passwd - 1] =
447 heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
448 new_record_nulls[Anum_pg_shadow_passwd - 1] = null ? 'n' : ' ';
452 if (stmt->validUntil)
454 new_record[Anum_pg_shadow_valuntil - 1] =
455 DirectFunctionCall1(nabstimein, CStringGetDatum(stmt->validUntil));
456 new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
461 new_record[Anum_pg_shadow_valuntil - 1] =
462 heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
463 new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
466 new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
467 simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
470 if (RelationGetForm(pg_shadow_rel)->relhasindex)
472 Relation idescs[Num_pg_shadow_indices];
474 CatalogOpenIndices(Num_pg_shadow_indices,
475 Name_pg_shadow_indices, idescs);
476 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
478 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
481 ReleaseSysCache(tuple);
482 heap_freetuple(new_tuple);
485 * Write the updated pg_shadow data to the flat password file.
487 write_password_file(pg_shadow_rel);
490 * Now we can clean up.
492 heap_close(pg_shadow_rel, NoLock);
501 DropUser(DropUserStmt *stmt)
503 Relation pg_shadow_rel;
504 TupleDesc pg_shadow_dsc;
508 elog(ERROR, "DROP USER: permission denied");
510 if (IsTransactionBlock())
511 elog(NOTICE, "DROP USER cannot be rolled back completely");
514 * Scan the pg_shadow relation to find the usesysid of the user to be
515 * deleted. Note we secure exclusive lock, because we need to protect
516 * our update of the flat password file.
518 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
519 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
521 foreach(item, stmt->users)
532 const char *user = strVal(lfirst(item));
534 tuple = SearchSysCache(SHADOWNAME,
535 PointerGetDatum(user),
537 if (!HeapTupleIsValid(tuple))
538 elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
539 (length(stmt->users) > 1) ? " (no users removed)" : "");
541 usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
544 * Check if user still owns a database. If so, error out.
546 * (It used to be that this function would drop the database
547 * automatically. This is not only very dangerous for people that
548 * don't read the manual, it doesn't seem to be the behaviour one
549 * would expect either.) -- petere 2000/01/14)
551 pg_rel = heap_openr(DatabaseRelationName, AccessShareLock);
552 pg_dsc = RelationGetDescr(pg_rel);
554 ScanKeyEntryInitialize(&scankey, 0x0,
555 Anum_pg_database_datdba, F_INT4EQ,
556 Int32GetDatum(usesysid));
558 scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
560 if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
564 datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
567 dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
568 elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
570 (length(stmt->users) > 1) ? " (no users removed)" : "");
574 heap_close(pg_rel, AccessShareLock);
577 * Somehow we'd have to check for tables, views, etc. owned by the
578 * user as well, but those could be spread out over all sorts of
579 * databases which we don't have access to (easily).
583 * Remove the user from the pg_shadow table
585 simple_heap_delete(pg_shadow_rel, &tuple->t_self);
587 ReleaseSysCache(tuple);
590 * Remove user from groups
592 * try calling alter group drop user for every group
594 pg_rel = heap_openr(GroupRelationName, ExclusiveLock);
595 pg_dsc = RelationGetDescr(pg_rel);
596 scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
597 while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
601 /* the group name from which to try to drop the user: */
602 datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
603 ags.name = DatumGetCString(DirectFunctionCall1(nameout, datum));
605 ags.listUsers = makeList1(makeInteger(usesysid));
606 AlterGroup(&ags, "DROP USER");
609 heap_close(pg_rel, ExclusiveLock);
612 * Advance command counter so that later iterations of this loop
613 * will see the changes already made. This is essential if, for
614 * example, we are trying to drop two users who are members of the
615 * same group --- the AlterGroup for the second user had better
616 * see the tuple updated from the first one.
618 CommandCounterIncrement();
622 * Write the updated pg_shadow data to the flat password file.
624 write_password_file(pg_shadow_rel);
627 * Now we can clean up.
629 heap_close(pg_shadow_rel, NoLock);
635 * CheckPgUserAclNotNull
637 * check to see if there is an ACL on pg_shadow
640 CheckPgUserAclNotNull()
644 htup = SearchSysCache(RELNAME,
645 PointerGetDatum(ShadowRelationName),
647 if (!HeapTupleIsValid(htup))
648 elog(ERROR, "CheckPgUserAclNotNull: \"%s\" not found",
651 if (heap_attisnull(htup, Anum_pg_class_relacl))
653 "To use passwords, you have to revoke permissions on %s "
654 "so normal users cannot read the passwords. "
655 "Try 'REVOKE ALL ON \"%s\" FROM PUBLIC'.",
656 ShadowRelationName, ShadowRelationName);
658 ReleaseSysCache(htup);
667 CreateGroup(CreateGroupStmt *stmt)
669 Relation pg_group_rel;
672 TupleDesc pg_group_dsc;
673 bool group_exists = false,
674 sysid_exists = false;
676 Datum new_record[Natts_pg_group];
677 char new_record_nulls[Natts_pg_group];
680 ArrayType *userarray;
683 * Make sure the user can do this.
686 elog(ERROR, "CREATE GROUP: permission denied");
688 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
689 pg_group_dsc = RelationGetDescr(pg_group_rel);
691 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
692 while (!group_exists && !sysid_exists &&
693 HeapTupleIsValid(tuple = heap_getnext(scan, false)))
698 datum = heap_getattr(tuple, Anum_pg_group_groname,
699 pg_group_dsc, &null);
701 group_exists = (strcmp((char *) DatumGetName(datum), stmt->name) == 0);
703 datum = heap_getattr(tuple, Anum_pg_group_grosysid,
704 pg_group_dsc, &null);
706 if (stmt->sysid >= 0) /* customized id wanted */
707 sysid_exists = (DatumGetInt32(datum) == stmt->sysid);
711 if (DatumGetInt32(datum) > max_id)
712 max_id = DatumGetInt32(datum);
718 elog(ERROR, "CREATE GROUP: group name \"%s\" already exists",
721 elog(ERROR, "CREATE GROUP: group sysid %d is already assigned",
725 * Translate the given user names to ids
727 foreach(item, stmt->initUsers)
729 const char *groupuser = strVal(lfirst(item));
732 v = makeInteger(get_usesysid(groupuser));
733 if (!member(v, newlist))
734 newlist = lcons(v, newlist);
737 /* build an array to insert */
742 userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
743 userarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
744 userarray->flags = 0;
745 ARR_NDIM(userarray) = 1;/* one dimensional array */
746 ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
747 ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
750 foreach(item, newlist)
751 ((int *) ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
757 * Form a tuple to insert
759 if (stmt->sysid >= 0)
760 max_id = stmt->sysid;
764 new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
765 new_record[Anum_pg_group_grosysid - 1] = (Datum) (max_id);
766 new_record[Anum_pg_group_grolist - 1] = (Datum) userarray;
768 new_record_nulls[Anum_pg_group_groname - 1] = ' ';
769 new_record_nulls[Anum_pg_group_grosysid - 1] = ' ';
770 new_record_nulls[Anum_pg_group_grolist - 1] = userarray ? ' ' : 'n';
772 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
775 * Insert a new record in the pg_group_table
777 heap_insert(pg_group_rel, tuple);
782 if (RelationGetForm(pg_group_rel)->relhasindex)
784 Relation idescs[Num_pg_group_indices];
786 CatalogOpenIndices(Num_pg_group_indices,
787 Name_pg_group_indices, idescs);
788 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
790 CatalogCloseIndices(Num_pg_group_indices, idescs);
793 heap_close(pg_group_rel, NoLock);
802 AlterGroup(AlterGroupStmt *stmt, const char *tag)
804 Relation pg_group_rel;
805 TupleDesc pg_group_dsc;
806 HeapTuple group_tuple;
809 * Make sure the user can do this.
812 elog(ERROR, "%s: permission denied", tag);
814 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
815 pg_group_dsc = RelationGetDescr(pg_group_rel);
818 * Fetch existing tuple for group.
820 group_tuple = SearchSysCache(GRONAME,
821 PointerGetDatum(stmt->name),
823 if (!HeapTupleIsValid(group_tuple))
824 elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
827 * Now decide what to do.
829 AssertState(stmt->action == +1 || stmt->action == -1);
831 if (stmt->action == +1) /* add users, might also be invoked by
834 Datum new_record[Natts_pg_group];
835 char new_record_nulls[Natts_pg_group] = {' ', ' ', ' '};
838 List *newlist = NULL,
842 Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
845 oldarray = (ArrayType *) datum;
846 Assert(null || ARR_NDIM(oldarray) == 1);
847 /* first add the old array to the hitherto empty list */
849 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
857 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
858 sizeof(int), 0, &valueNull));
859 v = makeInteger(arrval);
860 /* filter out duplicates */
861 if (!member(v, newlist))
862 newlist = lcons(v, newlist);
866 * now convert the to be added usernames to sysids and add them to
869 foreach(item, stmt->listUsers)
873 if (strcmp(tag, "ALTER GROUP") == 0)
875 /* Get the uid of the proposed user to add. */
876 v = makeInteger(get_usesysid(strVal(lfirst(item))));
878 else if (strcmp(tag, "CREATE USER") == 0)
881 * in this case we already know the uid and it wouldn't be
882 * in the cache anyway yet
888 elog(ERROR, "AlterGroup: unknown tag %s", tag);
889 v = NULL; /* keep compiler quiet */
892 if (!member(v, newlist))
893 newlist = lcons(v, newlist);
896 * we silently assume here that this error will only come
897 * up in a ALTER GROUP statement
899 elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"",
900 tag, strVal(lfirst(item)), stmt->name);
903 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
904 newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
906 ARR_NDIM(newarray) = 1; /* one dimensional array */
907 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
908 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
911 foreach(item, newlist)
912 ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
915 * Form a tuple with the new array and write it back.
917 new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
918 new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
919 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
921 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
922 simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
925 if (RelationGetForm(pg_group_rel)->relhasindex)
927 Relation idescs[Num_pg_group_indices];
929 CatalogOpenIndices(Num_pg_group_indices,
930 Name_pg_group_indices, idescs);
931 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
933 CatalogCloseIndices(Num_pg_group_indices, idescs);
935 } /* endif alter group add user */
937 else if (stmt->action == -1)/* drop users from group */
941 bool is_dropuser = strcmp(tag, "DROP USER") == 0;
943 datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
947 elog(NOTICE, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
952 Datum new_record[Natts_pg_group];
953 char new_record_nulls[Natts_pg_group] = {' ', ' ', ' '};
956 List *newlist = NULL,
960 oldarray = (ArrayType *) datum;
961 Assert(ARR_NDIM(oldarray) == 1);
962 /* first add the old array to the hitherto empty list */
963 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
971 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
972 sizeof(int), 0, &valueNull));
973 v = makeInteger(arrval);
974 /* filter out duplicates */
975 if (!member(v, newlist))
976 newlist = lcons(v, newlist);
980 * now convert the to be dropped usernames to sysids and
981 * remove them from the list
983 foreach(item, stmt->listUsers)
989 /* Get the uid of the proposed user to drop. */
990 v = makeInteger(get_usesysid(strVal(lfirst(item))));
994 /* for dropuser we already know the uid */
997 if (member(v, newlist))
998 newlist = LispRemove(v, newlist);
999 else if (!is_dropuser)
1000 elog(NOTICE, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
1003 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1004 newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1005 newarray->flags = 0;
1006 ARR_NDIM(newarray) = 1; /* one dimensional array */
1007 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
1008 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
1009 /* fill the array */
1011 foreach(item, newlist)
1012 ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
1015 * Insert the new tuple with the updated user list
1017 new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
1018 new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
1019 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
1021 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1022 simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
1024 /* Update indexes */
1025 if (RelationGetForm(pg_group_rel)->relhasindex)
1027 Relation idescs[Num_pg_group_indices];
1029 CatalogOpenIndices(Num_pg_group_indices,
1030 Name_pg_group_indices, idescs);
1031 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1033 CatalogCloseIndices(Num_pg_group_indices, idescs);
1036 } /* endif group not null */
1037 } /* endif alter group drop user */
1039 ReleaseSysCache(group_tuple);
1041 heap_close(pg_group_rel, NoLock);
1050 DropGroup(DropGroupStmt *stmt)
1052 Relation pg_group_rel;
1055 TupleDesc pg_group_dsc;
1056 bool gro_exists = false;
1059 * Make sure the user can do this.
1062 elog(ERROR, "DROP GROUP: permission denied");
1065 * Scan the pg_group table and delete all matching groups.
1067 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1068 pg_group_dsc = RelationGetDescr(pg_group_rel);
1069 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1071 while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1076 datum = heap_getattr(tuple, Anum_pg_group_groname,
1077 pg_group_dsc, &null);
1078 if (!null && strcmp((char *) DatumGetName(datum), stmt->name) == 0)
1081 simple_heap_delete(pg_group_rel, &tuple->t_self);
1091 elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
1093 heap_close(pg_group_rel, NoLock);