]> granicus.if.org Git - postgresql/blob - src/backend/commands/user.c
Commit to match discussed elog() changes. Only update is that LOG is
[postgresql] / src / backend / commands / user.c
1 /*-------------------------------------------------------------------------
2  *
3  * user.c
4  *        Commands for manipulating users and groups.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.92 2002/03/02 21:39:23 momjian Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
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 "storage/pmsignal.h"
30 #include "utils/array.h"
31 #include "utils/builtins.h"
32 #include "utils/fmgroids.h"
33 #include "utils/guc.h"
34 #include "utils/lsyscache.h"
35 #include "utils/syscache.h"
36
37
38 extern bool Password_encryption;
39
40 static void CheckPgUserAclNotNull(void);
41
42 /*---------------------------------------------------------------------
43  * write_password_file / update_pg_pwd
44  *
45  * copy the modified contents of pg_shadow to a file used by the postmaster
46  * for user authentication.  The file is stored as $PGDATA/global/pg_pwd.
47  *
48  * This function set is both a trigger function for direct updates to pg_shadow
49  * as well as being called directly from create/alter/drop user.
50  *
51  * We raise an error to force transaction rollback if we detect an illegal
52  * username or password --- illegal being defined as values that would
53  * mess up the pg_pwd parser.
54  *---------------------------------------------------------------------
55  */
56 static void
57 write_password_file(Relation rel)
58 {
59         char       *filename,
60                            *tempname;
61         int                     bufsize;
62         FILE       *fp;
63         mode_t          oumask;
64         HeapScanDesc scan;
65         HeapTuple       tuple;
66         TupleDesc       dsc = RelationGetDescr(rel);
67
68         /*
69          * Create a temporary filename to be renamed later.  This prevents the
70          * backend from clobbering the pg_pwd file while the postmaster might
71          * be reading from it.
72          */
73         filename = crypt_getpwdfilename();
74         bufsize = strlen(filename) + 12;
75         tempname = (char *) palloc(bufsize);
76
77         snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
78         oumask = umask((mode_t) 077);
79         fp = AllocateFile(tempname, "w");
80         umask(oumask);
81         if (fp == NULL)
82                 elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
83
84         /* read table */
85         scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
86         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
87         {
88                 Datum           datum_n,
89                                         datum_p,
90                                         datum_v;
91                 bool            null_n,
92                                         null_p,
93                                         null_v;
94                 char       *str_n,
95                                    *str_p,
96                                    *str_v;
97                 int                     i;
98
99                 datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
100                 if (null_n)
101                         continue;                       /* ignore NULL usernames */
102                 str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));
103
104                 datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
105
106                 /*
107                  * It can be argued that people having a null password shouldn't
108                  * be allowed to connect under password authentication, because
109                  * they need to have a password set up first. If you think
110                  * assuming an empty password in that case is better, change this
111                  * logic to look something like the code for valuntil.
112                  */
113                 if (null_p)
114                 {
115                         pfree(str_n);
116                         continue;
117                 }
118                 str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p));
119
120                 datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
121                 if (null_v)
122                         str_v = pstrdup("\\N");
123                 else
124                         str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));
125
126                 /*
127                  * Check for illegal characters in the username and password.
128                  */
129                 i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n");
130                 if (str_n[i] != '\0')
131                         elog(ERROR, "Invalid user name '%s'", str_n);
132                 i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n");
133                 if (str_p[i] != '\0')
134                         elog(ERROR, "Invalid user password '%s'", str_p);
135
136                 /*
137                  * The extra columns we emit here are not really necessary. To
138                  * remove them, the parser in backend/libpq/crypt.c would need to
139                  * be adjusted.
140                  */
141                 fprintf(fp,
142                                 "%s"
143                                 CRYPT_PWD_FILE_SEPSTR
144                                 "0"
145                                 CRYPT_PWD_FILE_SEPSTR
146                                 "x"
147                                 CRYPT_PWD_FILE_SEPSTR
148                                 "x"
149                                 CRYPT_PWD_FILE_SEPSTR
150                                 "x"
151                                 CRYPT_PWD_FILE_SEPSTR
152                                 "x"
153                                 CRYPT_PWD_FILE_SEPSTR
154                                 "%s"
155                                 CRYPT_PWD_FILE_SEPSTR
156                                 "%s\n",
157                                 str_n,
158                                 str_p,
159                                 str_v);
160
161                 pfree(str_n);
162                 pfree(str_p);
163                 pfree(str_v);
164         }
165         heap_endscan(scan);
166
167         fflush(fp);
168         if (ferror(fp))
169                 elog(ERROR, "%s: %m", tempname);
170         FreeFile(fp);
171
172         /*
173          * Rename the temp file to its final name, deleting the old pg_pwd. We
174          * expect that rename(2) is an atomic action.
175          */
176         if (rename(tempname, filename))
177                 elog(ERROR, "rename %s to %s: %m", tempname, filename);
178
179         pfree((void *) tempname);
180         pfree((void *) filename);
181
182         /*
183          * Signal the postmaster to reload its password-file cache.
184          */
185         SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
186 }
187
188
189
190 /* This is the wrapper for triggers. */
191 Datum
192 update_pg_pwd(PG_FUNCTION_ARGS)
193 {
194         /*
195          * ExclusiveLock ensures no one modifies pg_shadow while we read it,
196          * and that only one backend rewrites the flat file at a time.  It's
197          * OK to allow normal reads of pg_shadow in parallel, however.
198          */
199         Relation        rel = heap_openr(ShadowRelationName, ExclusiveLock);
200
201         write_password_file(rel);
202         /* OK to release lock, since we did not modify the relation */
203         heap_close(rel, ExclusiveLock);
204         return PointerGetDatum(NULL);
205 }
206
207
208
209 /*
210  * CREATE USER
211  */
212 void
213 CreateUser(CreateUserStmt *stmt)
214 {
215         Relation        pg_shadow_rel;
216         TupleDesc       pg_shadow_dsc;
217         HeapScanDesc scan;
218         HeapTuple       tuple;
219         Datum           new_record[Natts_pg_shadow];
220         char            new_record_nulls[Natts_pg_shadow];
221         bool            user_exists = false,
222                                 sysid_exists = false,
223                                 havesysid = false;
224         int                     max_id;
225         List       *item,
226                            *option;
227         char       *password = NULL;    /* PostgreSQL user password */
228         bool            encrypt_password = Password_encryption; /* encrypt password? */
229         char            encrypted_password[MD5_PASSWD_LEN + 1];
230         int                     sysid = 0;              /* PgSQL system id (valid if havesysid) */
231         bool            createdb = false;               /* Can the user create databases? */
232         bool            createuser = false;             /* Can this user create users? */
233         List       *groupElts = NIL;    /* The groups the user is a member of */
234         char       *validUntil = NULL;          /* The time the login is valid
235                                                                                  * until */
236         DefElem    *dpassword = NULL;
237         DefElem    *dsysid = NULL;
238         DefElem    *dcreatedb = NULL;
239         DefElem    *dcreateuser = NULL;
240         DefElem    *dgroupElts = NULL;
241         DefElem    *dvalidUntil = NULL;
242
243         /* Extract options from the statement node tree */
244         foreach(option, stmt->options)
245         {
246                 DefElem    *defel = (DefElem *) lfirst(option);
247
248                 if (strcmp(defel->defname, "password") == 0 ||
249                         strcmp(defel->defname, "encryptedPassword") == 0 ||
250                         strcmp(defel->defname, "unencryptedPassword") == 0)
251                 {
252                         if (dpassword)
253                                 elog(ERROR, "CREATE USER: conflicting options");
254                         dpassword = defel;
255                         if (strcmp(defel->defname, "encryptedPassword") == 0)
256                                 encrypt_password = true;
257                         else if (strcmp(defel->defname, "unencryptedPassword") == 0)
258                                 encrypt_password = false;
259                 }
260                 else if (strcmp(defel->defname, "sysid") == 0)
261                 {
262                         if (dsysid)
263                                 elog(ERROR, "CREATE USER: conflicting options");
264                         dsysid = defel;
265                 }
266                 else if (strcmp(defel->defname, "createdb") == 0)
267                 {
268                         if (dcreatedb)
269                                 elog(ERROR, "CREATE USER: conflicting options");
270                         dcreatedb = defel;
271                 }
272                 else if (strcmp(defel->defname, "createuser") == 0)
273                 {
274                         if (dcreateuser)
275                                 elog(ERROR, "CREATE USER: conflicting options");
276                         dcreateuser = defel;
277                 }
278                 else if (strcmp(defel->defname, "groupElts") == 0)
279                 {
280                         if (dgroupElts)
281                                 elog(ERROR, "CREATE USER: conflicting options");
282                         dgroupElts = defel;
283                 }
284                 else if (strcmp(defel->defname, "validUntil") == 0)
285                 {
286                         if (dvalidUntil)
287                                 elog(ERROR, "CREATE USER: conflicting options");
288                         dvalidUntil = defel;
289                 }
290                 else
291                         elog(ERROR, "CREATE USER: option \"%s\" not recognized",
292                                  defel->defname);
293         }
294
295         if (dcreatedb)
296                 createdb = intVal(dcreatedb->arg) != 0;
297         if (dcreateuser)
298                 createuser = intVal(dcreateuser->arg) != 0;
299         if (dsysid)
300         {
301                 sysid = intVal(dsysid->arg);
302                 if (sysid <= 0)
303                         elog(ERROR, "user id must be positive");
304                 havesysid = true;
305         }
306         if (dvalidUntil)
307                 validUntil = strVal(dvalidUntil->arg);
308         if (dpassword)
309                 password = strVal(dpassword->arg);
310         if (dgroupElts)
311                 groupElts = (List *) dgroupElts->arg;
312
313         /* Check some permissions first */
314         if (password)
315                 CheckPgUserAclNotNull();
316
317         if (!superuser())
318                 elog(ERROR, "CREATE USER: permission denied");
319
320         /*
321          * Scan the pg_shadow relation to be certain the user or id doesn't
322          * already exist.  Note we secure exclusive lock, because we also need
323          * to be sure of what the next usesysid should be, and we need to
324          * protect our update of the flat password file.
325          */
326         pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
327         pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
328
329         scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
330         max_id = 99;                            /* start auto-assigned ids at 100 */
331         while (!user_exists && !sysid_exists &&
332                    HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
333         {
334                 Datum           datum;
335                 bool            null;
336
337                 datum = heap_getattr(tuple, Anum_pg_shadow_usename,
338                                                          pg_shadow_dsc, &null);
339                 Assert(!null);
340                 user_exists = (strcmp((char *) DatumGetName(datum), stmt->user) == 0);
341
342                 datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
343                                                          pg_shadow_dsc, &null);
344                 Assert(!null);
345                 if (havesysid)                  /* customized id wanted */
346                         sysid_exists = (DatumGetInt32(datum) == sysid);
347                 else
348                 {
349                         /* pick 1 + max */
350                         if (DatumGetInt32(datum) > max_id)
351                                 max_id = DatumGetInt32(datum);
352                 }
353         }
354         heap_endscan(scan);
355
356         if (user_exists)
357                 elog(ERROR, "CREATE USER: user name \"%s\" already exists",
358                          stmt->user);
359         if (sysid_exists)
360                 elog(ERROR, "CREATE USER: sysid %d is already assigned", sysid);
361
362         /* If no sysid given, use max existing id + 1 */
363         if (!havesysid)
364                 sysid = max_id + 1;
365
366         /*
367          * Build a tuple to insert
368          */
369         new_record[Anum_pg_shadow_usename - 1] =
370                 DirectFunctionCall1(namein, CStringGetDatum(stmt->user));
371         new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid);
372
373         AssertState(BoolIsValid(createdb));
374         new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb);
375         new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
376         AssertState(BoolIsValid(createuser));
377         new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser);
378         /* superuser gets catupd right by default */
379         new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);
380
381         if (password)
382         {
383                 if (!encrypt_password || isMD5(password))
384                         new_record[Anum_pg_shadow_passwd - 1] =
385                                 DirectFunctionCall1(textin, CStringGetDatum(password));
386                 else
387                 {
388                         if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
389                                                         encrypted_password))
390                                 elog(ERROR, "CREATE USER: password encryption failed");
391                         new_record[Anum_pg_shadow_passwd - 1] =
392                                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
393                 }
394         }
395         if (validUntil)
396                 new_record[Anum_pg_shadow_valuntil - 1] =
397                         DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
398
399         new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
400         new_record_nulls[Anum_pg_shadow_usesysid - 1] = ' ';
401
402         new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
403         new_record_nulls[Anum_pg_shadow_usetrace - 1] = ' ';
404         new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
405         new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
406
407         new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
408         new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
409
410         new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n';
411
412         tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
413
414         /*
415          * Insert new record in the pg_shadow table
416          */
417         heap_insert(pg_shadow_rel, tuple);
418
419         /*
420          * Update indexes
421          */
422         if (RelationGetForm(pg_shadow_rel)->relhasindex)
423         {
424                 Relation        idescs[Num_pg_shadow_indices];
425
426                 CatalogOpenIndices(Num_pg_shadow_indices,
427                                                    Name_pg_shadow_indices, idescs);
428                 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
429                                                    tuple);
430                 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
431         }
432
433         /*
434          * Add the user to the groups specified. We'll just call the below
435          * AlterGroup for this.
436          */
437         foreach(item, groupElts)
438         {
439                 AlterGroupStmt ags;
440
441                 ags.name = strVal(lfirst(item));                /* the group name to add
442                                                                                                  * this in */
443                 ags.action = +1;
444                 ags.listUsers = makeList1(makeInteger(sysid));
445                 AlterGroup(&ags, "CREATE USER");
446         }
447
448         /*
449          * Write the updated pg_shadow data to the flat password file.
450          */
451         write_password_file(pg_shadow_rel);
452
453         /*
454          * Now we can clean up; but keep lock until commit.
455          */
456         heap_close(pg_shadow_rel, NoLock);
457 }
458
459
460
461 /*
462  * ALTER USER
463  */
464 extern void
465 AlterUser(AlterUserStmt *stmt)
466 {
467         Datum           new_record[Natts_pg_shadow];
468         char            new_record_nulls[Natts_pg_shadow];
469         Relation        pg_shadow_rel;
470         TupleDesc       pg_shadow_dsc;
471         HeapTuple       tuple,
472                                 new_tuple;
473         bool            null;
474         List       *option;
475         char       *password = NULL;    /* PostgreSQL user password */
476         bool            encrypt_password = Password_encryption; /* encrypt password? */
477         char            encrypted_password[MD5_PASSWD_LEN + 1];
478         int                     createdb = -1;  /* Can the user create databases? */
479         int                     createuser = -1;        /* Can this user create users? */
480         char       *validUntil = NULL;          /* The time the login is valid
481                                                                                  * until */
482         DefElem    *dpassword = NULL;
483         DefElem    *dcreatedb = NULL;
484         DefElem    *dcreateuser = NULL;
485         DefElem    *dvalidUntil = NULL;
486
487         /* Extract options from the statement node tree */
488         foreach(option, stmt->options)
489         {
490                 DefElem    *defel = (DefElem *) lfirst(option);
491
492                 if (strcmp(defel->defname, "password") == 0 ||
493                         strcmp(defel->defname, "encryptedPassword") == 0 ||
494                         strcmp(defel->defname, "unencryptedPassword") == 0)
495                 {
496                         if (dpassword)
497                                 elog(ERROR, "ALTER USER: conflicting options");
498                         dpassword = defel;
499                         if (strcmp(defel->defname, "encryptedPassword") == 0)
500                                 encrypt_password = true;
501                         else if (strcmp(defel->defname, "unencryptedPassword") == 0)
502                                 encrypt_password = false;
503                 }
504                 else if (strcmp(defel->defname, "createdb") == 0)
505                 {
506                         if (dcreatedb)
507                                 elog(ERROR, "ALTER USER: conflicting options");
508                         dcreatedb = defel;
509                 }
510                 else if (strcmp(defel->defname, "createuser") == 0)
511                 {
512                         if (dcreateuser)
513                                 elog(ERROR, "ALTER USER: conflicting options");
514                         dcreateuser = defel;
515                 }
516                 else if (strcmp(defel->defname, "validUntil") == 0)
517                 {
518                         if (dvalidUntil)
519                                 elog(ERROR, "ALTER USER: conflicting options");
520                         dvalidUntil = defel;
521                 }
522                 else
523                         elog(ERROR, "ALTER USER: option \"%s\" not recognized",
524                                  defel->defname);
525         }
526
527         if (dcreatedb)
528                 createdb = intVal(dcreatedb->arg);
529         if (dcreateuser)
530                 createuser = intVal(dcreateuser->arg);
531         if (dvalidUntil)
532                 validUntil = strVal(dvalidUntil->arg);
533         if (dpassword)
534                 password = strVal(dpassword->arg);
535
536         if (password)
537                 CheckPgUserAclNotNull();
538
539         /* must be superuser or just want to change your own password */
540         if (!superuser() &&
541                 !(createdb < 0 &&
542                   createuser < 0 &&
543                   !validUntil &&
544                   password &&
545                   strcmp(GetUserName(GetUserId()), stmt->user) == 0))
546                 elog(ERROR, "ALTER USER: permission denied");
547
548         /* changes to the flat password file cannot be rolled back */
549         if (IsTransactionBlock() && password)
550                 elog(INFO, "ALTER USER: password changes cannot be rolled back");
551
552         /*
553          * Scan the pg_shadow relation to be certain the user exists. Note we
554          * secure exclusive lock to protect our update of the flat password
555          * file.
556          */
557         pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
558         pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
559
560         tuple = SearchSysCache(SHADOWNAME,
561                                                    PointerGetDatum(stmt->user),
562                                                    0, 0, 0);
563         if (!HeapTupleIsValid(tuple))
564                 elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
565
566         /*
567          * Build a tuple to update, perusing the information just obtained
568          */
569         new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
570                                                                                         CStringGetDatum(stmt->user));
571         new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
572
573         /* sysid - leave as is */
574         new_record[Anum_pg_shadow_usesysid - 1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
575         new_record_nulls[Anum_pg_shadow_usesysid - 1] = null ? 'n' : ' ';
576
577         /* createdb */
578         if (createdb < 0)
579         {
580                 /* don't change */
581                 new_record[Anum_pg_shadow_usecreatedb - 1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
582                 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = null ? 'n' : ' ';
583         }
584         else
585         {
586                 new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0);
587                 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
588         }
589
590         /* trace - leave as is */
591         new_record[Anum_pg_shadow_usetrace - 1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
592         new_record_nulls[Anum_pg_shadow_usetrace - 1] = null ? 'n' : ' ';
593
594         /*
595          * createuser (superuser) and catupd
596          *
597          * XXX It's rather unclear how to handle catupd.  It's probably best to
598          * keep it equal to the superuser status, otherwise you could end up
599          * with a situation where no existing superuser can alter the
600          * catalogs, including pg_shadow!
601          */
602         if (createuser < 0)
603         {
604                 /* don't change */
605                 new_record[Anum_pg_shadow_usesuper - 1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
606                 new_record_nulls[Anum_pg_shadow_usesuper - 1] = null ? 'n' : ' ';
607
608                 new_record[Anum_pg_shadow_usecatupd - 1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
609                 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = null ? 'n' : ' ';
610         }
611         else
612         {
613                 new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0);
614                 new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
615
616                 new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser > 0);
617                 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
618         }
619
620         /* password */
621         if (password)
622         {
623                 if (!encrypt_password || isMD5(password))
624                         new_record[Anum_pg_shadow_passwd - 1] =
625                                 DirectFunctionCall1(textin, CStringGetDatum(password));
626                 else
627                 {
628                         if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
629                                                         encrypted_password))
630                                 elog(ERROR, "CREATE USER: password encryption failed");
631                         new_record[Anum_pg_shadow_passwd - 1] =
632                                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
633                 }
634                 new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
635         }
636         else
637         {
638                 /* leave as is */
639                 new_record[Anum_pg_shadow_passwd - 1] =
640                         heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
641                 new_record_nulls[Anum_pg_shadow_passwd - 1] = null ? 'n' : ' ';
642         }
643
644         /* valid until */
645         if (validUntil)
646         {
647                 new_record[Anum_pg_shadow_valuntil - 1] =
648                         DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
649                 new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
650         }
651         else
652         {
653                 /* leave as is */
654                 new_record[Anum_pg_shadow_valuntil - 1] =
655                         heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
656                 new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
657         }
658
659         /* leave useconfig as is */
660         new_record[Anum_pg_shadow_useconfig - 1] =
661                 heap_getattr(tuple, Anum_pg_shadow_useconfig, pg_shadow_dsc, &null);
662         new_record_nulls[Anum_pg_shadow_useconfig - 1] = null ? 'n' : ' ';
663
664         new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
665         simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
666
667         /* Update indexes */
668         if (RelationGetForm(pg_shadow_rel)->relhasindex)
669         {
670                 Relation        idescs[Num_pg_shadow_indices];
671
672                 CatalogOpenIndices(Num_pg_shadow_indices,
673                                                    Name_pg_shadow_indices, idescs);
674                 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
675                                                    new_tuple);
676                 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
677         }
678
679         ReleaseSysCache(tuple);
680         heap_freetuple(new_tuple);
681
682         /*
683          * Write the updated pg_shadow data to the flat password file.
684          */
685         write_password_file(pg_shadow_rel);
686
687         /*
688          * Now we can clean up.
689          */
690         heap_close(pg_shadow_rel, NoLock);
691 }
692
693
694
695 /*
696  * ALTER USER ... SET
697  */
698 void
699 AlterUserSet(AlterUserSetStmt *stmt)
700 {
701         char       *valuestr;
702         HeapTuple       oldtuple,
703                                 newtuple;
704         Relation        rel;
705         Datum           repl_val[Natts_pg_shadow];
706         char            repl_null[Natts_pg_shadow];
707         char            repl_repl[Natts_pg_shadow];
708         int                     i;
709
710         valuestr = (stmt->value
711                                 ? ((A_Const *) lfirst(stmt->value))->val.val.str
712                                 : NULL);
713
714         rel = heap_openr(ShadowRelationName, RowExclusiveLock);
715         oldtuple = SearchSysCache(SHADOWNAME,
716                                                           PointerGetDatum(stmt->user),
717                                                           0, 0, 0);
718         if (!HeapTupleIsValid(oldtuple))
719                 elog(ERROR, "user \"%s\" does not exist", stmt->user);
720
721         if (!(superuser()
722                   || ((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetUserId()))
723                 elog(ERROR, "permission denied");
724
725         for (i = 0; i < Natts_pg_shadow; i++)
726                 repl_repl[i] = ' ';
727
728         repl_repl[Anum_pg_shadow_useconfig-1] = 'r';
729         if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
730                 /* RESET ALL */
731                 repl_null[Anum_pg_shadow_useconfig-1] = 'n';
732         else
733         {
734                 Datum datum;
735                 bool isnull;
736                 ArrayType *a;
737
738                 repl_null[Anum_pg_shadow_useconfig-1] = ' ';
739
740                 datum = SysCacheGetAttr(SHADOWNAME, oldtuple,
741                                                                 Anum_pg_shadow_useconfig, &isnull);
742
743                 if (valuestr)
744                         a = GUCArrayAdd(isnull
745                                                         ? NULL
746                                                         : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
747                                                         stmt->variable, valuestr);
748                 else
749                         a = GUCArrayDelete(isnull
750                                                            ? NULL
751                                                            : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
752                                                            stmt->variable);
753
754                 repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
755         }
756
757         newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
758         simple_heap_update(rel, &oldtuple->t_self, newtuple);
759
760         {
761                 Relation        idescs[Num_pg_shadow_indices];
762
763                 CatalogOpenIndices(Num_pg_shadow_indices, Name_pg_shadow_indices, idescs);
764                 CatalogIndexInsert(idescs, Num_pg_shadow_indices, rel, newtuple);
765                 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
766         }
767
768         ReleaseSysCache(oldtuple);
769         heap_close(rel, RowExclusiveLock);
770 }
771
772
773
774 /*
775  * DROP USER
776  */
777 void
778 DropUser(DropUserStmt *stmt)
779 {
780         Relation        pg_shadow_rel;
781         TupleDesc       pg_shadow_dsc;
782         List       *item;
783
784         if (!superuser())
785                 elog(ERROR, "DROP USER: permission denied");
786
787         if (IsTransactionBlock())
788                 elog(INFO, "DROP USER cannot be rolled back completely");
789
790         /*
791          * Scan the pg_shadow relation to find the usesysid of the user to be
792          * deleted.  Note we secure exclusive lock, because we need to protect
793          * our update of the flat password file.
794          */
795         pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
796         pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
797
798         foreach(item, stmt->users)
799         {
800                 HeapTuple       tuple,
801                                         tmp_tuple;
802                 Relation        pg_rel;
803                 TupleDesc       pg_dsc;
804                 ScanKeyData scankey;
805                 HeapScanDesc scan;
806                 Datum           datum;
807                 bool            null;
808                 int32           usesysid;
809                 const char *user = strVal(lfirst(item));
810
811                 tuple = SearchSysCache(SHADOWNAME,
812                                                            PointerGetDatum(user),
813                                                            0, 0, 0);
814                 if (!HeapTupleIsValid(tuple))
815                         elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
816                                  (length(stmt->users) > 1) ? " (no users removed)" : "");
817
818                 usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
819
820                 if (usesysid == GetUserId())
821                         elog(ERROR, "current user cannot be dropped");
822                 if (usesysid == GetSessionUserId())
823                         elog(ERROR, "session user cannot be dropped");
824
825                 /*
826                  * Check if user still owns a database. If so, error out.
827                  *
828                  * (It used to be that this function would drop the database
829                  * automatically. This is not only very dangerous for people that
830                  * don't read the manual, it doesn't seem to be the behaviour one
831                  * would expect either.) -- petere 2000/01/14)
832                  */
833                 pg_rel = heap_openr(DatabaseRelationName, AccessShareLock);
834                 pg_dsc = RelationGetDescr(pg_rel);
835
836                 ScanKeyEntryInitialize(&scankey, 0x0,
837                                                            Anum_pg_database_datdba, F_INT4EQ,
838                                                            Int32GetDatum(usesysid));
839
840                 scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
841
842                 if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
843                 {
844                         char       *dbname;
845
846                         datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
847                                                                  pg_dsc, &null);
848                         Assert(!null);
849                         dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
850                         elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
851                                  user, dbname,
852                                  (length(stmt->users) > 1) ? " (no users removed)" : "");
853                 }
854
855                 heap_endscan(scan);
856                 heap_close(pg_rel, AccessShareLock);
857
858                 /*
859                  * Somehow we'd have to check for tables, views, etc. owned by the
860                  * user as well, but those could be spread out over all sorts of
861                  * databases which we don't have access to (easily).
862                  */
863
864                 /*
865                  * Remove the user from the pg_shadow table
866                  */
867                 simple_heap_delete(pg_shadow_rel, &tuple->t_self);
868
869                 ReleaseSysCache(tuple);
870
871                 /*
872                  * Remove user from groups
873                  *
874                  * try calling alter group drop user for every group
875                  */
876                 pg_rel = heap_openr(GroupRelationName, ExclusiveLock);
877                 pg_dsc = RelationGetDescr(pg_rel);
878                 scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
879                 while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
880                 {
881                         AlterGroupStmt ags;
882
883                         /* the group name from which to try to drop the user: */
884                         datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
885                         ags.name = DatumGetCString(DirectFunctionCall1(nameout, datum));
886                         ags.action = -1;
887                         ags.listUsers = makeList1(makeInteger(usesysid));
888                         AlterGroup(&ags, "DROP USER");
889                 }
890                 heap_endscan(scan);
891                 heap_close(pg_rel, ExclusiveLock);
892
893                 /*
894                  * Advance command counter so that later iterations of this loop
895                  * will see the changes already made.  This is essential if, for
896                  * example, we are trying to drop two users who are members of the
897                  * same group --- the AlterGroup for the second user had better
898                  * see the tuple updated from the first one.
899                  */
900                 CommandCounterIncrement();
901         }
902
903         /*
904          * Write the updated pg_shadow data to the flat password file.
905          */
906         write_password_file(pg_shadow_rel);
907
908         /*
909          * Now we can clean up.
910          */
911         heap_close(pg_shadow_rel, NoLock);
912 }
913
914
915
916 /*
917  * CheckPgUserAclNotNull
918  *
919  * check to see if there is an ACL on pg_shadow
920  */
921 static void
922 CheckPgUserAclNotNull()
923 {
924         HeapTuple       htup;
925
926         htup = SearchSysCache(RELNAME,
927                                                   PointerGetDatum(ShadowRelationName),
928                                                   0, 0, 0);
929         if (!HeapTupleIsValid(htup))
930                 elog(ERROR, "CheckPgUserAclNotNull: \"%s\" not found",
931                          ShadowRelationName);
932
933         if (heap_attisnull(htup, Anum_pg_class_relacl))
934                 elog(ERROR,
935                          "To use passwords, you have to revoke permissions on %s "
936                          "so normal users cannot read the passwords. "
937                          "Try 'REVOKE ALL ON \"%s\" FROM PUBLIC'.",
938                          ShadowRelationName, ShadowRelationName);
939
940         ReleaseSysCache(htup);
941 }
942
943
944
945 /*
946  * CREATE GROUP
947  */
948 void
949 CreateGroup(CreateGroupStmt *stmt)
950 {
951         Relation        pg_group_rel;
952         HeapScanDesc scan;
953         HeapTuple       tuple;
954         TupleDesc       pg_group_dsc;
955         bool            group_exists = false,
956                                 sysid_exists = false,
957                                 havesysid = false;
958         int                     max_id;
959         Datum           new_record[Natts_pg_group];
960         char            new_record_nulls[Natts_pg_group];
961         List       *item,
962                            *option,
963                            *newlist = NIL;
964         ArrayType  *userarray;
965         int                     sysid = 0;
966         List       *userElts = NIL;
967         DefElem    *dsysid = NULL;
968         DefElem    *duserElts = NULL;
969
970         foreach(option, stmt->options)
971         {
972                 DefElem    *defel = (DefElem *) lfirst(option);
973
974                 if (strcmp(defel->defname, "sysid") == 0)
975                 {
976                         if (dsysid)
977                                 elog(ERROR, "CREATE GROUP: conflicting options");
978                         dsysid = defel;
979                 }
980                 else if (strcmp(defel->defname, "userElts") == 0)
981                 {
982                         if (duserElts)
983                                 elog(ERROR, "CREATE GROUP: conflicting options");
984                         duserElts = defel;
985                 }
986                 else
987                         elog(ERROR, "CREATE GROUP: option \"%s\" not recognized",
988                                  defel->defname);
989         }
990
991         if (dsysid)
992         {
993                 sysid = intVal(dsysid->arg);
994                 if (sysid <= 0)
995                         elog(ERROR, "group id must be positive");
996                 havesysid = true;
997         }
998
999         if (duserElts)
1000                 userElts = (List *) duserElts->arg;
1001
1002         /*
1003          * Make sure the user can do this.
1004          */
1005         if (!superuser())
1006                 elog(ERROR, "CREATE GROUP: permission denied");
1007
1008         pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1009         pg_group_dsc = RelationGetDescr(pg_group_rel);
1010
1011         scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1012         max_id = 99;                            /* start auto-assigned ids at 100 */
1013         while (!group_exists && !sysid_exists &&
1014                    HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1015         {
1016                 Datum           datum;
1017                 bool            null;
1018
1019                 datum = heap_getattr(tuple, Anum_pg_group_groname,
1020                                                          pg_group_dsc, &null);
1021                 Assert(!null);
1022                 group_exists = (strcmp((char *) DatumGetName(datum), stmt->name) == 0);
1023
1024                 datum = heap_getattr(tuple, Anum_pg_group_grosysid,
1025                                                          pg_group_dsc, &null);
1026                 Assert(!null);
1027                 if (havesysid)                  /* customized id wanted */
1028                         sysid_exists = (DatumGetInt32(datum) == sysid);
1029                 else
1030                 {
1031                         /* pick 1 + max */
1032                         if (DatumGetInt32(datum) > max_id)
1033                                 max_id = DatumGetInt32(datum);
1034                 }
1035         }
1036         heap_endscan(scan);
1037
1038         if (group_exists)
1039                 elog(ERROR, "CREATE GROUP: group name \"%s\" already exists",
1040                          stmt->name);
1041         if (sysid_exists)
1042                 elog(ERROR, "CREATE GROUP: group sysid %d is already assigned",
1043                          sysid);
1044
1045         /*
1046          * Translate the given user names to ids
1047          */
1048         foreach(item, userElts)
1049         {
1050                 const char *groupuser = strVal(lfirst(item));
1051                 Value      *v;
1052
1053                 v = makeInteger(get_usesysid(groupuser));
1054                 if (!member(v, newlist))
1055                         newlist = lappend(newlist, v);
1056         }
1057
1058         /* build an array to insert */
1059         if (newlist)
1060         {
1061                 int                     i;
1062
1063                 userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1064                 userarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1065                 userarray->flags = 0;
1066                 ARR_NDIM(userarray) = 1;        /* one dimensional array */
1067                 ARR_LBOUND(userarray)[0] = 1;   /* axis starts at one */
1068                 ARR_DIMS(userarray)[0] = length(newlist);               /* axis is this long */
1069                 /* fill the array */
1070                 i = 0;
1071                 foreach(item, newlist)
1072                         ((int *) ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
1073         }
1074         else
1075                 userarray = NULL;
1076
1077         /*
1078          * Form a tuple to insert
1079          */
1080         if (!havesysid)
1081                 sysid = max_id + 1;
1082
1083         new_record[Anum_pg_group_groname - 1] =
1084                 DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1085         new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(sysid);
1086         new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(userarray);
1087
1088         new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1089         new_record_nulls[Anum_pg_group_grosysid - 1] = ' ';
1090         new_record_nulls[Anum_pg_group_grolist - 1] = userarray ? ' ' : 'n';
1091
1092         tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1093
1094         /*
1095          * Insert a new record in the pg_group_table
1096          */
1097         heap_insert(pg_group_rel, tuple);
1098
1099         /*
1100          * Update indexes
1101          */
1102         if (RelationGetForm(pg_group_rel)->relhasindex)
1103         {
1104                 Relation        idescs[Num_pg_group_indices];
1105
1106                 CatalogOpenIndices(Num_pg_group_indices,
1107                                                    Name_pg_group_indices, idescs);
1108                 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1109                                                    tuple);
1110                 CatalogCloseIndices(Num_pg_group_indices, idescs);
1111         }
1112
1113         heap_close(pg_group_rel, NoLock);
1114 }
1115
1116
1117
1118 /*
1119  * ALTER GROUP
1120  */
1121 void
1122 AlterGroup(AlterGroupStmt *stmt, const char *tag)
1123 {
1124         Relation        pg_group_rel;
1125         TupleDesc       pg_group_dsc;
1126         HeapTuple       group_tuple;
1127
1128         /*
1129          * Make sure the user can do this.
1130          */
1131         if (!superuser())
1132                 elog(ERROR, "%s: permission denied", tag);
1133
1134         pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1135         pg_group_dsc = RelationGetDescr(pg_group_rel);
1136
1137         /*
1138          * Fetch existing tuple for group.
1139          */
1140         group_tuple = SearchSysCache(GRONAME,
1141                                                                  PointerGetDatum(stmt->name),
1142                                                                  0, 0, 0);
1143         if (!HeapTupleIsValid(group_tuple))
1144                 elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
1145
1146         /*
1147          * Now decide what to do.
1148          */
1149         AssertState(stmt->action == +1 || stmt->action == -1);
1150
1151         if (stmt->action == +1)         /* add users, might also be invoked by
1152                                                                  * create user */
1153         {
1154                 Datum           new_record[Natts_pg_group];
1155                 char            new_record_nulls[Natts_pg_group];
1156                 ArrayType  *newarray,
1157                                    *oldarray;
1158                 List       *newlist = NIL,
1159                                    *item;
1160                 HeapTuple       tuple;
1161                 bool            null = false;
1162                 Datum           datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
1163                 int                     i;
1164
1165                 oldarray = (ArrayType *) datum;
1166                 Assert(null || ARR_NDIM(oldarray) == 1);
1167                 /* first add the old array to the hitherto empty list */
1168                 if (!null)
1169                         for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
1170                         {
1171                                 int                     index,
1172                                                         arrval;
1173                                 Value      *v;
1174                                 bool            valueNull;
1175
1176                                 index = i;
1177                                 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
1178                                                                                         sizeof(int), 0, &valueNull));
1179                                 v = makeInteger(arrval);
1180                                 /* filter out duplicates */
1181                                 if (!member(v, newlist))
1182                                         newlist = lappend(newlist, v);
1183                         }
1184
1185                 /*
1186                  * now convert the to be added usernames to sysids and add them to
1187                  * the list
1188                  */
1189                 foreach(item, stmt->listUsers)
1190                 {
1191                         Value      *v;
1192
1193                         if (strcmp(tag, "ALTER GROUP") == 0)
1194                         {
1195                                 /* Get the uid of the proposed user to add. */
1196                                 v = makeInteger(get_usesysid(strVal(lfirst(item))));
1197                         }
1198                         else if (strcmp(tag, "CREATE USER") == 0)
1199                         {
1200                                 /*
1201                                  * in this case we already know the uid and it wouldn't be
1202                                  * in the cache anyway yet
1203                                  */
1204                                 v = lfirst(item);
1205                         }
1206                         else
1207                         {
1208                                 elog(ERROR, "AlterGroup: unknown tag %s", tag);
1209                                 v = NULL;               /* keep compiler quiet */
1210                         }
1211
1212                         if (!member(v, newlist))
1213                                 newlist = lappend(newlist, v);
1214                         else
1215
1216                                 /*
1217                                  * we silently assume here that this error will only come
1218                                  * up in a ALTER GROUP statement
1219                                  */
1220                                 elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"",
1221                                          tag, strVal(lfirst(item)), stmt->name);
1222                 }
1223
1224                 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1225                 newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1226                 newarray->flags = 0;
1227                 ARR_NDIM(newarray) = 1; /* one dimensional array */
1228                 ARR_LBOUND(newarray)[0] = 1;    /* axis starts at one */
1229                 ARR_DIMS(newarray)[0] = length(newlist);                /* axis is this long */
1230                 /* fill the array */
1231                 i = 0;
1232                 foreach(item, newlist)
1233                         ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
1234
1235                 /*
1236                  * Form a tuple with the new array and write it back.
1237                  */
1238                 new_record[Anum_pg_group_groname - 1] =
1239                         DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1240                 new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1241                 new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
1242                 new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
1243                 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
1244                 new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
1245
1246                 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1247                 simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
1248
1249                 /* Update indexes */
1250                 if (RelationGetForm(pg_group_rel)->relhasindex)
1251                 {
1252                         Relation        idescs[Num_pg_group_indices];
1253
1254                         CatalogOpenIndices(Num_pg_group_indices,
1255                                                            Name_pg_group_indices, idescs);
1256                         CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1257                                                            tuple);
1258                         CatalogCloseIndices(Num_pg_group_indices, idescs);
1259                 }
1260         }                                                       /* endif alter group add user */
1261
1262         else if (stmt->action == -1)    /* drop users from group */
1263         {
1264                 Datum           datum;
1265                 bool            null;
1266                 bool            is_dropuser = strcmp(tag, "DROP USER") == 0;
1267
1268                 datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
1269                 if (null)
1270                 {
1271                         if (!is_dropuser)
1272                                 elog(NOTICE, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
1273                 }
1274                 else
1275                 {
1276                         HeapTuple       tuple;
1277                         Datum           new_record[Natts_pg_group];
1278                         char            new_record_nulls[Natts_pg_group];
1279                         ArrayType  *oldarray,
1280                                            *newarray;
1281                         List       *newlist = NIL,
1282                                            *item;
1283                         int                     i;
1284
1285                         oldarray = (ArrayType *) datum;
1286                         Assert(ARR_NDIM(oldarray) == 1);
1287                         /* first add the old array to the hitherto empty list */
1288                         for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
1289                         {
1290                                 int                     index,
1291                                                         arrval;
1292                                 Value      *v;
1293                                 bool            valueNull;
1294
1295                                 index = i;
1296                                 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
1297                                                                                         sizeof(int), 0, &valueNull));
1298                                 v = makeInteger(arrval);
1299                                 /* filter out duplicates */
1300                                 if (!member(v, newlist))
1301                                         newlist = lappend(newlist, v);
1302                         }
1303
1304                         /*
1305                          * now convert the to be dropped usernames to sysids and
1306                          * remove them from the list
1307                          */
1308                         foreach(item, stmt->listUsers)
1309                         {
1310                                 Value      *v;
1311
1312                                 if (!is_dropuser)
1313                                 {
1314                                         /* Get the uid of the proposed user to drop. */
1315                                         v = makeInteger(get_usesysid(strVal(lfirst(item))));
1316                                 }
1317                                 else
1318                                 {
1319                                         /* for dropuser we already know the uid */
1320                                         v = lfirst(item);
1321                                 }
1322                                 if (member(v, newlist))
1323                                         newlist = LispRemove(v, newlist);
1324                                 else if (!is_dropuser)
1325                                         elog(NOTICE, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
1326                         }
1327
1328                         newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1329                         newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1330                         newarray->flags = 0;
1331                         ARR_NDIM(newarray) = 1;         /* one dimensional array */
1332                         ARR_LBOUND(newarray)[0] = 1;            /* axis starts at one */
1333                         ARR_DIMS(newarray)[0] = length(newlist);        /* axis is this long */
1334                         /* fill the array */
1335                         i = 0;
1336                         foreach(item, newlist)
1337                                 ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
1338
1339                         /*
1340                          * Insert the new tuple with the updated user list
1341                          */
1342                         new_record[Anum_pg_group_groname - 1] =
1343                                 DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1344                         new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1345                         new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
1346                         new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
1347                         new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
1348                         new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
1349
1350                         tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1351                         simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
1352
1353                         /* Update indexes */
1354                         if (RelationGetForm(pg_group_rel)->relhasindex)
1355                         {
1356                                 Relation        idescs[Num_pg_group_indices];
1357
1358                                 CatalogOpenIndices(Num_pg_group_indices,
1359                                                                    Name_pg_group_indices, idescs);
1360                                 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1361                                                                    tuple);
1362                                 CatalogCloseIndices(Num_pg_group_indices, idescs);
1363                         }
1364
1365                 }                                               /* endif group not null */
1366         }                                                       /* endif alter group drop user */
1367
1368         ReleaseSysCache(group_tuple);
1369
1370         heap_close(pg_group_rel, NoLock);
1371 }
1372
1373
1374
1375 /*
1376  * DROP GROUP
1377  */
1378 void
1379 DropGroup(DropGroupStmt *stmt)
1380 {
1381         Relation        pg_group_rel;
1382         HeapScanDesc scan;
1383         HeapTuple       tuple;
1384         TupleDesc       pg_group_dsc;
1385         bool            gro_exists = false;
1386
1387         /*
1388          * Make sure the user can do this.
1389          */
1390         if (!superuser())
1391                 elog(ERROR, "DROP GROUP: permission denied");
1392
1393         /*
1394          * Scan the pg_group table and delete all matching groups.
1395          */
1396         pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1397         pg_group_dsc = RelationGetDescr(pg_group_rel);
1398         scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1399
1400         while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1401         {
1402                 Datum           datum;
1403                 bool            null;
1404
1405                 datum = heap_getattr(tuple, Anum_pg_group_groname,
1406                                                          pg_group_dsc, &null);
1407                 if (!null && strcmp((char *) DatumGetName(datum), stmt->name) == 0)
1408                 {
1409                         gro_exists = true;
1410                         simple_heap_delete(pg_group_rel, &tuple->t_self);
1411                 }
1412         }
1413
1414         heap_endscan(scan);
1415
1416         /*
1417          * Did we find any?
1418          */
1419         if (!gro_exists)
1420                 elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
1421
1422         heap_close(pg_group_rel, NoLock);
1423 }