1 /*-------------------------------------------------------------------------
4 * use pg_exec_query to create a new user in the catalog
6 * Copyright (c) 1994, Regents of the University of California
8 * $Id: user.c,v 1.45 1999/12/16 17:24:13 momjian Exp $
10 *-------------------------------------------------------------------------
12 #include <sys/types.h>
19 #include "access/heapam.h"
20 #include "catalog/catname.h"
21 #include "catalog/pg_database.h"
22 #include "catalog/pg_shadow.h"
23 #include "catalog/pg_group.h"
24 #include "catalog/indexing.h"
25 #include "commands/copy.h"
26 #include "commands/user.h"
27 #include "libpq/crypt.h"
28 #include "miscadmin.h"
29 #include "nodes/pg_list.h"
30 #include "tcop/tcopprot.h"
31 #include "utils/acl.h"
32 #include "utils/array.h"
33 #include "utils/syscache.h"
35 static void CheckPgUserAclNotNull(void);
37 #define SQL_LENGTH 512
39 /*---------------------------------------------------------------------
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 * NB: caller is responsible for ensuring that only one backend can
46 * execute this routine at a time. Acquiring AccessExclusiveLock on
47 * pg_shadow is the standard way to do that.
48 *---------------------------------------------------------------------
51 /* This is the old name. Now uses a lower case name to be able to call this
53 #define UpdatePgPwdFile() update_pg_pwd()
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);
70 snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
73 * Copy the contents of pg_shadow to the pg_pwd ASCII file using the
74 * SEPCHAR character as the delimiter between fields. Make sure the
75 * file is created with mode 600 (umask 077).
77 DoCopy(ShadowRelationName, /* relname */
82 tempname, /* filename */
83 CRYPT_PWD_FILE_SEPSTR, /* delim */
85 0077); /* fileumask */
87 * And rename the temp file to its final name, deleting the old pg_pwd.
89 rename(tempname, filename);
92 * Create a flag file the postmaster will detect the next time it
93 * tries to authenticate a user. The postmaster will know to reload
94 * the pg_pwd file contents.
96 filename = crypt_getpwdreloadfilename();
97 creat(filename, S_IRUSR | S_IWUSR);
99 pfree((void *) tempname);
102 /*---------------------------------------------------------------------
105 * Add the user to the pg_shadow relation, and if specified make sure the
106 * user is specified in the desired groups of defined in pg_group.
107 *---------------------------------------------------------------------
110 DefineUser(CreateUserStmt *stmt, CommandDest dest)
114 Relation pg_shadow_rel;
115 TupleDesc pg_shadow_dsc;
118 bool user_exists = false,
119 sysid_exists = false,
127 havesysid = stmt->sysid >= 0;
128 havepassword = stmt->password && stmt->password[0];
129 havevaluntil = stmt->validUntil && stmt->validUntil[0];
132 CheckPgUserAclNotNull();
133 if (!(inblock = IsTransactionBlock()))
134 BeginTransactionBlock();
137 * Make sure the user attempting to create a user can insert into the
138 * pg_shadow relation.
140 pg_shadow = GetPgUserName();
141 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
143 UserAbortTransactionBlock();
144 elog(ERROR, "DefineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
145 pg_shadow, ShadowRelationName);
150 * Scan the pg_shadow relation to be certain the user or id doesn't already
151 * exist. Note we secure exclusive lock, because we also need to be
152 * sure of what the next usesysid should be, and we need to protect
153 * our update of the flat password file.
155 pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
156 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
158 scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
159 while (!user_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
164 datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
165 user_exists = datum && !null && (strcmp((char *) datum, stmt->user) == 0);
167 datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
168 if (havesysid) /* customized id wanted */
169 sysid_exists = datum && !null && ((int)datum == stmt->sysid);
170 else /* pick 1 + max */
172 if ((int) datum > max_id)
173 max_id = (int) datum;
178 if (user_exists || sysid_exists)
180 heap_close(pg_shadow_rel, AccessExclusiveLock);
181 UserAbortTransactionBlock();
183 elog(ERROR, "DefineUser: user name \"%s\" already exists", stmt->user);
185 elog(ERROR, "DefineUser: sysid %d is already assigned", stmt->sysid);
190 * Build the insert statement to be executed.
192 * XXX Ugly as this code is, it still fails to cope with ' or \ in any of
193 * the provided strings.
195 * XXX This routine would be *lots* better if it inserted the new
196 * tuple with formtuple/heap_insert. For one thing, all of the
197 * transaction-block gamesmanship could be eliminated, because
198 * it's only there to make the world safe for a recursive call
199 * to pg_exec_query_dest().
201 snprintf(sql, SQL_LENGTH,
202 "insert into %s (usename,usesysid,usecreatedb,usetrace,"
203 "usesuper,usecatupd,passwd,valuntil) "
204 "values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
207 havesysid ? stmt->sysid : max_id + 1,
208 (stmt->createdb && *stmt->createdb) ? 't' : 'f',
209 (stmt->createuser && *stmt->createuser) ? 't' : 'f',
210 ((stmt->createdb && *stmt->createdb) ||
211 (stmt->createuser && *stmt->createuser)) ? 't' : 'f',
212 havepassword ? "'" : "",
213 havepassword ? stmt->password : "NULL",
214 havepassword ? "'" : "",
215 havevaluntil ? "'" : "",
216 havevaluntil ? stmt->validUntil : "NULL",
217 havevaluntil ? "'" : "");
220 * XXX If insert fails, say because a bogus valuntil date is given,
221 * need to catch the resulting error and undo our transaction.
223 pg_exec_query_dest(sql, dest, false);
226 * Add the user to the groups specified. We'll just call the below
227 * AlterGroup for this.
229 foreach(item, stmt->groupElts)
233 ags.name = strVal(lfirst(item));
235 ags.listUsers = lcons((void*)makeString(stmt->user), NIL);
236 AlterGroup(&ags, dest);
240 * Write the updated pg_shadow data to the flat password file.
241 * Because we are still holding AccessExclusiveLock on pg_shadow,
242 * we can be sure no other backend will try to write the flat
243 * file at the same time.
248 * Now we can clean up.
250 heap_close(pg_shadow_rel, AccessExclusiveLock);
252 if (IsTransactionBlock() && !inblock)
253 EndTransactionBlock();
258 AlterUser(AlterUserStmt *stmt, CommandDest dest)
263 Relation pg_shadow_rel;
264 TupleDesc pg_shadow_dsc;
270 CheckPgUserAclNotNull();
271 if (!(inblock = IsTransactionBlock()))
272 BeginTransactionBlock();
275 * Make sure the user attempting to create a user can insert into the
276 * pg_shadow relation.
278 pg_shadow = GetPgUserName();
279 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
281 UserAbortTransactionBlock();
282 elog(ERROR, "AlterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
283 pg_shadow, ShadowRelationName);
288 * Scan the pg_shadow relation to be certain the user exists.
289 * Note we secure exclusive lock to protect our update of the
290 * flat password file.
292 pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
293 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
295 tuple = SearchSysCacheTuple(SHADOWNAME,
296 PointerGetDatum(stmt->user),
298 if (!HeapTupleIsValid(tuple))
300 heap_close(pg_shadow_rel, AccessExclusiveLock);
301 UserAbortTransactionBlock();
302 elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
305 /* look for duplicate sysid */
306 tuple = SearchSysCacheTuple(SHADOWSYSID,
307 Int32GetDatum(stmt->sysid),
309 if (HeapTupleIsValid(tuple))
314 datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
315 if (datum && !null && strcmp((char *) datum, stmt->user) != 0)
317 heap_close(pg_shadow_rel, AccessExclusiveLock);
318 UserAbortTransactionBlock();
319 elog(ERROR, "AlterUser: sysid %d is already assigned", stmt->sysid);
325 * Create the update statement to modify the user.
327 * XXX see diatribe in preceding routine. This code is just as bogus.
329 snprintf(sql, SQL_LENGTH, "update %s set ", ShadowRelationName);
333 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
334 "passwd = '%s'", stmt->password);
342 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
343 "usesysid = %d", stmt->sysid);
351 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
353 *stmt->createdb ? 't' : 'f');
357 if (stmt->createuser)
361 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
363 *stmt->createuser ? 't' : 'f');
367 if (stmt->validUntil)
371 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
376 snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
377 " where usename = '%s'",
380 pg_exec_query_dest(sql, dest, false);
383 * Add stuff here for groups?
386 elog(NOTICE, "IN GROUP is not implemented for ALTER USER.");
389 * Write the updated pg_shadow data to the flat password file.
390 * Because we are still holding AccessExclusiveLock on pg_shadow,
391 * we can be sure no other backend will try to write the flat
392 * file at the same time.
397 * Now we can clean up.
399 heap_close(pg_shadow_rel, AccessExclusiveLock);
401 if (IsTransactionBlock() && !inblock)
402 EndTransactionBlock();
407 RemoveUser(char *user, CommandDest dest)
410 Relation pg_shadow_rel,
416 char sql[SQL_LENGTH];
423 if (!(inblock = IsTransactionBlock()))
424 BeginTransactionBlock();
427 * Make sure the user attempting to create a user can delete from the
428 * pg_shadow relation.
430 pg_shadow = GetPgUserName();
431 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
433 UserAbortTransactionBlock();
434 elog(ERROR, "RemoveUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
435 pg_shadow, ShadowRelationName);
439 * Scan the pg_shadow relation to find the usesysid of the user to be
440 * deleted. Note we secure exclusive lock, because we need to protect
441 * our update of the flat password file.
443 pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
444 pg_dsc = RelationGetDescr(pg_shadow_rel);
446 tuple = SearchSysCacheTuple(SHADOWNAME,
447 PointerGetDatum(user),
449 if (!HeapTupleIsValid(tuple))
451 heap_close(pg_shadow_rel, AccessExclusiveLock);
452 UserAbortTransactionBlock();
453 elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
456 usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
459 * Perform a scan of the pg_database relation to find the databases
460 * owned by usesysid. Then drop them.
462 pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
463 pg_dsc = RelationGetDescr(pg_rel);
465 scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
466 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
468 datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
470 if ((int) datum == usesysid)
472 datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
473 if (memcmp((void *) datum, "template1", 9) != 0)
476 (char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1));
477 dbase[ndbase] = (char *) palloc(NAMEDATALEN + 1);
478 memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
479 dbase[ndbase++][NAMEDATALEN] = '\0';
484 heap_close(pg_rel, AccessExclusiveLock);
488 elog(NOTICE, "Dropping database %s", dbase[ndbase]);
489 snprintf(sql, SQL_LENGTH, "DROP DATABASE %s", dbase[ndbase]);
490 pfree((void *) dbase[ndbase]);
491 pg_exec_query_dest(sql, dest, false);
494 pfree((void *) dbase);
497 * Since pg_shadow is global over all databases, one of two things
498 * must be done to insure complete consistency. First, pg_shadow
499 * could be made non-global. This would elminate the code above for
500 * deleting database and would require the addition of code to delete
501 * tables, views, etc owned by the user.
503 * The second option would be to create a means of deleting tables, view,
504 * etc. owned by the user from other databases. pg_shadow is global
505 * and so this must be done at some point.
507 * Let us not forget that the user should be removed from the pg_groups
510 * Todd A. Brandys 11/18/1997
515 * Remove the user from the pg_shadow table
517 snprintf(sql, SQL_LENGTH,
518 "delete from %s where usename = '%s'", ShadowRelationName, user);
519 pg_exec_query_dest(sql, dest, false);
522 * Write the updated pg_shadow data to the flat password file.
523 * Because we are still holding AccessExclusiveLock on pg_shadow,
524 * we can be sure no other backend will try to write the flat
525 * file at the same time.
530 * Now we can clean up.
532 heap_close(pg_shadow_rel, AccessExclusiveLock);
534 if (IsTransactionBlock() && !inblock)
535 EndTransactionBlock();
539 * CheckPgUserAclNotNull
541 * check to see if there is an ACL on pg_shadow
544 CheckPgUserAclNotNull()
548 htup = SearchSysCacheTuple(RELNAME,
549 PointerGetDatum(ShadowRelationName),
551 if (!HeapTupleIsValid(htup))
553 elog(ERROR, "IsPgUserAclNull: class \"%s\" not found",
557 if (heap_attisnull(htup, Anum_pg_class_relacl))
559 elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
560 elog(NOTICE, "so normal users can not read the passwords.");
561 elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");
568 /*** GROUP THINGS ***/
571 CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
573 Relation pg_group_rel;
576 TupleDesc pg_group_dsc;
578 bool group_exists = false,
579 sysid_exists = false;
581 Datum new_record[Natts_pg_group];
582 char new_record_nulls[Natts_pg_group];
583 List *item, *newlist=NULL;
584 ArrayType *userarray;
587 if (!(inblock = IsTransactionBlock()))
588 BeginTransactionBlock();
591 * Make sure the user can do this.
593 if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_AP) != ACLCHECK_OK)
595 UserAbortTransactionBlock();
596 elog(ERROR, "CreateGroup: Permission denied.");
599 pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
600 pg_group_dsc = RelationGetDescr(pg_group_rel);
602 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
603 while (!group_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, false)))
608 datum = heap_getattr(tuple, Anum_pg_group_groname, pg_group_dsc, &null);
609 group_exists = datum && !null && (strcmp((char *) datum, stmt->name) == 0);
611 datum = heap_getattr(tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
612 if (stmt->sysid >= 0) /* customized id wanted */
613 sysid_exists = datum && !null && ((int)datum == stmt->sysid);
614 else /* pick 1 + max */
616 if ((int) datum > max_id)
617 max_id = (int) datum;
622 if (group_exists || sysid_exists)
624 heap_close(pg_group_rel, AccessExclusiveLock);
625 UserAbortTransactionBlock();
627 elog(ERROR, "CreateGroup: Group name \"%s\" already exists.", stmt->name);
629 elog(ERROR, "CreateGroup: Group sysid %d is already assigned.", stmt->sysid);
633 * Translate the given user names to ids
636 foreach(item, stmt->initUsers)
638 const char * groupuser = strVal(lfirst(item));
641 tuple = SearchSysCacheTuple(SHADOWNAME,
642 PointerGetDatum(groupuser),
644 if (!HeapTupleIsValid(tuple))
646 heap_close(pg_group_rel, AccessExclusiveLock);
647 UserAbortTransactionBlock();
648 elog(ERROR, "CreateGroup: User \"%s\" does not exist.", groupuser);
651 v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
652 if (!member(v, newlist))
653 newlist = lcons(v, newlist);
656 /* build an array to insert */
661 userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
662 ARR_SIZE(userarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
663 ARR_FLAGS(userarray) = 0x0;
664 ARR_NDIM(userarray) = 1; /* one dimensional array */
665 ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
666 ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
669 foreach(item, newlist)
671 ((int*)ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
678 * Form a tuple to insert
681 max_id = stmt->sysid;
685 new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
686 new_record[Anum_pg_group_grosysid-1] = (Datum)(max_id);
687 new_record[Anum_pg_group_grolist-1] = (Datum)userarray;
689 new_record_nulls[Anum_pg_group_groname-1] = ' ';
690 new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
691 new_record_nulls[Anum_pg_group_grolist-1] = userarray ? ' ' : 'n';
693 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
696 * Insert a new record in the pg_group_table
698 heap_insert(pg_group_rel, tuple);
703 if (RelationGetForm(pg_group_rel)->relhasindex) {
704 Relation idescs[Num_pg_group_indices];
706 CatalogOpenIndices(Num_pg_group_indices,
707 Name_pg_group_indices, idescs);
708 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
710 CatalogCloseIndices(Num_pg_group_indices, idescs);
713 heap_close(pg_group_rel, NoLock);
715 if (IsTransactionBlock() && !inblock)
716 EndTransactionBlock();
722 AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
724 Relation pg_group_rel;
725 TupleDesc pg_group_dsc;
727 HeapTuple group_tuple;
729 if (!(inblock = IsTransactionBlock()))
730 BeginTransactionBlock();
733 * Make sure the user can do this.
735 if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
737 UserAbortTransactionBlock();
738 elog(ERROR, "AlterGroup: Permission denied.");
741 pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
742 pg_group_dsc = RelationGetDescr(pg_group_rel);
745 * Verify that group exists.
746 * If we find a tuple, will take that the rest of the way and make our
747 * modifications on it.
749 if (!HeapTupleIsValid(group_tuple = SearchSysCacheTupleCopy(GRONAME, PointerGetDatum(stmt->name), 0, 0, 0)))
751 heap_close(pg_group_rel, AccessExclusiveLock);
752 UserAbortTransactionBlock();
753 elog(ERROR, "AlterGroup: Group \"%s\" does not exist.", stmt->name);
757 * Now decide what to do.
759 if (stmt->action == 0) /* change sysid */
761 bool sysid_exists = false;
765 Datum new_record[Natts_pg_group];
766 char new_record_nulls[Natts_pg_group];
770 * First check if the id is already assigned.
772 ScanKeyEntryInitialize(&keys[0], 0x0, Anum_pg_group_grosysid, F_INT4EQ,
773 Int32GetDatum(stmt->sysid));
774 ScanKeyEntryInitialize(&keys[1], 0x0, Anum_pg_group_groname, F_NAMENE,
775 PointerGetDatum(stmt->name));
776 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 2, keys);
778 if (HeapTupleIsValid(heap_getnext(scan, false)))
781 heap_close(pg_group_rel, AccessExclusiveLock);
782 UserAbortTransactionBlock();
783 elog(ERROR, "AlterGroup: Group sysid %d is already assigned.", stmt->sysid);
788 * Insert the new tuple with the updated sysid
790 new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
791 new_record[Anum_pg_group_grosysid-1] = (Datum)(stmt->sysid);
792 new_record[Anum_pg_group_grolist-1] = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
793 new_record_nulls[Anum_pg_group_groname-1] = ' ';
794 new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
795 new_record_nulls[Anum_pg_group_grolist-1] = null ? 'n' : ' ';
797 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
798 heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
801 if (RelationGetForm(pg_group_rel)->relhasindex) {
802 Relation idescs[Num_pg_group_indices];
804 CatalogOpenIndices(Num_pg_group_indices,
805 Name_pg_group_indices, idescs);
806 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
808 CatalogCloseIndices(Num_pg_group_indices, idescs);
815 else if (stmt->action > 0)
817 Datum new_record[Natts_pg_group];
818 char new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
819 ArrayType *newarray, *oldarray;
820 List * newlist = NULL, *item;
823 Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
826 oldarray = (ArrayType*)datum;
827 Assert(null || ARR_NDIM(oldarray) == 1);
828 /* first add the old array to the hitherto empty list */
830 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
836 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true/*by value*/,
837 sizeof(int), 0, &valueNull));
838 v = makeInteger(arrval);
839 /* filter out duplicates */
840 if (!member(v, newlist))
841 newlist = lcons(v, newlist);
845 * now convert the to be added usernames to sysids and add them
848 foreach(item, stmt->listUsers)
851 /* Get the uid of the proposed user to add. */
852 tuple = SearchSysCacheTuple(SHADOWNAME,
853 PointerGetDatum(strVal(lfirst(item))),
855 if (!HeapTupleIsValid(tuple))
857 heap_close(pg_group_rel, AccessExclusiveLock);
858 UserAbortTransactionBlock();
859 elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
862 v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
863 if (!member(v, newlist))
864 newlist = lcons(v, newlist);
866 elog(NOTICE, "AlterGroup: User \"%s\" is already in group \"%s\".", strVal(lfirst(item)), stmt->name);
869 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
870 ARR_SIZE(newarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
871 ARR_FLAGS(newarray) = 0x0;
872 ARR_NDIM(newarray) = 1; /* one dimensional array */
873 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
874 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
877 foreach(item, newlist)
879 ((int*)ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
883 * Form a tuple with the new array and write it back.
885 new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
886 new_record[Anum_pg_group_grosysid-1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
887 new_record[Anum_pg_group_grolist-1] = PointerGetDatum(newarray);
889 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
890 heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
893 if (RelationGetForm(pg_group_rel)->relhasindex) {
894 Relation idescs[Num_pg_group_indices];
896 CatalogOpenIndices(Num_pg_group_indices,
897 Name_pg_group_indices, idescs);
898 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
900 CatalogCloseIndices(Num_pg_group_indices, idescs);
902 } /* endif alter group add user */
905 * drop users from group
907 else if (stmt->action < 0)
912 datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
914 elog(NOTICE, "AlterGroup: Group \"%s\"'s membership is NULL.", stmt->name);
918 Datum new_record[Natts_pg_group];
919 char new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
920 ArrayType *oldarray, *newarray;
921 List * newlist = NULL, *item;
924 oldarray = (ArrayType*)datum;
925 Assert(ARR_NDIM(oldarray) == 1);
926 /* first add the old array to the hitherto empty list */
927 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
933 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true/*by value*/,
934 sizeof(int), 0, &valueNull));
935 v = makeInteger(arrval);
936 /* filter out duplicates */
937 if (!member(v, newlist))
938 newlist = lcons(v, newlist);
942 * now convert the to be dropped usernames to sysids and remove
945 foreach(item, stmt->listUsers)
948 /* Get the uid of the proposed user to drop. */
949 tuple = SearchSysCacheTuple(SHADOWNAME,
950 PointerGetDatum(strVal(lfirst(item))),
952 if (!HeapTupleIsValid(tuple))
954 heap_close(pg_group_rel, AccessExclusiveLock);
955 UserAbortTransactionBlock();
956 elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
959 v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
960 if (member(v, newlist))
961 newlist = LispRemove(v, newlist);
963 elog(NOTICE, "AlterGroup: User \"%s\" is not in group \"%s\".", strVal(lfirst(item)), stmt->name);
966 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
967 ARR_SIZE(newarray) = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
968 ARR_FLAGS(newarray) = 0x0;
969 ARR_NDIM(newarray) = 1; /* one dimensional array */
970 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
971 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
974 foreach(item, newlist)
976 ((int*)ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
980 * Insert the new tuple with the updated user list
982 new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
983 new_record[Anum_pg_group_grosysid-1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
984 new_record[Anum_pg_group_grolist-1] = PointerGetDatum(newarray);
986 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
987 heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
990 if (RelationGetForm(pg_group_rel)->relhasindex) {
991 Relation idescs[Num_pg_group_indices];
993 CatalogOpenIndices(Num_pg_group_indices,
994 Name_pg_group_indices, idescs);
995 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
997 CatalogCloseIndices(Num_pg_group_indices, idescs);
1000 } /* endif group not null */
1001 } /* endif alter group drop user */
1003 heap_close(pg_group_rel, NoLock);
1007 if (IsTransactionBlock() && !inblock)
1008 EndTransactionBlock();
1014 DropGroup(DropGroupStmt *stmt, CommandDest dest)
1016 Relation pg_group_rel;
1019 TupleDesc pg_group_dsc;
1021 bool gro_exists = false;
1023 if (!(inblock = IsTransactionBlock()))
1024 BeginTransactionBlock();
1027 * Make sure the user can do this.
1029 if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
1031 UserAbortTransactionBlock();
1032 elog(ERROR, "DropGroup: Permission denied.");
1036 * Scan the pg_group table and delete all matching users.
1038 pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
1039 pg_group_dsc = RelationGetDescr(pg_group_rel);
1040 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1042 while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1047 datum = heap_getattr(tuple, Anum_pg_group_groname, pg_group_dsc, &null);
1048 if (datum && !null && strcmp((char*)datum, stmt->name)==0)
1051 heap_delete(pg_group_rel, &tuple->t_self, NULL);
1063 heap_close(pg_group_rel, AccessExclusiveLock);
1064 UserAbortTransactionBlock();
1065 elog(ERROR, "DropGroup: Group \"%s\" does not exist.", stmt->name);
1068 heap_close(pg_group_rel, NoLock);
1070 if (IsTransactionBlock() && !inblock)
1071 EndTransactionBlock();