]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Fix some corner cases in ACL manipulation: don't foul up on an empty
[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-2003, 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.100 2003/10/29 22:20:54 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "catalog/namespace.h"
20 #include "catalog/pg_shadow.h"
21 #include "catalog/pg_type.h"
22 #include "commands/dbcommands.h"
23 #include "miscadmin.h"
24 #include "utils/acl.h"
25 #include "utils/builtins.h"
26 #include "utils/lsyscache.h"
27 #include "utils/syscache.h"
28
29
30 #define ACL_IDTYPE_GID_KEYWORD  "group"
31 #define ACL_IDTYPE_UID_KEYWORD  "user"
32
33 static const char *getid(const char *s, char *n);
34 static void putid(char *p, const char *s);
35 static Acl *allocacl(int n);
36 static const char *aclparse(const char *s, AclItem *aip);
37 static bool aclitem_match(const AclItem *a1, const AclItem *a2);
38 static Acl *recursive_revoke(Acl *acl, AclId grantee,
39                                  AclMode revoke_privs, DropBehavior behavior);
40
41 static AclMode convert_priv_string(text *priv_type_text);
42
43 static Oid      convert_table_name(text *tablename);
44 static AclMode convert_table_priv_string(text *priv_type_text);
45 static Oid      convert_database_name(text *databasename);
46 static AclMode convert_database_priv_string(text *priv_type_text);
47 static Oid      convert_function_name(text *functionname);
48 static AclMode convert_function_priv_string(text *priv_type_text);
49 static Oid      convert_language_name(text *languagename);
50 static AclMode convert_language_priv_string(text *priv_type_text);
51 static Oid      convert_schema_name(text *schemaname);
52 static AclMode convert_schema_priv_string(text *priv_type_text);
53
54
55 /*
56  * getid
57  *              Consumes the first alphanumeric string (identifier) found in string
58  *              's', ignoring any leading white space.  If it finds a double quote
59  *              it returns the word inside the quotes.
60  *
61  * RETURNS:
62  *              the string position in 's' that points to the next non-space character
63  *              in 's', after any quotes.  Also:
64  *              - loads the identifier into 'n'.  (If no identifier is found, 'n'
65  *                contains an empty string.)  'n' must be NAMEDATALEN bytes.
66  */
67 static const char *
68 getid(const char *s, char *n)
69 {
70         int                     len = 0;
71         bool            in_quotes = false;
72
73         Assert(s && n);
74
75         while (isspace((unsigned char) *s))
76                 s++;
77         /* This code had better match what putid() does, below */
78         for (;
79                  *s != '\0' &&
80                  (isalnum((unsigned char) *s) ||
81                   *s == '_' ||
82                   *s == '"' ||
83                   in_quotes);
84                  s++)
85         {
86                 if (*s == '"')
87                 {
88                         /* safe to look at next char (could be '\0' though) */
89                         if (*(s + 1) != '"')
90                         {
91                                 in_quotes = !in_quotes;
92                                 continue;
93                         }
94                         /* it's an escaped double quote; skip the escaping char */
95                         s++;
96                 }
97
98                 /* Add the character to the string */
99                 if (len >= NAMEDATALEN - 1)
100                         ereport(ERROR,
101                                         (errcode(ERRCODE_NAME_TOO_LONG),
102                                          errmsg("identifier too long"),
103                                          errdetail("Identifier must be less than %d characters.",
104                                                            NAMEDATALEN)));
105
106                 n[len++] = *s;
107         }
108         n[len] = '\0';
109         while (isspace((unsigned char) *s))
110                 s++;
111         return s;
112 }
113
114 /*
115  * Write a user or group Name at *p, adding double quotes if needed.
116  * There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
117  * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c
118  */
119 static void
120 putid(char *p, const char *s)
121 {
122         const char *src;
123         bool            safe = true;
124
125         for (src = s; *src; src++)
126         {
127                 /* This test had better match what getid() does, above */
128                 if (!isalnum((unsigned char) *src) && *src != '_')
129                 {
130                         safe = false;
131                         break;
132                 }
133         }
134         if (!safe)
135                 *p++ = '"';
136         for (src = s; *src; src++)
137         {
138                 /* A double quote character in a username is encoded as "" */
139                 if (*src == '"')
140                         *p++ = '"';
141                 *p++ = *src;
142         }
143         if (!safe)
144                 *p++ = '"';
145         *p = '\0';
146 }
147
148 /*
149  * aclparse
150  *              Consumes and parses an ACL specification of the form:
151  *                              [group|user] [A-Za-z0-9]*=[rwaR]*
152  *              from string 's', ignoring any leading white space or white space
153  *              between the optional id type keyword (group|user) and the actual
154  *              ACL specification.
155  *
156  *              This routine is called by the parser as well as aclitemin(), hence
157  *              the added generality.
158  *
159  * RETURNS:
160  *              the string position in 's' immediately following the ACL
161  *              specification.  Also:
162  *              - loads the structure pointed to by 'aip' with the appropriate
163  *                UID/GID, id type identifier and mode type values.
164  */
165 static const char *
166 aclparse(const char *s, AclItem *aip)
167 {
168         AclMode         privs,
169                                 goption,
170                                 read;
171         uint32          idtype;
172         char            name[NAMEDATALEN];
173         char            name2[NAMEDATALEN];
174
175         Assert(s && aip);
176
177 #ifdef ACLDEBUG
178         elog(LOG, "aclparse: input = \"%s\"", s);
179 #endif
180         idtype = ACL_IDTYPE_UID;
181         s = getid(s, name);
182         if (*s != '=')
183         {
184                 /* we just read a keyword, not a name */
185                 if (strcmp(name, ACL_IDTYPE_GID_KEYWORD) == 0)
186                         idtype = ACL_IDTYPE_GID;
187                 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD) != 0)
188                         ereport(ERROR,
189                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
190                                          errmsg("unrecognized key word: \"%s\"", name),
191                                  errhint("ACL key word must be \"group\" or \"user\".")));
192                 s = getid(s, name);             /* move s to the name beyond the keyword */
193                 if (name[0] == '\0')
194                         ereport(ERROR,
195                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
196                                          errmsg("missing name"),
197                            errhint("A name must follow the \"group\" or \"user\" key word.")));
198         }
199         if (name[0] == '\0')
200                 idtype = ACL_IDTYPE_WORLD;
201
202         if (*s != '=')
203                 ereport(ERROR,
204                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
205                                  errmsg("missing \"=\" sign")));
206
207         privs = goption = ACL_NO_RIGHTS;
208
209         for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
210         {
211                 switch (*s)
212                 {
213                         case '*':
214                                 goption |= read;
215                                 break;
216                         case ACL_INSERT_CHR:
217                                 read = ACL_INSERT;
218                                 break;
219                         case ACL_SELECT_CHR:
220                                 read = ACL_SELECT;
221                                 break;
222                         case ACL_UPDATE_CHR:
223                                 read = ACL_UPDATE;
224                                 break;
225                         case ACL_DELETE_CHR:
226                                 read = ACL_DELETE;
227                                 break;
228                         case ACL_RULE_CHR:
229                                 read = ACL_RULE;
230                                 break;
231                         case ACL_REFERENCES_CHR:
232                                 read = ACL_REFERENCES;
233                                 break;
234                         case ACL_TRIGGER_CHR:
235                                 read = ACL_TRIGGER;
236                                 break;
237                         case ACL_EXECUTE_CHR:
238                                 read = ACL_EXECUTE;
239                                 break;
240                         case ACL_USAGE_CHR:
241                                 read = ACL_USAGE;
242                                 break;
243                         case ACL_CREATE_CHR:
244                                 read = ACL_CREATE;
245                                 break;
246                         case ACL_CREATE_TEMP_CHR:
247                                 read = ACL_CREATE_TEMP;
248                                 break;
249                         default:
250                                 ereport(ERROR,
251                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
252                                   errmsg("invalid mode character: must be one of \"%s\"",
253                                                  ACL_ALL_RIGHTS_STR)));
254                 }
255
256                 privs |= read;
257         }
258
259         switch (idtype)
260         {
261                 case ACL_IDTYPE_UID:
262                         aip->ai_grantee = get_usesysid(name);
263                         break;
264                 case ACL_IDTYPE_GID:
265                         aip->ai_grantee = get_grosysid(name);
266                         break;
267                 case ACL_IDTYPE_WORLD:
268                         aip->ai_grantee = ACL_ID_WORLD;
269                         break;
270         }
271
272         /*
273          * XXX Allow a degree of backward compatibility by defaulting the
274          * grantor to the superuser.
275          */
276         if (*s == '/')
277         {
278                 s = getid(s + 1, name2);
279                 if (name2[0] == '\0')
280                         ereport(ERROR,
281                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
282                                          errmsg("a name must follow the \"/\" sign")));
283
284                 aip->ai_grantor = get_usesysid(name2);
285         }
286         else
287         {
288                 aip->ai_grantor = BOOTSTRAP_USESYSID;
289                 ereport(WARNING,
290                                 (errcode(ERRCODE_INVALID_GRANTOR),
291                                  errmsg("defaulting grantor to user ID %u", BOOTSTRAP_USESYSID)));
292         }
293
294         ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, goption, idtype);
295
296 #ifdef ACLDEBUG
297         elog(LOG, "aclparse: correctly read [%x %d %x]",
298                  idtype, aip->ai_grantee, privs);
299 #endif
300         return s;
301 }
302
303 /*
304  * allocacl
305  *              Allocates storage for a new Acl with 'n' entries.
306  *
307  * RETURNS:
308  *              the new Acl
309  */
310 static Acl *
311 allocacl(int n)
312 {
313         Acl                *new_acl;
314         Size            size;
315
316         if (n < 0)
317                 elog(ERROR, "invalid size: %d", n);
318         size = ACL_N_SIZE(n);
319         new_acl = (Acl *) palloc0(size);
320         new_acl->size = size;
321         new_acl->ndim = 1;
322         new_acl->flags = 0;
323         new_acl->elemtype = ACLITEMOID;
324         ARR_LBOUND(new_acl)[0] = 0;
325         ARR_DIMS(new_acl)[0] = n;
326         return new_acl;
327 }
328
329 /*
330  * aclitemin
331  *              Allocates storage for, and fills in, a new AclItem given a string
332  *              's' that contains an ACL specification.  See aclparse for details.
333  *
334  * RETURNS:
335  *              the new AclItem
336  */
337 Datum
338 aclitemin(PG_FUNCTION_ARGS)
339 {
340         const char *s = PG_GETARG_CSTRING(0);
341         AclItem    *aip;
342
343         aip = (AclItem *) palloc(sizeof(AclItem));
344         s = aclparse(s, aip);
345         while (isspace((unsigned char) *s))
346                 ++s;
347         if (*s)
348                 ereport(ERROR,
349                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
350                    errmsg("extra garbage at the end of the ACL specification")));
351
352         PG_RETURN_ACLITEM_P(aip);
353 }
354
355 /*
356  * aclitemout
357  *              Allocates storage for, and fills in, a new null-delimited string
358  *              containing a formatted ACL specification.  See aclparse for details.
359  *
360  * RETURNS:
361  *              the new string
362  */
363 Datum
364 aclitemout(PG_FUNCTION_ARGS)
365 {
366         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
367         char       *p;
368         char       *out;
369         HeapTuple       htup;
370         unsigned        i;
371         char       *tmpname;
372
373         out = palloc(strlen("group =/") +
374                                  2 * N_ACL_RIGHTS +
375                                  2 * (2 * NAMEDATALEN + 2) +
376                                  1);
377
378         p = out;
379         *p = '\0';
380
381         switch (ACLITEM_GET_IDTYPE(*aip))
382         {
383                 case ACL_IDTYPE_UID:
384                         htup = SearchSysCache(SHADOWSYSID,
385                                                                   ObjectIdGetDatum(aip->ai_grantee),
386                                                                   0, 0, 0);
387                         if (HeapTupleIsValid(htup))
388                         {
389                                 putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
390                                 ReleaseSysCache(htup);
391                         }
392                         else
393                         {
394                                 /* Generate numeric UID if we don't find an entry */
395                                 sprintf(p, "%d", aip->ai_grantee);
396                         }
397                         break;
398                 case ACL_IDTYPE_GID:
399                         strcpy(p, "group ");
400                         p += strlen(p);
401                         tmpname = get_groname(aip->ai_grantee);
402                         if (tmpname != NULL)
403                                 putid(p, tmpname);
404                         else
405                         {
406                                 /* Generate numeric GID if we don't find an entry */
407                                 sprintf(p, "%d", aip->ai_grantee);
408                         }
409                         break;
410                 case ACL_IDTYPE_WORLD:
411                         break;
412                 default:
413                         elog(ERROR, "unrecognized idtype: %d",
414                                  (int) ACLITEM_GET_IDTYPE(*aip));
415                         break;
416         }
417         while (*p)
418                 ++p;
419
420         *p++ = '=';
421
422         for (i = 0; i < N_ACL_RIGHTS; ++i)
423         {
424                 if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
425                         *p++ = ACL_ALL_RIGHTS_STR[i];
426                 if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
427                         *p++ = '*';
428         }
429
430         *p++ = '/';
431         *p = '\0';
432
433         htup = SearchSysCache(SHADOWSYSID,
434                                                   ObjectIdGetDatum(aip->ai_grantor),
435                                                   0, 0, 0);
436         if (HeapTupleIsValid(htup))
437         {
438                 putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
439                 ReleaseSysCache(htup);
440         }
441         else
442         {
443                 /* Generate numeric UID if we don't find an entry */
444                 sprintf(p, "%d", aip->ai_grantor);
445         }
446
447         while (*p)
448                 ++p;
449         *p = '\0';
450
451         PG_RETURN_CSTRING(out);
452 }
453
454 /*
455  * aclitem_match
456  *              Two AclItems are considered to match iff they have the same
457  *              grantee and grantor; the privileges are ignored.
458  */
459 static bool
460 aclitem_match(const AclItem *a1, const AclItem *a2)
461 {
462         return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
463                 a1->ai_grantee == a2->ai_grantee &&
464                 a1->ai_grantor == a2->ai_grantor;
465 }
466
467 /*
468  * aclitem equality operator
469  */
470 Datum
471 aclitem_eq(PG_FUNCTION_ARGS)
472 {
473         AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
474         AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
475         bool            result;
476
477         result = a1->ai_privs == a2->ai_privs &&
478                 a1->ai_grantee == a2->ai_grantee &&
479                 a1->ai_grantor == a2->ai_grantor;
480         PG_RETURN_BOOL(result);
481 }
482
483 /*
484  * aclitem hash function
485  *
486  * We make aclitems hashable not so much because anyone is likely to hash
487  * them, as because we want array equality to work on aclitem arrays, and
488  * with the typcache mechanism we must have a hash or btree opclass.
489  */
490 Datum
491 hash_aclitem(PG_FUNCTION_ARGS)
492 {
493         AclItem    *a = PG_GETARG_ACLITEM_P(0);
494
495         /* not very bright, but avoids any issue of padding in struct */
496         PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
497 }
498
499
500 /*
501  * acldefault()  --- create an ACL describing default access permissions
502  *
503  * Change this routine if you want to alter the default access policy for
504  * newly-created objects (or any object with a NULL acl entry).
505  */
506 Acl *
507 acldefault(GrantObjectType objtype, AclId ownerid)
508 {
509         AclMode         world_default;
510         AclMode         owner_default;
511         Acl                *acl;
512         AclItem    *aip;
513
514         switch (objtype)
515         {
516                 case ACL_OBJECT_RELATION:
517                         world_default = ACL_NO_RIGHTS;
518                         owner_default = ACL_ALL_RIGHTS_RELATION;
519                         break;
520                 case ACL_OBJECT_DATABASE:
521                         world_default = ACL_CREATE_TEMP;        /* not NO_RIGHTS! */
522                         owner_default = ACL_ALL_RIGHTS_DATABASE;
523                         break;
524                 case ACL_OBJECT_FUNCTION:
525                         /* Grant EXECUTE by default, for now */
526                         world_default = ACL_EXECUTE;
527                         owner_default = ACL_ALL_RIGHTS_FUNCTION;
528                         break;
529                 case ACL_OBJECT_LANGUAGE:
530                         /* Grant USAGE by default, for now */
531                         world_default = ACL_USAGE;
532                         owner_default = ACL_ALL_RIGHTS_LANGUAGE;
533                         break;
534                 case ACL_OBJECT_NAMESPACE:
535                         world_default = ACL_NO_RIGHTS;
536                         owner_default = ACL_ALL_RIGHTS_NAMESPACE;
537                         break;
538                 default:
539                         elog(ERROR, "unrecognized objtype: %d", (int) objtype);
540                         world_default = ACL_NO_RIGHTS;          /* keep compiler quiet */
541                         owner_default = ACL_NO_RIGHTS;
542                         break;
543         }
544
545         acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1);
546         aip = ACL_DAT(acl);
547
548         if (world_default != ACL_NO_RIGHTS)
549         {
550                 aip->ai_grantee = ACL_ID_WORLD;
551                 aip->ai_grantor = ownerid;
552                 ACLITEM_SET_PRIVS_IDTYPE(*aip, world_default, ACL_NO_RIGHTS,
553                                                                  ACL_IDTYPE_WORLD);
554                 aip++;
555         }
556
557         aip->ai_grantee = ownerid;
558         aip->ai_grantor = ownerid;
559         /* owner gets default privileges with grant option */
560         ACLITEM_SET_PRIVS_IDTYPE(*aip, owner_default, owner_default,
561                                                          ACL_IDTYPE_UID);
562
563         return acl;
564 }
565
566
567 /*
568  * Add or replace an item in an ACL array.      The result is a modified copy;
569  * the input object is not changed.
570  *
571  * NB: caller is responsible for having detoasted the input ACL, if needed.
572  */
573 Acl *
574 aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
575                    unsigned modechg, DropBehavior behavior)
576 {
577         Acl                *new_acl = NULL;
578         AclItem    *old_aip,
579                            *new_aip = NULL;
580         AclMode         old_privs,
581                                 old_goptions,
582                                 new_privs,
583                                 new_goptions;
584         int                     dst,
585                                 num;
586
587         /* These checks for null input are probably dead code, but... */
588         if (!old_acl || ACL_NUM(old_acl) < 0)
589                 old_acl = allocacl(0);
590         if (!mod_aip)
591         {
592                 new_acl = allocacl(ACL_NUM(old_acl));
593                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
594                 return new_acl;
595         }
596
597         num = ACL_NUM(old_acl);
598         old_aip = ACL_DAT(old_acl);
599
600         /*
601          * Search the ACL for an existing entry for this grantee and grantor.
602          * If one exists, just modify the entry in-place (well, in the same
603          * position, since we actually return a copy); otherwise, insert the
604          * new entry at the end.
605          */
606
607         for (dst = 0; dst < num; ++dst)
608         {
609                 if (aclitem_match(mod_aip, old_aip + dst))
610                 {
611                         /* found a match, so modify existing item */
612                         new_acl = allocacl(num);
613                         new_aip = ACL_DAT(new_acl);
614                         memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
615                         break;
616                 }
617         }
618
619         if (dst == num)
620         {
621                 /* need to append a new item */
622                 new_acl = allocacl(num + 1);
623                 new_aip = ACL_DAT(new_acl);
624                 memcpy(new_aip, old_aip, num * sizeof(AclItem));
625
626                 /* initialize the new entry with no permissions */
627                 new_aip[dst].ai_grantee = mod_aip->ai_grantee;
628                 new_aip[dst].ai_grantor = mod_aip->ai_grantor;
629                 ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS, ACL_NO_RIGHTS,
630                                                                  ACLITEM_GET_IDTYPE(*mod_aip));
631                 num++;                                  /* set num to the size of new_acl */
632         }
633
634         old_privs = ACLITEM_GET_PRIVS(new_aip[dst]);
635         old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
636
637         /* apply the specified permissions change */
638         switch (modechg)
639         {
640                 case ACL_MODECHG_ADD:
641                         ACLITEM_SET_PRIVS(new_aip[dst],
642                                                           old_privs | ACLITEM_GET_PRIVS(*mod_aip));
643                         ACLITEM_SET_GOPTIONS(new_aip[dst],
644                                                                  old_goptions | ACLITEM_GET_GOPTIONS(*mod_aip));
645                         break;
646                 case ACL_MODECHG_DEL:
647                         ACLITEM_SET_PRIVS(new_aip[dst],
648                                                           old_privs & ~ACLITEM_GET_PRIVS(*mod_aip));
649                         ACLITEM_SET_GOPTIONS(new_aip[dst],
650                                                                  old_goptions & ~ACLITEM_GET_GOPTIONS(*mod_aip));
651                         break;
652                 case ACL_MODECHG_EQL:
653                         ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
654                                                                          ACLITEM_GET_PRIVS(*mod_aip),
655                                                                          ACLITEM_GET_GOPTIONS(*mod_aip),
656                                                                          ACLITEM_GET_IDTYPE(new_aip[dst]));
657                         break;
658         }
659
660         new_privs = ACLITEM_GET_PRIVS(new_aip[dst]);
661         new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
662
663         /*
664          * If the adjusted entry has no permissions, delete it from the list.
665          */
666         if (new_privs == ACL_NO_RIGHTS && new_goptions == ACL_NO_RIGHTS)
667         {
668                 memmove(new_aip + dst,
669                                 new_aip + dst + 1,
670                                 (num - dst - 1) * sizeof(AclItem));
671                 ARR_DIMS(new_acl)[0] = num - 1;
672                 ARR_SIZE(new_acl) -= sizeof(AclItem);
673         }
674
675         /*
676          * Remove abandoned privileges (cascading revoke).  Currently we
677          * can only handle this when the grantee is a user.
678          */
679         if ((old_goptions & ~new_goptions) != 0
680                 && ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID)
681                 new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
682                                                                    (old_goptions & ~new_goptions),
683                                                                    behavior);
684
685         return new_acl;
686 }
687
688
689 /*
690  * Ensure that no privilege is "abandoned".  A privilege is abandoned
691  * if the user that granted the privilege loses the grant option.  (So
692  * the chain through which it was granted is broken.)  Either the
693  * abandoned privileges are revoked as well, or an error message is
694  * printed, depending on the drop behavior option.
695  */
696 static Acl *
697 recursive_revoke(Acl *acl,
698                                  AclId grantee,
699                                  AclMode revoke_privs,
700                                  DropBehavior behavior)
701 {
702         int                     i;
703
704 restart:
705         for (i = 0; i < ACL_NUM(acl); i++)
706         {
707                 AclItem    *aip = ACL_DAT(acl);
708
709                 if (aip[i].ai_grantor == grantee
710                         && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
711                 {
712                         AclItem         mod_acl;
713
714                         if (behavior == DROP_RESTRICT)
715                                 ereport(ERROR,
716                                                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
717                                                  errmsg("dependent privileges exist"),
718                                                  errhint("Use CASCADE to revoke them too.")));
719
720                         mod_acl.ai_grantor = grantee;
721                         mod_acl.ai_grantee = aip[i].ai_grantee;
722                         ACLITEM_SET_PRIVS_IDTYPE(mod_acl,
723                                                                          revoke_privs,
724                                                                          revoke_privs,
725                                                                          ACLITEM_GET_IDTYPE(aip[i]));
726
727                         acl = aclinsert3(acl, &mod_acl, ACL_MODECHG_DEL, behavior);
728                         goto restart;
729                 }
730         }
731
732         return acl;
733 }
734
735
736 /*
737  * aclinsert (exported function)
738  */
739 Datum
740 aclinsert(PG_FUNCTION_ARGS)
741 {
742         Acl                *old_acl = PG_GETARG_ACL_P(0);
743         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
744
745         PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL, DROP_CASCADE));
746 }
747
748 Datum
749 aclremove(PG_FUNCTION_ARGS)
750 {
751         Acl                *old_acl = PG_GETARG_ACL_P(0);
752         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
753         Acl                *new_acl;
754         AclItem    *old_aip,
755                            *new_aip;
756         int                     dst,
757                                 old_num,
758                                 new_num;
759
760         /* These checks for null input should be dead code, but... */
761         if (!old_acl || ACL_NUM(old_acl) < 0)
762                 old_acl = allocacl(0);
763         if (!mod_aip)
764         {
765                 new_acl = allocacl(ACL_NUM(old_acl));
766                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
767                 PG_RETURN_ACL_P(new_acl);
768         }
769
770         old_num = ACL_NUM(old_acl);
771         old_aip = ACL_DAT(old_acl);
772
773         /* Search for the matching entry */
774         for (dst = 0;
775                  dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
776                  ++dst)
777                  /* continue */ ;
778
779         if (dst >= old_num)
780         {
781                 /* Not found, so return copy of source ACL */
782                 new_acl = allocacl(old_num);
783                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
784         }
785         else
786         {
787                 new_num = old_num - 1;
788                 new_acl = allocacl(new_num);
789                 new_aip = ACL_DAT(new_acl);
790                 if (dst > 0)
791                         memcpy((char *) new_aip,
792                                    (char *) old_aip,
793                                    dst * sizeof(AclItem));
794                 if (dst < new_num)
795                         memcpy((char *) (new_aip + dst),
796                                    (char *) (old_aip + dst + 1),
797                                    (new_num - dst) * sizeof(AclItem));
798         }
799
800         PG_RETURN_ACL_P(new_acl);
801 }
802
803 Datum
804 aclcontains(PG_FUNCTION_ARGS)
805 {
806         Acl                *acl = PG_GETARG_ACL_P(0);
807         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
808         AclItem    *aidat;
809         int                     i,
810                                 num;
811
812         num = ACL_NUM(acl);
813         aidat = ACL_DAT(acl);
814         for (i = 0; i < num; ++i)
815         {
816                 if (aip->ai_grantee == aidat[i].ai_grantee
817                         && ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
818                         && aip->ai_grantor == aidat[i].ai_grantor
819                         && (ACLITEM_GET_PRIVS(*aip) & ACLITEM_GET_PRIVS(aidat[i])) == ACLITEM_GET_PRIVS(*aip)
820                         && (ACLITEM_GET_GOPTIONS(*aip) & ACLITEM_GET_GOPTIONS(aidat[i])) == ACLITEM_GET_GOPTIONS(*aip))
821                         PG_RETURN_BOOL(true);
822         }
823         PG_RETURN_BOOL(false);
824 }
825
826 Datum
827 makeaclitem(PG_FUNCTION_ARGS)
828 {
829         int32           u_grantee = PG_GETARG_INT32(0);
830         int32           g_grantee = PG_GETARG_INT32(1);
831         int32           grantor = PG_GETARG_INT32(2);
832         text       *privtext = PG_GETARG_TEXT_P(3);
833         bool            goption = PG_GETARG_BOOL(4);
834         AclItem    *aclitem;
835         AclMode         priv;
836
837         priv = convert_priv_string(privtext);
838
839         aclitem = (AclItem *) palloc(sizeof(*aclitem));
840
841         if (u_grantee == 0 && g_grantee == 0)
842         {
843                 aclitem->ai_grantee = ACL_ID_WORLD;
844
845                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_WORLD);
846         }
847         else if (u_grantee != 0 && g_grantee != 0)
848         {
849                 ereport(ERROR,
850                                 (errcode(ERRCODE_DATA_EXCEPTION),
851                                  errmsg("cannot specify both user and group")));
852         }
853         else if (u_grantee != 0)
854         {
855                 aclitem->ai_grantee = u_grantee;
856
857                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_UID);
858         }
859         else /* (g_grantee != 0) */
860         {
861                 aclitem->ai_grantee = g_grantee;
862
863                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_GID);
864         }
865
866         aclitem->ai_grantor = grantor;
867
868         ACLITEM_SET_PRIVS(*aclitem, priv);
869         if (goption)
870                 ACLITEM_SET_GOPTIONS(*aclitem, priv);
871         else
872                 ACLITEM_SET_GOPTIONS(*aclitem, ACL_NO_RIGHTS);
873
874         PG_RETURN_ACLITEM_P(aclitem);
875 }
876
877 static AclMode
878 convert_priv_string(text *priv_type_text)
879 {
880         char       *priv_type;
881
882         priv_type = DatumGetCString(DirectFunctionCall1(textout,
883                                                                            PointerGetDatum(priv_type_text)));
884
885         if (strcasecmp(priv_type, "SELECT") == 0)
886                 return ACL_SELECT;
887         if (strcasecmp(priv_type, "INSERT") == 0)
888                 return ACL_INSERT;
889         if (strcasecmp(priv_type, "UPDATE") == 0)
890                 return ACL_UPDATE;
891         if (strcasecmp(priv_type, "DELETE") == 0)
892                 return ACL_DELETE;
893         if (strcasecmp(priv_type, "RULE") == 0)
894                 return ACL_RULE;
895         if (strcasecmp(priv_type, "REFERENCES") == 0)
896                 return ACL_REFERENCES;
897         if (strcasecmp(priv_type, "TRIGGER") == 0)
898                 return ACL_TRIGGER;
899         if (strcasecmp(priv_type, "EXECUTE") == 0)
900                 return ACL_EXECUTE;
901         if (strcasecmp(priv_type, "USAGE") == 0)
902                 return ACL_USAGE;
903         if (strcasecmp(priv_type, "CREATE") == 0)
904                 return ACL_CREATE;
905         if (strcasecmp(priv_type, "TEMP") == 0)
906                 return ACL_CREATE_TEMP;
907         if (strcasecmp(priv_type, "TEMPORARY") == 0)
908                 return ACL_CREATE_TEMP;
909
910         ereport(ERROR,
911                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
912                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
913         return ACL_NO_RIGHTS;           /* keep compiler quiet */
914 }
915
916
917 /*
918  * has_table_privilege variants
919  *              These are all named "has_table_privilege" at the SQL level.
920  *              They take various combinations of relation name, relation OID,
921  *              user name, user sysid, or implicit user = current_user.
922  *
923  *              The result is a boolean value: true if user has the indicated
924  *              privilege, false if not.
925  */
926
927 /*
928  * has_table_privilege_name_name
929  *              Check user privileges on a table given
930  *              name username, text tablename, and text priv name.
931  */
932 Datum
933 has_table_privilege_name_name(PG_FUNCTION_ARGS)
934 {
935         Name            username = PG_GETARG_NAME(0);
936         text       *tablename = PG_GETARG_TEXT_P(1);
937         text       *priv_type_text = PG_GETARG_TEXT_P(2);
938         int32           usesysid;
939         Oid                     tableoid;
940         AclMode         mode;
941         AclResult       aclresult;
942
943         usesysid = get_usesysid(NameStr(*username));
944         tableoid = convert_table_name(tablename);
945         mode = convert_table_priv_string(priv_type_text);
946
947         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
948
949         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
950 }
951
952 /*
953  * has_table_privilege_name
954  *              Check user privileges on a table given
955  *              text tablename and text priv name.
956  *              current_user is assumed
957  */
958 Datum
959 has_table_privilege_name(PG_FUNCTION_ARGS)
960 {
961         text       *tablename = PG_GETARG_TEXT_P(0);
962         text       *priv_type_text = PG_GETARG_TEXT_P(1);
963         AclId           usesysid;
964         Oid                     tableoid;
965         AclMode         mode;
966         AclResult       aclresult;
967
968         usesysid = GetUserId();
969         tableoid = convert_table_name(tablename);
970         mode = convert_table_priv_string(priv_type_text);
971
972         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
973
974         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
975 }
976
977 /*
978  * has_table_privilege_name_id
979  *              Check user privileges on a table given
980  *              name usename, table oid, and text priv name.
981  */
982 Datum
983 has_table_privilege_name_id(PG_FUNCTION_ARGS)
984 {
985         Name            username = PG_GETARG_NAME(0);
986         Oid                     tableoid = PG_GETARG_OID(1);
987         text       *priv_type_text = PG_GETARG_TEXT_P(2);
988         int32           usesysid;
989         AclMode         mode;
990         AclResult       aclresult;
991
992         usesysid = get_usesysid(NameStr(*username));
993         mode = convert_table_priv_string(priv_type_text);
994
995         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
996
997         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
998 }
999
1000 /*
1001  * has_table_privilege_id
1002  *              Check user privileges on a table given
1003  *              table oid, and text priv name.
1004  *              current_user is assumed
1005  */
1006 Datum
1007 has_table_privilege_id(PG_FUNCTION_ARGS)
1008 {
1009         Oid                     tableoid = PG_GETARG_OID(0);
1010         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1011         AclId           usesysid;
1012         AclMode         mode;
1013         AclResult       aclresult;
1014
1015         usesysid = GetUserId();
1016         mode = convert_table_priv_string(priv_type_text);
1017
1018         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1019
1020         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1021 }
1022
1023 /*
1024  * has_table_privilege_id_name
1025  *              Check user privileges on a table given
1026  *              usesysid, text tablename, and text priv name.
1027  */
1028 Datum
1029 has_table_privilege_id_name(PG_FUNCTION_ARGS)
1030 {
1031         int32           usesysid = PG_GETARG_INT32(0);
1032         text       *tablename = PG_GETARG_TEXT_P(1);
1033         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1034         Oid                     tableoid;
1035         AclMode         mode;
1036         AclResult       aclresult;
1037
1038         tableoid = convert_table_name(tablename);
1039         mode = convert_table_priv_string(priv_type_text);
1040
1041         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1042
1043         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1044 }
1045
1046 /*
1047  * has_table_privilege_id_id
1048  *              Check user privileges on a table given
1049  *              usesysid, table oid, and text priv name.
1050  */
1051 Datum
1052 has_table_privilege_id_id(PG_FUNCTION_ARGS)
1053 {
1054         int32           usesysid = PG_GETARG_INT32(0);
1055         Oid                     tableoid = PG_GETARG_OID(1);
1056         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1057         AclMode         mode;
1058         AclResult       aclresult;
1059
1060         mode = convert_table_priv_string(priv_type_text);
1061
1062         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1063
1064         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1065 }
1066
1067 /*
1068  *              Support routines for has_table_privilege family.
1069  */
1070
1071 /*
1072  * Given a table name expressed as a string, look it up and return Oid
1073  */
1074 static Oid
1075 convert_table_name(text *tablename)
1076 {
1077         RangeVar   *relrv;
1078
1079         relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename,
1080                                                                                                  "has_table_privilege"));
1081
1082         return RangeVarGetRelid(relrv, false);
1083 }
1084
1085 /*
1086  * convert_table_priv_string
1087  *              Convert text string to AclMode value.
1088  */
1089 static AclMode
1090 convert_table_priv_string(text *priv_type_text)
1091 {
1092         char       *priv_type;
1093
1094         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1095                                                                            PointerGetDatum(priv_type_text)));
1096
1097         /*
1098          * Return mode from priv_type string
1099          */
1100         if (strcasecmp(priv_type, "SELECT") == 0)
1101                 return ACL_SELECT;
1102         if (strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
1103                 return ACL_GRANT_OPTION_FOR(ACL_SELECT);
1104
1105         if (strcasecmp(priv_type, "INSERT") == 0)
1106                 return ACL_INSERT;
1107         if (strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
1108                 return ACL_GRANT_OPTION_FOR(ACL_INSERT);
1109
1110         if (strcasecmp(priv_type, "UPDATE") == 0)
1111                 return ACL_UPDATE;
1112         if (strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
1113                 return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
1114
1115         if (strcasecmp(priv_type, "DELETE") == 0)
1116                 return ACL_DELETE;
1117         if (strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
1118                 return ACL_GRANT_OPTION_FOR(ACL_DELETE);
1119
1120         if (strcasecmp(priv_type, "RULE") == 0)
1121                 return ACL_RULE;
1122         if (strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
1123                 return ACL_GRANT_OPTION_FOR(ACL_RULE);
1124
1125         if (strcasecmp(priv_type, "REFERENCES") == 0)
1126                 return ACL_REFERENCES;
1127         if (strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
1128                 return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
1129
1130         if (strcasecmp(priv_type, "TRIGGER") == 0)
1131                 return ACL_TRIGGER;
1132         if (strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
1133                 return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
1134
1135         ereport(ERROR,
1136                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1137                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1138         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1139 }
1140
1141
1142 /*
1143  * has_database_privilege variants
1144  *              These are all named "has_database_privilege" at the SQL level.
1145  *              They take various combinations of database name, database OID,
1146  *              user name, user sysid, or implicit user = current_user.
1147  *
1148  *              The result is a boolean value: true if user has the indicated
1149  *              privilege, false if not.
1150  */
1151
1152 /*
1153  * has_database_privilege_name_name
1154  *              Check user privileges on a database given
1155  *              name username, text databasename, and text priv name.
1156  */
1157 Datum
1158 has_database_privilege_name_name(PG_FUNCTION_ARGS)
1159 {
1160         Name            username = PG_GETARG_NAME(0);
1161         text       *databasename = PG_GETARG_TEXT_P(1);
1162         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1163         int32           usesysid;
1164         Oid                     databaseoid;
1165         AclMode         mode;
1166         AclResult       aclresult;
1167
1168         usesysid = get_usesysid(NameStr(*username));
1169         databaseoid = convert_database_name(databasename);
1170         mode = convert_database_priv_string(priv_type_text);
1171
1172         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1173
1174         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1175 }
1176
1177 /*
1178  * has_database_privilege_name
1179  *              Check user privileges on a database given
1180  *              text databasename and text priv name.
1181  *              current_user is assumed
1182  */
1183 Datum
1184 has_database_privilege_name(PG_FUNCTION_ARGS)
1185 {
1186         text       *databasename = PG_GETARG_TEXT_P(0);
1187         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1188         AclId           usesysid;
1189         Oid                     databaseoid;
1190         AclMode         mode;
1191         AclResult       aclresult;
1192
1193         usesysid = GetUserId();
1194         databaseoid = convert_database_name(databasename);
1195         mode = convert_database_priv_string(priv_type_text);
1196
1197         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1198
1199         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1200 }
1201
1202 /*
1203  * has_database_privilege_name_id
1204  *              Check user privileges on a database given
1205  *              name usename, database oid, and text priv name.
1206  */
1207 Datum
1208 has_database_privilege_name_id(PG_FUNCTION_ARGS)
1209 {
1210         Name            username = PG_GETARG_NAME(0);
1211         Oid                     databaseoid = PG_GETARG_OID(1);
1212         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1213         int32           usesysid;
1214         AclMode         mode;
1215         AclResult       aclresult;
1216
1217         usesysid = get_usesysid(NameStr(*username));
1218         mode = convert_database_priv_string(priv_type_text);
1219
1220         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1221
1222         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1223 }
1224
1225 /*
1226  * has_database_privilege_id
1227  *              Check user privileges on a database given
1228  *              database oid, and text priv name.
1229  *              current_user is assumed
1230  */
1231 Datum
1232 has_database_privilege_id(PG_FUNCTION_ARGS)
1233 {
1234         Oid                     databaseoid = PG_GETARG_OID(0);
1235         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1236         AclId           usesysid;
1237         AclMode         mode;
1238         AclResult       aclresult;
1239
1240         usesysid = GetUserId();
1241         mode = convert_database_priv_string(priv_type_text);
1242
1243         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1244
1245         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1246 }
1247
1248 /*
1249  * has_database_privilege_id_name
1250  *              Check user privileges on a database given
1251  *              usesysid, text databasename, and text priv name.
1252  */
1253 Datum
1254 has_database_privilege_id_name(PG_FUNCTION_ARGS)
1255 {
1256         int32           usesysid = PG_GETARG_INT32(0);
1257         text       *databasename = PG_GETARG_TEXT_P(1);
1258         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1259         Oid                     databaseoid;
1260         AclMode         mode;
1261         AclResult       aclresult;
1262
1263         databaseoid = convert_database_name(databasename);
1264         mode = convert_database_priv_string(priv_type_text);
1265
1266         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1267
1268         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1269 }
1270
1271 /*
1272  * has_database_privilege_id_id
1273  *              Check user privileges on a database given
1274  *              usesysid, database oid, and text priv name.
1275  */
1276 Datum
1277 has_database_privilege_id_id(PG_FUNCTION_ARGS)
1278 {
1279         int32           usesysid = PG_GETARG_INT32(0);
1280         Oid                     databaseoid = PG_GETARG_OID(1);
1281         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1282         AclMode         mode;
1283         AclResult       aclresult;
1284
1285         mode = convert_database_priv_string(priv_type_text);
1286
1287         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1288
1289         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1290 }
1291
1292 /*
1293  *              Support routines for has_database_privilege family.
1294  */
1295
1296 /*
1297  * Given a database name expressed as a string, look it up and return Oid
1298  */
1299 static Oid
1300 convert_database_name(text *databasename)
1301 {
1302         char       *dbname;
1303         Oid                     oid;
1304
1305         dbname = DatumGetCString(DirectFunctionCall1(textout,
1306                                                                                  PointerGetDatum(databasename)));
1307
1308         oid = get_database_oid(dbname);
1309         if (!OidIsValid(oid))
1310                 ereport(ERROR,
1311                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
1312                                  errmsg("database \"%s\" does not exist", dbname)));
1313
1314         return oid;
1315 }
1316
1317 /*
1318  * convert_database_priv_string
1319  *              Convert text string to AclMode value.
1320  */
1321 static AclMode
1322 convert_database_priv_string(text *priv_type_text)
1323 {
1324         char       *priv_type;
1325
1326         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1327                                                                            PointerGetDatum(priv_type_text)));
1328
1329         /*
1330          * Return mode from priv_type string
1331          */
1332         if (strcasecmp(priv_type, "CREATE") == 0)
1333                 return ACL_CREATE;
1334         if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1335                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1336
1337         if (strcasecmp(priv_type, "TEMPORARY") == 0)
1338                 return ACL_CREATE_TEMP;
1339         if (strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
1340                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1341
1342         if (strcasecmp(priv_type, "TEMP") == 0)
1343                 return ACL_CREATE_TEMP;
1344         if (strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
1345                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1346
1347         ereport(ERROR,
1348                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1349                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1350         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1351 }
1352
1353
1354 /*
1355  * has_function_privilege variants
1356  *              These are all named "has_function_privilege" at the SQL level.
1357  *              They take various combinations of function name, function OID,
1358  *              user name, user sysid, or implicit user = current_user.
1359  *
1360  *              The result is a boolean value: true if user has the indicated
1361  *              privilege, false if not.
1362  */
1363
1364 /*
1365  * has_function_privilege_name_name
1366  *              Check user privileges on a function given
1367  *              name username, text functionname, and text priv name.
1368  */
1369 Datum
1370 has_function_privilege_name_name(PG_FUNCTION_ARGS)
1371 {
1372         Name            username = PG_GETARG_NAME(0);
1373         text       *functionname = PG_GETARG_TEXT_P(1);
1374         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1375         int32           usesysid;
1376         Oid                     functionoid;
1377         AclMode         mode;
1378         AclResult       aclresult;
1379
1380         usesysid = get_usesysid(NameStr(*username));
1381         functionoid = convert_function_name(functionname);
1382         mode = convert_function_priv_string(priv_type_text);
1383
1384         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1385
1386         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1387 }
1388
1389 /*
1390  * has_function_privilege_name
1391  *              Check user privileges on a function given
1392  *              text functionname and text priv name.
1393  *              current_user is assumed
1394  */
1395 Datum
1396 has_function_privilege_name(PG_FUNCTION_ARGS)
1397 {
1398         text       *functionname = PG_GETARG_TEXT_P(0);
1399         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1400         AclId           usesysid;
1401         Oid                     functionoid;
1402         AclMode         mode;
1403         AclResult       aclresult;
1404
1405         usesysid = GetUserId();
1406         functionoid = convert_function_name(functionname);
1407         mode = convert_function_priv_string(priv_type_text);
1408
1409         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1410
1411         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1412 }
1413
1414 /*
1415  * has_function_privilege_name_id
1416  *              Check user privileges on a function given
1417  *              name usename, function oid, and text priv name.
1418  */
1419 Datum
1420 has_function_privilege_name_id(PG_FUNCTION_ARGS)
1421 {
1422         Name            username = PG_GETARG_NAME(0);
1423         Oid                     functionoid = PG_GETARG_OID(1);
1424         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1425         int32           usesysid;
1426         AclMode         mode;
1427         AclResult       aclresult;
1428
1429         usesysid = get_usesysid(NameStr(*username));
1430         mode = convert_function_priv_string(priv_type_text);
1431
1432         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1433
1434         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1435 }
1436
1437 /*
1438  * has_function_privilege_id
1439  *              Check user privileges on a function given
1440  *              function oid, and text priv name.
1441  *              current_user is assumed
1442  */
1443 Datum
1444 has_function_privilege_id(PG_FUNCTION_ARGS)
1445 {
1446         Oid                     functionoid = PG_GETARG_OID(0);
1447         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1448         AclId           usesysid;
1449         AclMode         mode;
1450         AclResult       aclresult;
1451
1452         usesysid = GetUserId();
1453         mode = convert_function_priv_string(priv_type_text);
1454
1455         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1456
1457         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1458 }
1459
1460 /*
1461  * has_function_privilege_id_name
1462  *              Check user privileges on a function given
1463  *              usesysid, text functionname, and text priv name.
1464  */
1465 Datum
1466 has_function_privilege_id_name(PG_FUNCTION_ARGS)
1467 {
1468         int32           usesysid = PG_GETARG_INT32(0);
1469         text       *functionname = PG_GETARG_TEXT_P(1);
1470         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1471         Oid                     functionoid;
1472         AclMode         mode;
1473         AclResult       aclresult;
1474
1475         functionoid = convert_function_name(functionname);
1476         mode = convert_function_priv_string(priv_type_text);
1477
1478         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1479
1480         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1481 }
1482
1483 /*
1484  * has_function_privilege_id_id
1485  *              Check user privileges on a function given
1486  *              usesysid, function oid, and text priv name.
1487  */
1488 Datum
1489 has_function_privilege_id_id(PG_FUNCTION_ARGS)
1490 {
1491         int32           usesysid = PG_GETARG_INT32(0);
1492         Oid                     functionoid = PG_GETARG_OID(1);
1493         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1494         AclMode         mode;
1495         AclResult       aclresult;
1496
1497         mode = convert_function_priv_string(priv_type_text);
1498
1499         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1500
1501         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1502 }
1503
1504 /*
1505  *              Support routines for has_function_privilege family.
1506  */
1507
1508 /*
1509  * Given a function name expressed as a string, look it up and return Oid
1510  */
1511 static Oid
1512 convert_function_name(text *functionname)
1513 {
1514         char       *funcname;
1515         Oid                     oid;
1516
1517         funcname = DatumGetCString(DirectFunctionCall1(textout,
1518                                                                                  PointerGetDatum(functionname)));
1519
1520         oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
1521                                                                                          CStringGetDatum(funcname)));
1522
1523         if (!OidIsValid(oid))
1524                 ereport(ERROR,
1525                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1526                                  errmsg("function \"%s\" does not exist", funcname)));
1527
1528         return oid;
1529 }
1530
1531 /*
1532  * convert_function_priv_string
1533  *              Convert text string to AclMode value.
1534  */
1535 static AclMode
1536 convert_function_priv_string(text *priv_type_text)
1537 {
1538         char       *priv_type;
1539
1540         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1541                                                                            PointerGetDatum(priv_type_text)));
1542
1543         /*
1544          * Return mode from priv_type string
1545          */
1546         if (strcasecmp(priv_type, "EXECUTE") == 0)
1547                 return ACL_EXECUTE;
1548         if (strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
1549                 return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
1550
1551         ereport(ERROR,
1552                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1553                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1554         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1555 }
1556
1557
1558 /*
1559  * has_language_privilege variants
1560  *              These are all named "has_language_privilege" at the SQL level.
1561  *              They take various combinations of language name, language OID,
1562  *              user name, user sysid, or implicit user = current_user.
1563  *
1564  *              The result is a boolean value: true if user has the indicated
1565  *              privilege, false if not.
1566  */
1567
1568 /*
1569  * has_language_privilege_name_name
1570  *              Check user privileges on a language given
1571  *              name username, text languagename, and text priv name.
1572  */
1573 Datum
1574 has_language_privilege_name_name(PG_FUNCTION_ARGS)
1575 {
1576         Name            username = PG_GETARG_NAME(0);
1577         text       *languagename = PG_GETARG_TEXT_P(1);
1578         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1579         int32           usesysid;
1580         Oid                     languageoid;
1581         AclMode         mode;
1582         AclResult       aclresult;
1583
1584         usesysid = get_usesysid(NameStr(*username));
1585         languageoid = convert_language_name(languagename);
1586         mode = convert_language_priv_string(priv_type_text);
1587
1588         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1589
1590         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1591 }
1592
1593 /*
1594  * has_language_privilege_name
1595  *              Check user privileges on a language given
1596  *              text languagename and text priv name.
1597  *              current_user is assumed
1598  */
1599 Datum
1600 has_language_privilege_name(PG_FUNCTION_ARGS)
1601 {
1602         text       *languagename = PG_GETARG_TEXT_P(0);
1603         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1604         AclId           usesysid;
1605         Oid                     languageoid;
1606         AclMode         mode;
1607         AclResult       aclresult;
1608
1609         usesysid = GetUserId();
1610         languageoid = convert_language_name(languagename);
1611         mode = convert_language_priv_string(priv_type_text);
1612
1613         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1614
1615         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1616 }
1617
1618 /*
1619  * has_language_privilege_name_id
1620  *              Check user privileges on a language given
1621  *              name usename, language oid, and text priv name.
1622  */
1623 Datum
1624 has_language_privilege_name_id(PG_FUNCTION_ARGS)
1625 {
1626         Name            username = PG_GETARG_NAME(0);
1627         Oid                     languageoid = PG_GETARG_OID(1);
1628         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1629         int32           usesysid;
1630         AclMode         mode;
1631         AclResult       aclresult;
1632
1633         usesysid = get_usesysid(NameStr(*username));
1634         mode = convert_language_priv_string(priv_type_text);
1635
1636         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1637
1638         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1639 }
1640
1641 /*
1642  * has_language_privilege_id
1643  *              Check user privileges on a language given
1644  *              language oid, and text priv name.
1645  *              current_user is assumed
1646  */
1647 Datum
1648 has_language_privilege_id(PG_FUNCTION_ARGS)
1649 {
1650         Oid                     languageoid = PG_GETARG_OID(0);
1651         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1652         AclId           usesysid;
1653         AclMode         mode;
1654         AclResult       aclresult;
1655
1656         usesysid = GetUserId();
1657         mode = convert_language_priv_string(priv_type_text);
1658
1659         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1660
1661         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1662 }
1663
1664 /*
1665  * has_language_privilege_id_name
1666  *              Check user privileges on a language given
1667  *              usesysid, text languagename, and text priv name.
1668  */
1669 Datum
1670 has_language_privilege_id_name(PG_FUNCTION_ARGS)
1671 {
1672         int32           usesysid = PG_GETARG_INT32(0);
1673         text       *languagename = PG_GETARG_TEXT_P(1);
1674         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1675         Oid                     languageoid;
1676         AclMode         mode;
1677         AclResult       aclresult;
1678
1679         languageoid = convert_language_name(languagename);
1680         mode = convert_language_priv_string(priv_type_text);
1681
1682         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1683
1684         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1685 }
1686
1687 /*
1688  * has_language_privilege_id_id
1689  *              Check user privileges on a language given
1690  *              usesysid, language oid, and text priv name.
1691  */
1692 Datum
1693 has_language_privilege_id_id(PG_FUNCTION_ARGS)
1694 {
1695         int32           usesysid = PG_GETARG_INT32(0);
1696         Oid                     languageoid = PG_GETARG_OID(1);
1697         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1698         AclMode         mode;
1699         AclResult       aclresult;
1700
1701         mode = convert_language_priv_string(priv_type_text);
1702
1703         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1704
1705         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1706 }
1707
1708 /*
1709  *              Support routines for has_language_privilege family.
1710  */
1711
1712 /*
1713  * Given a language name expressed as a string, look it up and return Oid
1714  */
1715 static Oid
1716 convert_language_name(text *languagename)
1717 {
1718         char       *langname;
1719         Oid                     oid;
1720
1721         langname = DatumGetCString(DirectFunctionCall1(textout,
1722                                                                                  PointerGetDatum(languagename)));
1723
1724         oid = GetSysCacheOid(LANGNAME,
1725                                                  CStringGetDatum(langname),
1726                                                  0, 0, 0);
1727         if (!OidIsValid(oid))
1728                 ereport(ERROR,
1729                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1730                                  errmsg("language \"%s\" does not exist", langname)));
1731
1732         return oid;
1733 }
1734
1735 /*
1736  * convert_language_priv_string
1737  *              Convert text string to AclMode value.
1738  */
1739 static AclMode
1740 convert_language_priv_string(text *priv_type_text)
1741 {
1742         char       *priv_type;
1743
1744         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1745                                                                            PointerGetDatum(priv_type_text)));
1746
1747         /*
1748          * Return mode from priv_type string
1749          */
1750         if (strcasecmp(priv_type, "USAGE") == 0)
1751                 return ACL_USAGE;
1752         if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
1753                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
1754
1755         ereport(ERROR,
1756                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1757                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1758         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1759 }
1760
1761
1762 /*
1763  * has_schema_privilege variants
1764  *              These are all named "has_schema_privilege" at the SQL level.
1765  *              They take various combinations of schema name, schema OID,
1766  *              user name, user sysid, or implicit user = current_user.
1767  *
1768  *              The result is a boolean value: true if user has the indicated
1769  *              privilege, false if not.
1770  */
1771
1772 /*
1773  * has_schema_privilege_name_name
1774  *              Check user privileges on a schema given
1775  *              name username, text schemaname, and text priv name.
1776  */
1777 Datum
1778 has_schema_privilege_name_name(PG_FUNCTION_ARGS)
1779 {
1780         Name            username = PG_GETARG_NAME(0);
1781         text       *schemaname = PG_GETARG_TEXT_P(1);
1782         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1783         int32           usesysid;
1784         Oid                     schemaoid;
1785         AclMode         mode;
1786         AclResult       aclresult;
1787
1788         usesysid = get_usesysid(NameStr(*username));
1789         schemaoid = convert_schema_name(schemaname);
1790         mode = convert_schema_priv_string(priv_type_text);
1791
1792         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1793
1794         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1795 }
1796
1797 /*
1798  * has_schema_privilege_name
1799  *              Check user privileges on a schema given
1800  *              text schemaname and text priv name.
1801  *              current_user is assumed
1802  */
1803 Datum
1804 has_schema_privilege_name(PG_FUNCTION_ARGS)
1805 {
1806         text       *schemaname = PG_GETARG_TEXT_P(0);
1807         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1808         AclId           usesysid;
1809         Oid                     schemaoid;
1810         AclMode         mode;
1811         AclResult       aclresult;
1812
1813         usesysid = GetUserId();
1814         schemaoid = convert_schema_name(schemaname);
1815         mode = convert_schema_priv_string(priv_type_text);
1816
1817         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1818
1819         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1820 }
1821
1822 /*
1823  * has_schema_privilege_name_id
1824  *              Check user privileges on a schema given
1825  *              name usename, schema oid, and text priv name.
1826  */
1827 Datum
1828 has_schema_privilege_name_id(PG_FUNCTION_ARGS)
1829 {
1830         Name            username = PG_GETARG_NAME(0);
1831         Oid                     schemaoid = PG_GETARG_OID(1);
1832         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1833         int32           usesysid;
1834         AclMode         mode;
1835         AclResult       aclresult;
1836
1837         usesysid = get_usesysid(NameStr(*username));
1838         mode = convert_schema_priv_string(priv_type_text);
1839
1840         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1841
1842         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1843 }
1844
1845 /*
1846  * has_schema_privilege_id
1847  *              Check user privileges on a schema given
1848  *              schema oid, and text priv name.
1849  *              current_user is assumed
1850  */
1851 Datum
1852 has_schema_privilege_id(PG_FUNCTION_ARGS)
1853 {
1854         Oid                     schemaoid = PG_GETARG_OID(0);
1855         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1856         AclId           usesysid;
1857         AclMode         mode;
1858         AclResult       aclresult;
1859
1860         usesysid = GetUserId();
1861         mode = convert_schema_priv_string(priv_type_text);
1862
1863         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1864
1865         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1866 }
1867
1868 /*
1869  * has_schema_privilege_id_name
1870  *              Check user privileges on a schema given
1871  *              usesysid, text schemaname, and text priv name.
1872  */
1873 Datum
1874 has_schema_privilege_id_name(PG_FUNCTION_ARGS)
1875 {
1876         int32           usesysid = PG_GETARG_INT32(0);
1877         text       *schemaname = PG_GETARG_TEXT_P(1);
1878         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1879         Oid                     schemaoid;
1880         AclMode         mode;
1881         AclResult       aclresult;
1882
1883         schemaoid = convert_schema_name(schemaname);
1884         mode = convert_schema_priv_string(priv_type_text);
1885
1886         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1887
1888         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1889 }
1890
1891 /*
1892  * has_schema_privilege_id_id
1893  *              Check user privileges on a schema given
1894  *              usesysid, schema oid, and text priv name.
1895  */
1896 Datum
1897 has_schema_privilege_id_id(PG_FUNCTION_ARGS)
1898 {
1899         int32           usesysid = PG_GETARG_INT32(0);
1900         Oid                     schemaoid = PG_GETARG_OID(1);
1901         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1902         AclMode         mode;
1903         AclResult       aclresult;
1904
1905         mode = convert_schema_priv_string(priv_type_text);
1906
1907         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1908
1909         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1910 }
1911
1912 /*
1913  *              Support routines for has_schema_privilege family.
1914  */
1915
1916 /*
1917  * Given a schema name expressed as a string, look it up and return Oid
1918  */
1919 static Oid
1920 convert_schema_name(text *schemaname)
1921 {
1922         char       *nspname;
1923         Oid                     oid;
1924
1925         nspname = DatumGetCString(DirectFunctionCall1(textout,
1926                                                                                    PointerGetDatum(schemaname)));
1927
1928         oid = GetSysCacheOid(NAMESPACENAME,
1929                                                  CStringGetDatum(nspname),
1930                                                  0, 0, 0);
1931         if (!OidIsValid(oid))
1932                 ereport(ERROR,
1933                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1934                                  errmsg("schema \"%s\" does not exist", nspname)));
1935
1936         return oid;
1937 }
1938
1939 /*
1940  * convert_schema_priv_string
1941  *              Convert text string to AclMode value.
1942  */
1943 static AclMode
1944 convert_schema_priv_string(text *priv_type_text)
1945 {
1946         char       *priv_type;
1947
1948         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1949                                                                            PointerGetDatum(priv_type_text)));
1950
1951         /*
1952          * Return mode from priv_type string
1953          */
1954         if (strcasecmp(priv_type, "CREATE") == 0)
1955                 return ACL_CREATE;
1956         if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1957                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1958
1959         if (strcasecmp(priv_type, "USAGE") == 0)
1960                 return ACL_USAGE;
1961         if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
1962                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
1963
1964         ereport(ERROR,
1965                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1966                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1967         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1968 }