]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[postgresql] / src / backend / utils / adt / acl.c
1 /*-------------------------------------------------------------------------
2  *
3  * acl.c
4  *        Basic access control list data structures manipulation routines.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.57 2001/01/24 19:43:12 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include <ctype.h>
16
17 #include "postgres.h"
18
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/pg_shadow.h"
22 #include "catalog/pg_type.h"
23 #include "lib/stringinfo.h"
24 #include "miscadmin.h"
25 #include "utils/acl.h"
26 #include "utils/builtins.h"
27 #include "utils/memutils.h"
28 #include "utils/syscache.h"
29
30 static char *getid(char *s, char *n);
31 static bool aclitemeq(AclItem *a1, AclItem *a2);
32 static bool aclitemgt(AclItem *a1, AclItem *a2);
33 static char *aclparse(char *s, AclItem *aip, unsigned *modechg);
34
35 #define ACL_IDTYPE_GID_KEYWORD  "group"
36 #define ACL_IDTYPE_UID_KEYWORD  "user"
37
38
39 /*
40  * getid
41  *              Consumes the first alphanumeric string (identifier) found in string
42  *              's', ignoring any leading white space.  If it finds a double quote
43  *              it returns the word inside the quotes.
44  *
45  * RETURNS:
46  *              the string position in 's' that points to the next non-space character
47  *              in 's', after any quotes.  Also:
48  *              - loads the identifier into 'name'.  (If no identifier is found, 'name'
49  *                contains an empty string.)  name must be NAMEDATALEN bytes.
50  */
51 static char *
52 getid(char *s, char *n)
53 {
54         unsigned        len;
55         char       *id;
56         int                     in_quotes = 0;
57
58         Assert(s && n);
59
60         while (isspace((unsigned char) *s))
61                 ++s;
62
63         if (*s == '"')
64         {
65                 in_quotes = 1;
66                 s++;
67         }
68
69         for (id = s, len = 0;
70                  isalnum((unsigned char) *s) || *s == '_' || in_quotes;
71                  ++len, ++s)
72         {
73                 if (in_quotes && *s == '"')
74                 {
75                         len--;
76                         in_quotes = 0;
77                 }
78         }
79         if (len >= NAMEDATALEN)
80                 elog(ERROR, "getid: identifier must be <%d characters",
81                          NAMEDATALEN);
82         if (len > 0)
83                 memmove(n, id, len);
84         n[len] = '\0';
85         while (isspace((unsigned char) *s))
86                 ++s;
87         return s;
88 }
89
90 /*
91  * aclparse
92  *              Consumes and parses an ACL specification of the form:
93  *                              [group|user] [A-Za-z0-9]*[+-=][rwaR]*
94  *              from string 's', ignoring any leading white space or white space
95  *              between the optional id type keyword (group|user) and the actual
96  *              ACL specification.
97  *
98  *              This routine is called by the parser as well as aclitemin(), hence
99  *              the added generality.
100  *
101  * RETURNS:
102  *              the string position in 's' immediately following the ACL
103  *              specification.  Also:
104  *              - loads the structure pointed to by 'aip' with the appropriate
105  *                UID/GID, id type identifier and mode type values.
106  *              - loads 'modechg' with the mode change flag.
107  */
108 static char *
109 aclparse(char *s, AclItem *aip, unsigned *modechg)
110 {
111         HeapTuple       htup;
112         char            name[NAMEDATALEN];
113
114         Assert(s && aip && modechg);
115
116 #ifdef ACLDEBUG_TRACE
117         printf("aclparse: input = '%s'\n", s);
118 #endif
119         aip->ai_idtype = ACL_IDTYPE_UID;
120         s = getid(s, name);
121         if (*s != ACL_MODECHG_ADD_CHR &&
122                 *s != ACL_MODECHG_DEL_CHR &&
123                 *s != ACL_MODECHG_EQL_CHR)
124         {
125                 /* we just read a keyword, not a name */
126                 if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD))
127                         aip->ai_idtype = ACL_IDTYPE_GID;
128                 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD))
129                         elog(ERROR, "aclparse: bad keyword, must be [group|user]");
130                 s = getid(s, name);             /* move s to the name beyond the keyword */
131                 if (name[0] == '\0')
132                         elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
133         }
134         if (name[0] == '\0')
135                 aip->ai_idtype = ACL_IDTYPE_WORLD;
136
137         switch (*s)
138         {
139                 case ACL_MODECHG_ADD_CHR:
140                         *modechg = ACL_MODECHG_ADD;
141                         break;
142                 case ACL_MODECHG_DEL_CHR:
143                         *modechg = ACL_MODECHG_DEL;
144                         break;
145                 case ACL_MODECHG_EQL_CHR:
146                         *modechg = ACL_MODECHG_EQL;
147                         break;
148                 default:
149                         elog(ERROR, "aclparse: mode change flag must use \"%s\"",
150                                  ACL_MODECHG_STR);
151         }
152
153         aip->ai_mode = ACL_NO;
154         while (isalpha((unsigned char) *++s))
155         {
156                 switch (*s)
157                 {
158                         case ACL_MODE_AP_CHR:
159                                 aip->ai_mode |= ACL_AP;
160                                 break;
161                         case ACL_MODE_RD_CHR:
162                                 aip->ai_mode |= ACL_RD;
163                                 break;
164                         case ACL_MODE_WR_CHR:
165                                 aip->ai_mode |= ACL_WR;
166                                 break;
167                         case ACL_MODE_RU_CHR:
168                                 aip->ai_mode |= ACL_RU;
169                                 break;
170                         default:
171                                 elog(ERROR, "aclparse: mode flags must use \"%s\"",
172                                          ACL_MODE_STR);
173                 }
174         }
175
176         switch (aip->ai_idtype)
177         {
178                 case ACL_IDTYPE_UID:
179                         htup = SearchSysCache(SHADOWNAME,
180                                                                   PointerGetDatum(name),
181                                                                   0, 0, 0);
182                         if (!HeapTupleIsValid(htup))
183                                 elog(ERROR, "aclparse: non-existent user \"%s\"", name);
184                         aip->ai_id = ((Form_pg_shadow) GETSTRUCT(htup))->usesysid;
185                         ReleaseSysCache(htup);
186                         break;
187                 case ACL_IDTYPE_GID:
188                         aip->ai_id = get_grosysid(name);
189                         break;
190                 case ACL_IDTYPE_WORLD:
191                         aip->ai_id = ACL_ID_WORLD;
192                         break;
193         }
194
195 #ifdef ACLDEBUG_TRACE
196         elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x",
197                  aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
198 #endif
199         return s;
200 }
201
202 /*
203  * makeacl
204  *              Allocates storage for a new Acl with 'n' entries.
205  *
206  * RETURNS:
207  *              the new Acl
208  */
209 Acl *
210 makeacl(int n)
211 {
212         Acl                *new_acl;
213         Size            size;
214
215         if (n < 0)
216                 elog(ERROR, "makeacl: invalid size: %d", n);
217         size = ACL_N_SIZE(n);
218         new_acl = (Acl *) palloc(size);
219         MemSet((char *) new_acl, 0, size);
220         new_acl->size = size;
221         new_acl->ndim = 1;
222         new_acl->flags = 0;
223         ARR_LBOUND(new_acl)[0] = 0;
224         ARR_DIMS(new_acl)[0] = n;
225         return new_acl;
226 }
227
228 /*
229  * aclitemin
230  *              Allocates storage for, and fills in, a new AclItem given a string
231  *              's' that contains an ACL specification.  See aclparse for details.
232  *
233  * RETURNS:
234  *              the new AclItem
235  */
236 Datum
237 aclitemin(PG_FUNCTION_ARGS)
238 {
239         char       *s = PG_GETARG_CSTRING(0);
240         AclItem    *aip;
241         unsigned        modechg;
242
243         aip = (AclItem *) palloc(sizeof(AclItem));
244         s = aclparse(s, aip, &modechg);
245         if (modechg != ACL_MODECHG_EQL)
246                 elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
247         while (isspace((unsigned char) *s))
248                 ++s;
249         if (*s)
250                 elog(ERROR, "aclitemin: extra garbage at end of specification");
251         PG_RETURN_ACLITEM_P(aip);
252 }
253
254 /*
255  * aclitemout
256  *              Allocates storage for, and fills in, a new null-delimited string
257  *              containing a formatted ACL specification.  See aclparse for details.
258  *
259  * RETURNS:
260  *              the new string
261  */
262 Datum
263 aclitemout(PG_FUNCTION_ARGS)
264 {
265         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
266         char       *p;
267         char       *out;
268         HeapTuple       htup;
269         unsigned        i;
270         char       *tmpname;
271
272         p = out = palloc(strlen("group =arwR ") + 1 + NAMEDATALEN);
273         *p = '\0';
274
275         switch (aip->ai_idtype)
276         {
277                 case ACL_IDTYPE_UID:
278                         htup = SearchSysCache(SHADOWSYSID,
279                                                                   ObjectIdGetDatum(aip->ai_id),
280                                                                   0, 0, 0);
281                         if (HeapTupleIsValid(htup))
282                         {
283                                 strncat(p,
284                                                 NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
285                                                 NAMEDATALEN);
286                                 ReleaseSysCache(htup);
287                         }
288                         else
289                         {
290                                 /* Generate numeric UID if we don't find an entry */
291                                 char       *tmp;
292
293                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
294                                                                           Int32GetDatum((int32) aip->ai_id)));
295                                 strcat(p, tmp);
296                                 pfree(tmp);
297                         }
298                         break;
299                 case ACL_IDTYPE_GID:
300                         strcat(p, "group ");
301                         tmpname = get_groname(aip->ai_id);
302                         if (tmpname != NULL)
303                                 strncat(p, tmpname, NAMEDATALEN);
304                         else
305                         {
306                                 /* Generate numeric GID if we don't find an entry */
307                                 char       *tmp;
308
309                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
310                                                                           Int32GetDatum((int32) aip->ai_id)));
311                                 strcat(p, tmp);
312                                 pfree(tmp);
313                         }
314                         break;
315                 case ACL_IDTYPE_WORLD:
316                         break;
317                 default:
318                         elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
319                         break;
320         }
321         while (*p)
322                 ++p;
323         *p++ = '=';
324         for (i = 0; i < N_ACL_MODES; ++i)
325                 if ((aip->ai_mode >> i) & 01)
326                         *p++ = ACL_MODE_STR[i];
327         *p = '\0';
328
329         PG_RETURN_CSTRING(out);
330 }
331
332 /*
333  * aclitemeq
334  * aclitemgt
335  *              AclItem equality and greater-than comparison routines.
336  *              Two AclItems are considered equal iff they have the
337  *              same identifier (and identifier type); the mode is ignored.
338  *              Note that these routines are really only useful for sorting
339  *              AclItems into identifier order.
340  *
341  * RETURNS:
342  *              a boolean value indicating = or >
343  */
344 static bool
345 aclitemeq(AclItem *a1, AclItem *a2)
346 {
347         return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id;
348 }
349
350 static bool
351 aclitemgt(AclItem *a1, AclItem *a2)
352 {
353         return ((a1->ai_idtype > a2->ai_idtype) ||
354                         (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
355 }
356
357
358 /*
359  * acldefault()  --- create an ACL describing default access permissions
360  *
361  * Change this routine if you want to alter the default access policy for
362  * newly-created tables (or any table with a NULL acl entry in pg_class)
363  */
364 Acl *
365 acldefault(char *relname, AclId ownerid)
366 {
367         Acl                *acl;
368         AclItem    *aip;
369
370 #define ACL_WORLD_DEFAULT               (ACL_NO)
371 /* #define              ACL_WORLD_DEFAULT               (ACL_RD|ACL_WR|ACL_AP|ACL_RU) */
372 #define ACL_OWNER_DEFAULT               (ACL_RD|ACL_WR|ACL_AP|ACL_RU)
373
374         acl = makeacl(2);
375         aip = ACL_DAT(acl);
376         aip[0].ai_idtype = ACL_IDTYPE_WORLD;
377         aip[0].ai_id = ACL_ID_WORLD;
378         aip[0].ai_mode = IsSystemRelationName(relname) ? ACL_RD : ACL_WORLD_DEFAULT;
379         aip[1].ai_idtype = ACL_IDTYPE_UID;
380         aip[1].ai_id = ownerid;
381         aip[1].ai_mode = ACL_OWNER_DEFAULT;
382         return acl;
383 }
384
385
386 /*
387  * Add or replace an item in an ACL array.
388  *
389  * NB: caller is responsible for having detoasted the input ACL, if needed.
390  */
391 Acl *
392 aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg)
393 {
394         Acl                *new_acl;
395         AclItem    *old_aip,
396                            *new_aip;
397         int                     src,
398                                 dst,
399                                 num;
400
401         /* These checks for null input are probably dead code, but... */
402         if (!old_acl || ACL_NUM(old_acl) < 1)
403                 old_acl = makeacl(1);
404         if (!mod_aip)
405         {
406                 new_acl = makeacl(ACL_NUM(old_acl));
407                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
408                 return new_acl;
409         }
410
411         num = ACL_NUM(old_acl);
412         old_aip = ACL_DAT(old_acl);
413
414         /*
415          * Search the ACL for an existing entry for 'id'.  If one exists, just
416          * modify the entry in-place (well, in the same position, since we
417          * actually return a copy); otherwise, insert the new entry in
418          * sort-order.
419          */
420         /* find the first element not less than the element to be inserted */
421         for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
422                 ;
423
424         if (dst < num && aclitemeq(mod_aip, old_aip + dst))
425         {
426                 /* modify in-place */
427                 new_acl = makeacl(num);
428                 new_aip = ACL_DAT(new_acl);
429                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
430                 src = dst;
431         }
432         else
433         {
434                 new_acl = makeacl(num + 1);
435                 new_aip = ACL_DAT(new_acl);
436                 if (dst == 0)
437                 {                                               /* start */
438                         elog(ERROR, "aclinsert3: insertion before world ACL??");
439                 }
440                 else if (dst >= num)
441                 {                                               /* end */
442                         memcpy((char *) new_aip,
443                                    (char *) old_aip,
444                                    num * sizeof(AclItem));
445                 }
446                 else
447                 {                                               /* middle */
448                         memcpy((char *) new_aip,
449                                    (char *) old_aip,
450                                    dst * sizeof(AclItem));
451                         memcpy((char *) (new_aip + dst + 1),
452                                    (char *) (old_aip + dst),
453                                    (num - dst) * sizeof(AclItem));
454                 }
455                 new_aip[dst].ai_id = mod_aip->ai_id;
456                 new_aip[dst].ai_idtype = mod_aip->ai_idtype;
457                 num++;                                  /* set num to the size of new_acl */
458                 src = 0;                                /* if add or del, start from world entry */
459         }
460
461         /* apply the permissions mod */
462         switch (modechg)
463         {
464                 case ACL_MODECHG_ADD:
465                         new_aip[dst].ai_mode = old_aip[src].ai_mode | mod_aip->ai_mode;
466                         break;
467                 case ACL_MODECHG_DEL:
468                         new_aip[dst].ai_mode = old_aip[src].ai_mode & ~mod_aip->ai_mode;
469                         break;
470                 case ACL_MODECHG_EQL:
471                         new_aip[dst].ai_mode = mod_aip->ai_mode;
472                         break;
473         }
474
475         /*
476          * if the adjusted entry has no permissions, delete it from the
477          * list.  For example, this helps in removing entries for users who no
478          * longer exist.  EXCEPTION: never remove the world entry.
479          */
480         if (new_aip[dst].ai_mode == 0 && dst > 0)
481         {
482                 int                     i;
483
484                 for (i = dst + 1; i < num; i++)
485                 {
486                         new_aip[i - 1].ai_id = new_aip[i].ai_id;
487                         new_aip[i - 1].ai_idtype = new_aip[i].ai_idtype;
488                         new_aip[i - 1].ai_mode = new_aip[i].ai_mode;
489                 }
490                 ARR_DIMS(new_acl)[0] = num - 1;
491                 /* Adjust also the array size because it is used for memcpy */
492                 ARR_SIZE(new_acl) -= sizeof(AclItem);
493         }
494
495         return new_acl;
496 }
497
498 /*
499  * aclinsert (exported function)
500  */
501 Datum
502 aclinsert(PG_FUNCTION_ARGS)
503 {
504         Acl                *old_acl = PG_GETARG_ACL_P(0);
505         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
506
507         PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
508 }
509
510 Datum
511 aclremove(PG_FUNCTION_ARGS)
512 {
513         Acl                *old_acl = PG_GETARG_ACL_P(0);
514         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
515         Acl                *new_acl;
516         AclItem    *old_aip,
517                            *new_aip;
518         int                     dst,
519                                 old_num,
520                                 new_num;
521
522         /* These checks for null input should be dead code, but... */
523         if (!old_acl || ACL_NUM(old_acl) < 1)
524                 old_acl = makeacl(1);
525         if (!mod_aip)
526         {
527                 new_acl = makeacl(ACL_NUM(old_acl));
528                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
529                 PG_RETURN_ACL_P(new_acl);
530         }
531
532         old_num = ACL_NUM(old_acl);
533         old_aip = ACL_DAT(old_acl);
534
535         /* Search for the matching entry */
536         for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
537                 ;
538
539         if (dst >= old_num)
540         {
541                 /* Not found, so return copy of source ACL */
542                 new_acl = makeacl(old_num);
543                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
544         }
545         else
546         {
547                 new_num = old_num - 1;
548                 new_acl = makeacl(new_num);
549                 new_aip = ACL_DAT(new_acl);
550                 if (dst == 0)
551                 {                                               /* start */
552                         elog(ERROR, "aclremove: removal of the world ACL??");
553                 }
554                 else if (dst == old_num - 1)
555                 {                                               /* end */
556                         memcpy((char *) new_aip,
557                                    (char *) old_aip,
558                                    new_num * sizeof(AclItem));
559                 }
560                 else
561                 {                                               /* middle */
562                         memcpy((char *) new_aip,
563                                    (char *) old_aip,
564                                    dst * sizeof(AclItem));
565                         memcpy((char *) (new_aip + dst),
566                                    (char *) (old_aip + dst + 1),
567                                    (new_num - dst) * sizeof(AclItem));
568                 }
569         }
570
571         PG_RETURN_ACL_P(new_acl);
572 }
573
574 Datum
575 aclcontains(PG_FUNCTION_ARGS)
576 {
577         Acl                *acl = PG_GETARG_ACL_P(0);
578         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
579         AclItem    *aidat;
580         int                     i,
581                                 num;
582
583         num = ACL_NUM(acl);
584         aidat = ACL_DAT(acl);
585         for (i = 0; i < num; ++i)
586         {
587                 /* Note that aclitemeq only considers id, not mode */
588                 if (aclitemeq(aip, aidat + i) &&
589                         aip->ai_mode == aidat[i].ai_mode)
590                         PG_RETURN_BOOL(true);
591         }
592         PG_RETURN_BOOL(false);
593 }
594
595 /*
596  * ExecuteChangeACLStmt
597  *              Called to execute the utility command type ChangeACLStmt
598  */
599 void
600 ExecuteChangeACLStmt(ChangeACLStmt *stmt)
601 {
602         AclItem         aclitem;
603         unsigned        modechg;
604         List       *i;
605
606         /* see comment in pg_type.h */
607         Assert(ACLITEMSIZE == sizeof(AclItem));
608
609         /* Convert string ACL spec into internal form */
610         aclparse(stmt->aclString, &aclitem, &modechg);
611
612         foreach(i, stmt->relNames)
613         {
614                 char       *relname = strVal(lfirst(i));
615                 Relation        rel;
616
617                 rel = heap_openr(relname, AccessExclusiveLock);
618                 if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
619                         elog(ERROR, "\"%s\" is an index relation",
620                                  relname);
621                 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
622                         elog(ERROR, "you do not own class \"%s\"",
623                                  relname);
624                 ChangeAcl(relname, &aclitem, modechg);
625                 /* close rel, but keep lock until end of xact */
626                 heap_close(rel, NoLock);
627         }
628 }
629
630
631 /*
632  * Parser support routines for ACL-related statements.
633  *
634  * XXX CAUTION: these are called from gram.y, which is not allowed to
635  * do any table accesses.  Therefore, it is not kosher to do things
636  * like trying to translate usernames to user IDs here.  Keep it all
637  * in string form until statement execution time.
638  */
639
640 /*
641  * aclmakepriv
642  *        make a acl privilege string out of an existing privilege string
643  * and a new privilege
644  *
645  * does not add duplicate privileges
646  */
647 char *
648 aclmakepriv(char *old_privlist, char new_priv)
649 {
650         char       *priv;
651         int                     i;
652         int                     l;
653
654         Assert(strlen(old_privlist) < 5);
655         priv = palloc(5); /* at most "rwaR" */ ;
656
657         if (old_privlist == NULL || old_privlist[0] == '\0')
658         {
659                 priv[0] = new_priv;
660                 priv[1] = '\0';
661                 return priv;
662         }
663
664         strcpy(priv, old_privlist);
665
666         l = strlen(old_privlist);
667
668         if (l == 4)
669         {                                                       /* can't add any more privileges */
670                 return priv;
671         }
672
673         /* check to see if the new privilege is already in the old string */
674         for (i = 0; i < l; i++)
675         {
676                 if (priv[i] == new_priv)
677                         break;
678         }
679         if (i == l)
680         {                                                       /* we really have a new privilege */
681                 priv[l] = new_priv;
682                 priv[l + 1] = '\0';
683         }
684
685         return priv;
686 }
687
688 /*
689  * aclmakeuser
690  *        user_type must be "A"  - all users
691  *                                              "G"  - group
692  *                                              "U"  - user
693  *
694  * Just concatenates the two strings together with a space in between.
695  * Per above comments, we can't try to resolve a user or group name here.
696  */
697 char *
698 aclmakeuser(char *user_type, char *user)
699 {
700         char       *user_list;
701
702         user_list = palloc(strlen(user_type) + strlen(user) + 2);
703         sprintf(user_list, "%s %s", user_type, user);
704         return user_list;
705 }
706
707 /*
708  * makeAclStmt:
709  *        create a ChangeACLStmt at parse time.
710  *        we take in the privileges, relation_name_list, and grantee
711  *        as well as a single character '+' or '-' to indicate grant or revoke
712  *
713  * We convert the information to the same external form recognized by
714  * aclitemin (see aclparse), and save that string in the ChangeACLStmt.
715  * Conversion to internal form happens when the statement is executed.
716  */
717 ChangeACLStmt *
718 makeAclStmt(char *privileges, List *rel_list, char *grantee,
719                         char grant_or_revoke)
720 {
721         ChangeACLStmt *n = makeNode(ChangeACLStmt);
722         StringInfoData str;
723
724         initStringInfo(&str);
725
726         /* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
727         if (grantee[0] == 'G')          /* group permissions */
728         {
729                 appendStringInfo(&str, "%s \"%s\"%c%s",
730                                                  ACL_IDTYPE_GID_KEYWORD,
731                                                  grantee + 2, grant_or_revoke, privileges);
732         }
733         else if (grantee[0] == 'U') /* user permission */
734         {
735                 appendStringInfo(&str, "%s \"%s\"%c%s",
736                                                  ACL_IDTYPE_UID_KEYWORD,
737                                                  grantee + 2, grant_or_revoke, privileges);
738         }
739         else
740         {
741                 /* all permission */
742                 appendStringInfo(&str, "%c%s",
743                                                  grant_or_revoke, privileges);
744         }
745         n->relNames = rel_list;
746         n->aclString = pstrdup(str.data);
747
748         pfree(str.data);
749         return n;
750 }