]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Replace pg_shadow and pg_group by new role-capable catalogs pg_authid
[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-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.115 2005/06/28 05:09:00 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "catalog/namespace.h"
20 #include "catalog/pg_authid.h"
21 #include "catalog/pg_auth_members.h"
22 #include "catalog/pg_type.h"
23 #include "commands/dbcommands.h"
24 #include "commands/tablespace.h"
25 #include "miscadmin.h"
26 #include "utils/acl.h"
27 #include "utils/builtins.h"
28 #include "utils/catcache.h"
29 #include "utils/inval.h"
30 #include "utils/lsyscache.h"
31 #include "utils/memutils.h"
32 #include "utils/syscache.h"
33
34
35 #define ACL_IDTYPE_ROLE_KEYWORD "role"
36
37 /* The rolmemcache is a possibly-empty list of role OIDs.
38  * rolmemRole is the Role for which the cache was generated.
39  * In the event of a Role change the cache will be regenerated.
40  */
41 static List     *rolmemcache = NIL;
42 static Oid      rolmemRole = InvalidOid;
43
44 /* rolmemcache and rolmemRole only valid when 
45  * rolmemcacheValid is true */
46 static bool rolmemcacheValid = false;
47
48 static const char *getid(const char *s, char *n);
49 static void putid(char *p, const char *s);
50 static Acl *allocacl(int n);
51 static const char *aclparse(const char *s, AclItem *aip);
52 static bool aclitem_match(const AclItem *a1, const AclItem *a2);
53 static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
54                                   Oid ownerId);
55 static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
56                                  Oid ownerId, DropBehavior behavior);
57
58 static AclMode convert_priv_string(text *priv_type_text);
59
60 static Oid      convert_table_name(text *tablename);
61 static AclMode convert_table_priv_string(text *priv_type_text);
62 static Oid      convert_database_name(text *databasename);
63 static AclMode convert_database_priv_string(text *priv_type_text);
64 static Oid      convert_function_name(text *functionname);
65 static AclMode convert_function_priv_string(text *priv_type_text);
66 static Oid      convert_language_name(text *languagename);
67 static AclMode convert_language_priv_string(text *priv_type_text);
68 static Oid      convert_schema_name(text *schemaname);
69 static AclMode convert_schema_priv_string(text *priv_type_text);
70 static Oid      convert_tablespace_name(text *tablespacename);
71 static AclMode convert_tablespace_priv_string(text *priv_type_text);
72
73 static void RolMemCacheCallback(Datum arg, Oid relid);
74 static void recomputeRolMemCache(Oid roleid);
75
76
77 /*
78  * getid
79  *              Consumes the first alphanumeric string (identifier) found in string
80  *              's', ignoring any leading white space.  If it finds a double quote
81  *              it returns the word inside the quotes.
82  *
83  * RETURNS:
84  *              the string position in 's' that points to the next non-space character
85  *              in 's', after any quotes.  Also:
86  *              - loads the identifier into 'n'.  (If no identifier is found, 'n'
87  *                contains an empty string.)  'n' must be NAMEDATALEN bytes.
88  */
89 static const char *
90 getid(const char *s, char *n)
91 {
92         int                     len = 0;
93         bool            in_quotes = false;
94
95         Assert(s && n);
96
97         while (isspace((unsigned char) *s))
98                 s++;
99         /* This code had better match what putid() does, below */
100         for (;
101                  *s != '\0' &&
102                  (isalnum((unsigned char) *s) ||
103                   *s == '_' ||
104                   *s == '"' ||
105                   in_quotes);
106                  s++)
107         {
108                 if (*s == '"')
109                 {
110                         /* safe to look at next char (could be '\0' though) */
111                         if (*(s + 1) != '"')
112                         {
113                                 in_quotes = !in_quotes;
114                                 continue;
115                         }
116                         /* it's an escaped double quote; skip the escaping char */
117                         s++;
118                 }
119
120                 /* Add the character to the string */
121                 if (len >= NAMEDATALEN - 1)
122                         ereport(ERROR,
123                                         (errcode(ERRCODE_NAME_TOO_LONG),
124                                          errmsg("identifier too long"),
125                                  errdetail("Identifier must be less than %d characters.",
126                                                    NAMEDATALEN)));
127
128                 n[len++] = *s;
129         }
130         n[len] = '\0';
131         while (isspace((unsigned char) *s))
132                 s++;
133         return s;
134 }
135
136 /*
137  * Write a user or group Name at *p, adding double quotes if needed.
138  * There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
139  * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c
140  */
141 static void
142 putid(char *p, const char *s)
143 {
144         const char *src;
145         bool            safe = true;
146
147         for (src = s; *src; src++)
148         {
149                 /* This test had better match what getid() does, above */
150                 if (!isalnum((unsigned char) *src) && *src != '_')
151                 {
152                         safe = false;
153                         break;
154                 }
155         }
156         if (!safe)
157                 *p++ = '"';
158         for (src = s; *src; src++)
159         {
160                 /* A double quote character in a username is encoded as "" */
161                 if (*src == '"')
162                         *p++ = '"';
163                 *p++ = *src;
164         }
165         if (!safe)
166                 *p++ = '"';
167         *p = '\0';
168 }
169
170 /*
171  * aclparse
172  *              Consumes and parses an ACL specification of the form:
173  *                              [group|user] [A-Za-z0-9]*=[rwaR]*
174  *              from string 's', ignoring any leading white space or white space
175  *              between the optional id type keyword (group|user) and the actual
176  *              ACL specification.
177  *
178  *              This routine is called by the parser as well as aclitemin(), hence
179  *              the added generality.
180  *
181  * RETURNS:
182  *              the string position in 's' immediately following the ACL
183  *              specification.  Also:
184  *              - loads the structure pointed to by 'aip' with the appropriate
185  *                UID/GID, id type identifier and mode type values.
186  */
187 static const char *
188 aclparse(const char *s, AclItem *aip)
189 {
190         AclMode         privs,
191                                 goption,
192                                 read;
193         char            name[NAMEDATALEN];
194         char            name2[NAMEDATALEN];
195
196         Assert(s && aip);
197
198 #ifdef ACLDEBUG
199         elog(LOG, "aclparse: input = \"%s\"", s);
200 #endif
201         s = getid(s, name);
202         if (*s != '=')
203         {
204                 /* we just read a keyword, not a name */
205                 if (strcmp(name, ACL_IDTYPE_ROLE_KEYWORD) != 0)
206                         ereport(ERROR,
207                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
208                                          errmsg("unrecognized key word: \"%s\"", name),
209                                 errhint("ACL key word must be \"role\".")));
210                 s = getid(s, name);             /* move s to the name beyond the keyword */
211                 if (name[0] == '\0')
212                         ereport(ERROR,
213                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
214                                          errmsg("missing name"),
215                                          errhint("A name must follow the \"role\" key word.")));
216         }
217
218         if (*s != '=')
219                 ereport(ERROR,
220                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
221                                  errmsg("missing \"=\" sign")));
222
223         privs = goption = ACL_NO_RIGHTS;
224
225         for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
226         {
227                 switch (*s)
228                 {
229                         case '*':
230                                 goption |= read;
231                                 break;
232                         case ACL_INSERT_CHR:
233                                 read = ACL_INSERT;
234                                 break;
235                         case ACL_SELECT_CHR:
236                                 read = ACL_SELECT;
237                                 break;
238                         case ACL_UPDATE_CHR:
239                                 read = ACL_UPDATE;
240                                 break;
241                         case ACL_DELETE_CHR:
242                                 read = ACL_DELETE;
243                                 break;
244                         case ACL_RULE_CHR:
245                                 read = ACL_RULE;
246                                 break;
247                         case ACL_REFERENCES_CHR:
248                                 read = ACL_REFERENCES;
249                                 break;
250                         case ACL_TRIGGER_CHR:
251                                 read = ACL_TRIGGER;
252                                 break;
253                         case ACL_EXECUTE_CHR:
254                                 read = ACL_EXECUTE;
255                                 break;
256                         case ACL_USAGE_CHR:
257                                 read = ACL_USAGE;
258                                 break;
259                         case ACL_CREATE_CHR:
260                                 read = ACL_CREATE;
261                                 break;
262                         case ACL_CREATE_TEMP_CHR:
263                                 read = ACL_CREATE_TEMP;
264                                 break;
265                         default:
266                                 ereport(ERROR,
267                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
268                                   errmsg("invalid mode character: must be one of \"%s\"",
269                                                  ACL_ALL_RIGHTS_STR)));
270                 }
271
272                 privs |= read;
273         }
274
275         if (name[0] == '\0')
276                 aip->ai_grantee = ACL_ID_PUBLIC;
277         else
278                 aip->ai_grantee = get_roleid_checked(name);
279
280         /*
281          * XXX Allow a degree of backward compatibility by defaulting the
282          * grantor to the superuser.
283          */
284         if (*s == '/')
285         {
286                 s = getid(s + 1, name2);
287                 if (name2[0] == '\0')
288                         ereport(ERROR,
289                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
290                                          errmsg("a name must follow the \"/\" sign")));
291                 aip->ai_grantor = get_roleid_checked(name2);
292         }
293         else
294         {
295                 aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
296                 ereport(WARNING,
297                                 (errcode(ERRCODE_INVALID_GRANTOR),
298                                  errmsg("defaulting grantor to user ID %u",
299                                                 BOOTSTRAP_SUPERUSERID)));
300         }
301
302         ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
303
304 #ifdef ACLDEBUG
305         elog(LOG, "aclparse: correctly read [%u %x %x]",
306                  aip->ai_grantee, privs, goption);
307 #endif
308
309         return s;
310 }
311
312 /*
313  * allocacl
314  *              Allocates storage for a new Acl with 'n' entries.
315  *
316  * RETURNS:
317  *              the new Acl
318  */
319 static Acl *
320 allocacl(int n)
321 {
322         Acl                *new_acl;
323         Size            size;
324
325         if (n < 0)
326                 elog(ERROR, "invalid size: %d", n);
327         size = ACL_N_SIZE(n);
328         new_acl = (Acl *) palloc0(size);
329         new_acl->size = size;
330         new_acl->ndim = 1;
331         new_acl->flags = 0;
332         new_acl->elemtype = ACLITEMOID;
333         ARR_LBOUND(new_acl)[0] = 1;
334         ARR_DIMS(new_acl)[0] = n;
335         return new_acl;
336 }
337
338 /*
339  * aclitemin
340  *              Allocates storage for, and fills in, a new AclItem given a string
341  *              's' that contains an ACL specification.  See aclparse for details.
342  *
343  * RETURNS:
344  *              the new AclItem
345  */
346 Datum
347 aclitemin(PG_FUNCTION_ARGS)
348 {
349         const char *s = PG_GETARG_CSTRING(0);
350         AclItem    *aip;
351
352         aip = (AclItem *) palloc(sizeof(AclItem));
353         s = aclparse(s, aip);
354         while (isspace((unsigned char) *s))
355                 ++s;
356         if (*s)
357                 ereport(ERROR,
358                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
359                    errmsg("extra garbage at the end of the ACL specification")));
360
361         PG_RETURN_ACLITEM_P(aip);
362 }
363
364 /*
365  * aclitemout
366  *              Allocates storage for, and fills in, a new null-delimited string
367  *              containing a formatted ACL specification.  See aclparse for details.
368  *
369  * RETURNS:
370  *              the new string
371  */
372 Datum
373 aclitemout(PG_FUNCTION_ARGS)
374 {
375         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
376         char       *p;
377         char       *out;
378         HeapTuple       htup;
379         unsigned        i;
380
381         out = palloc(strlen("group =/") +
382                                  2 * N_ACL_RIGHTS +
383                                  2 * (2 * NAMEDATALEN + 2) +
384                                  1);
385
386         p = out;
387         *p = '\0';
388
389         if (aip->ai_grantee != ACL_ID_PUBLIC)
390         {
391                 htup = SearchSysCache(AUTHOID,
392                                                           ObjectIdGetDatum(aip->ai_grantee),
393                                                           0, 0, 0);
394                 if (HeapTupleIsValid(htup))
395                 {
396                         putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
397                         ReleaseSysCache(htup);
398                 }
399                 else
400                 {
401                         /* Generate numeric OID if we don't find an entry */
402                         sprintf(p, "%u", aip->ai_grantee);
403                 }
404         }
405         while (*p)
406                 ++p;
407
408         *p++ = '=';
409
410         for (i = 0; i < N_ACL_RIGHTS; ++i)
411         {
412                 if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
413                         *p++ = ACL_ALL_RIGHTS_STR[i];
414                 if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
415                         *p++ = '*';
416         }
417
418         *p++ = '/';
419         *p = '\0';
420
421         htup = SearchSysCache(AUTHOID,
422                                                   ObjectIdGetDatum(aip->ai_grantor),
423                                                   0, 0, 0);
424         if (HeapTupleIsValid(htup))
425         {
426                 putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
427                 ReleaseSysCache(htup);
428         }
429         else
430         {
431                 /* Generate numeric OID if we don't find an entry */
432                 sprintf(p, "%u", aip->ai_grantor);
433         }
434
435         while (*p)
436                 ++p;
437         *p = '\0';
438
439         PG_RETURN_CSTRING(out);
440 }
441
442 /*
443  * aclitem_match
444  *              Two AclItems are considered to match iff they have the same
445  *              grantee and grantor; the privileges are ignored.
446  */
447 static bool
448 aclitem_match(const AclItem *a1, const AclItem *a2)
449 {
450         return a1->ai_grantee == a2->ai_grantee &&
451                 a1->ai_grantor == a2->ai_grantor;
452 }
453
454 /*
455  * aclitem equality operator
456  */
457 Datum
458 aclitem_eq(PG_FUNCTION_ARGS)
459 {
460         AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
461         AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
462         bool            result;
463
464         result = a1->ai_privs == a2->ai_privs &&
465                 a1->ai_grantee == a2->ai_grantee &&
466                 a1->ai_grantor == a2->ai_grantor;
467         PG_RETURN_BOOL(result);
468 }
469
470 /*
471  * aclitem hash function
472  *
473  * We make aclitems hashable not so much because anyone is likely to hash
474  * them, as because we want array equality to work on aclitem arrays, and
475  * with the typcache mechanism we must have a hash or btree opclass.
476  */
477 Datum
478 hash_aclitem(PG_FUNCTION_ARGS)
479 {
480         AclItem    *a = PG_GETARG_ACLITEM_P(0);
481
482         /* not very bright, but avoids any issue of padding in struct */
483         PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
484 }
485
486
487 /*
488  * acldefault()  --- create an ACL describing default access permissions
489  *
490  * Change this routine if you want to alter the default access policy for
491  * newly-created objects (or any object with a NULL acl entry).
492  */
493 Acl *
494 acldefault(GrantObjectType objtype, Oid ownerId)
495 {
496         AclMode         world_default;
497         AclMode         owner_default;
498         Acl                *acl;
499         AclItem    *aip;
500
501         switch (objtype)
502         {
503                 case ACL_OBJECT_RELATION:
504                         world_default = ACL_NO_RIGHTS;
505                         owner_default = ACL_ALL_RIGHTS_RELATION;
506                         break;
507                 case ACL_OBJECT_DATABASE:
508                         world_default = ACL_CREATE_TEMP;        /* not NO_RIGHTS! */
509                         owner_default = ACL_ALL_RIGHTS_DATABASE;
510                         break;
511                 case ACL_OBJECT_FUNCTION:
512                         /* Grant EXECUTE by default, for now */
513                         world_default = ACL_EXECUTE;
514                         owner_default = ACL_ALL_RIGHTS_FUNCTION;
515                         break;
516                 case ACL_OBJECT_LANGUAGE:
517                         /* Grant USAGE by default, for now */
518                         world_default = ACL_USAGE;
519                         owner_default = ACL_ALL_RIGHTS_LANGUAGE;
520                         break;
521                 case ACL_OBJECT_NAMESPACE:
522                         world_default = ACL_NO_RIGHTS;
523                         owner_default = ACL_ALL_RIGHTS_NAMESPACE;
524                         break;
525                 case ACL_OBJECT_TABLESPACE:
526                         world_default = ACL_NO_RIGHTS;
527                         owner_default = ACL_ALL_RIGHTS_TABLESPACE;
528                         break;
529                 default:
530                         elog(ERROR, "unrecognized objtype: %d", (int) objtype);
531                         world_default = ACL_NO_RIGHTS;          /* keep compiler quiet */
532                         owner_default = ACL_NO_RIGHTS;
533                         break;
534         }
535
536         acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1);
537         aip = ACL_DAT(acl);
538
539         if (world_default != ACL_NO_RIGHTS)
540         {
541                 aip->ai_grantee = ACL_ID_PUBLIC;
542                 aip->ai_grantor = ownerId;
543                 ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
544                 aip++;
545         }
546
547         /*
548          * Note that the owner's entry shows all ordinary privileges but no
549          * grant options.  This is because his grant options come "from the
550          * system" and not from his own efforts.  (The SQL spec says that the
551          * owner's rights come from a "_SYSTEM" authid.)  However, we do
552          * consider that the owner's ordinary privileges are self-granted;
553          * this lets him revoke them.  We implement the owner's grant options
554          * without any explicit "_SYSTEM"-like ACL entry, by internally
555          * special-casing the owner whereever we are testing grant options.
556          */
557         aip->ai_grantee = ownerId;
558         aip->ai_grantor = ownerId;
559         ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
560
561         return acl;
562 }
563
564
565 /*
566  * Update an ACL array to add or remove specified privileges.
567  *
568  *      old_acl: the input ACL array
569  *      mod_aip: defines the privileges to be added, removed, or substituted
570  *      modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
571  *      ownerId: Oid of object owner
572  *      behavior: RESTRICT or CASCADE behavior for recursive removal
573  *
574  * ownerid and behavior are only relevant when the update operation specifies
575  * deletion of grant options.
576  *
577  * The result is a modified copy; the input object is not changed.
578  *
579  * NB: caller is responsible for having detoasted the input ACL, if needed.
580  */
581 Acl *
582 aclupdate(const Acl *old_acl, const AclItem *mod_aip,
583                   int modechg, Oid ownerId, DropBehavior behavior)
584 {
585         Acl                *new_acl = NULL;
586         AclItem    *old_aip,
587                            *new_aip = NULL;
588         AclMode         old_rights,
589                                 old_goptions,
590                                 new_rights,
591                                 new_goptions;
592         int                     dst,
593                                 num;
594
595         /* These checks for null input are probably dead code, but... */
596         if (!old_acl || ACL_NUM(old_acl) < 0)
597                 old_acl = allocacl(0);
598         if (!mod_aip)
599         {
600                 new_acl = allocacl(ACL_NUM(old_acl));
601                 memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
602                 return new_acl;
603         }
604
605         /* If granting grant options, check for circularity */
606         if (modechg != ACL_MODECHG_DEL &&
607                 ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
608                 check_circularity(old_acl, mod_aip, ownerId);
609
610         num = ACL_NUM(old_acl);
611         old_aip = ACL_DAT(old_acl);
612
613         /*
614          * Search the ACL for an existing entry for this grantee and grantor.
615          * If one exists, just modify the entry in-place (well, in the same
616          * position, since we actually return a copy); otherwise, insert the
617          * new entry at the end.
618          */
619
620         for (dst = 0; dst < num; ++dst)
621         {
622                 if (aclitem_match(mod_aip, old_aip + dst))
623                 {
624                         /* found a match, so modify existing item */
625                         new_acl = allocacl(num);
626                         new_aip = ACL_DAT(new_acl);
627                         memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
628                         break;
629                 }
630         }
631
632         if (dst == num)
633         {
634                 /* need to append a new item */
635                 new_acl = allocacl(num + 1);
636                 new_aip = ACL_DAT(new_acl);
637                 memcpy(new_aip, old_aip, num * sizeof(AclItem));
638
639                 /* initialize the new entry with no permissions */
640                 new_aip[dst].ai_grantee = mod_aip->ai_grantee;
641                 new_aip[dst].ai_grantor = mod_aip->ai_grantor;
642                 ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
643                                                                    ACL_NO_RIGHTS, ACL_NO_RIGHTS);
644                 num++;                                  /* set num to the size of new_acl */
645         }
646
647         old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
648         old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
649
650         /* apply the specified permissions change */
651         switch (modechg)
652         {
653                 case ACL_MODECHG_ADD:
654                         ACLITEM_SET_RIGHTS(new_aip[dst],
655                                                            old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
656                         break;
657                 case ACL_MODECHG_DEL:
658                         ACLITEM_SET_RIGHTS(new_aip[dst],
659                                                          old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
660                         break;
661                 case ACL_MODECHG_EQL:
662                         ACLITEM_SET_RIGHTS(new_aip[dst],
663                                                            ACLITEM_GET_RIGHTS(*mod_aip));
664                         break;
665         }
666
667         new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
668         new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
669
670         /*
671          * If the adjusted entry has no permissions, delete it from the list.
672          */
673         if (new_rights == ACL_NO_RIGHTS)
674         {
675                 memmove(new_aip + dst,
676                                 new_aip + dst + 1,
677                                 (num - dst - 1) * sizeof(AclItem));
678                 ARR_DIMS(new_acl)[0] = num - 1;
679                 ARR_SIZE(new_acl) -= sizeof(AclItem);
680         }
681
682         /*
683          * Remove abandoned privileges (cascading revoke).      Currently we can
684          * only handle this when the grantee is not PUBLIC.
685          */
686         if ((old_goptions & ~new_goptions) != 0)
687         {
688                 Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
689                 new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
690                                                                    (old_goptions & ~new_goptions),
691                                                                    ownerId, behavior);
692         }
693
694         return new_acl;
695 }
696
697 /*
698  * Update an ACL array to reflect a change of owner to the parent object
699  *
700  *      old_acl: the input ACL array (must not be NULL)
701  *      oldOwnerId: Oid of the old object owner
702  *      newOwnerId: Oid of the new object owner
703  *
704  * The result is a modified copy; the input object is not changed.
705  *
706  * NB: caller is responsible for having detoasted the input ACL, if needed.
707  */
708 Acl *
709 aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
710 {
711         Acl                *new_acl;
712         AclItem    *new_aip;
713         AclItem    *old_aip;
714         AclItem    *dst_aip;
715         AclItem    *src_aip;
716         AclItem    *targ_aip;
717         bool            newpresent = false;
718         int                     dst,
719                                 src,
720                                 targ,
721                                 num;
722
723         /*
724          * Make a copy of the given ACL, substituting new owner ID for old
725          * wherever it appears as either grantor or grantee.  Also note if the
726          * new owner ID is already present.
727          */
728         num = ACL_NUM(old_acl);
729         old_aip = ACL_DAT(old_acl);
730         new_acl = allocacl(num);
731         new_aip = ACL_DAT(new_acl);
732         memcpy(new_aip, old_aip, num * sizeof(AclItem));
733         for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
734         {
735                 if (dst_aip->ai_grantor == oldOwnerId)
736                         dst_aip->ai_grantor = newOwnerId;
737                 else if (dst_aip->ai_grantor == newOwnerId)
738                         newpresent = true;
739                 if (dst_aip->ai_grantee == oldOwnerId)
740                         dst_aip->ai_grantee = newOwnerId;
741                 else if (dst_aip->ai_grantee == newOwnerId)
742                         newpresent = true;
743         }
744
745         /*
746          * If the old ACL contained any references to the new owner, then we
747          * may now have generated an ACL containing duplicate entries.  Find
748          * them and merge them so that there are not duplicates.  (This is
749          * relatively expensive since we use a stupid O(N^2) algorithm, but
750          * it's unlikely to be the normal case.)
751          *
752          * To simplify deletion of duplicate entries, we temporarily leave them
753          * in the array but set their privilege masks to zero; when we reach
754          * such an entry it's just skipped.  (Thus, a side effect of this code
755          * will be to remove privilege-free entries, should there be any in
756          * the input.)  dst is the next output slot, targ is the currently
757          * considered input slot (always >= dst), and src scans entries to the
758          * right of targ looking for duplicates.  Once an entry has been
759          * emitted to dst it is known duplicate-free and need not be
760          * considered anymore.
761          */
762         if (newpresent)
763         {
764                 dst = 0;
765                 for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
766                 {
767                         /* ignore if deleted in an earlier pass */
768                         if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
769                                 continue;
770                         /* find and merge any duplicates */
771                         for (src = targ + 1, src_aip = targ_aip + 1; src < num;
772                                  src++, src_aip++)
773                         {
774                                 if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
775                                         continue;
776                                 if (aclitem_match(targ_aip, src_aip))
777                                 {
778                                         ACLITEM_SET_RIGHTS(*targ_aip,
779                                                                            ACLITEM_GET_RIGHTS(*targ_aip) |
780                                                                            ACLITEM_GET_RIGHTS(*src_aip));
781                                         /* mark the duplicate deleted */
782                                         ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
783                                 }
784                         }
785                         /* and emit to output */
786                         new_aip[dst] = *targ_aip;
787                         dst++;
788                 }
789                 /* Adjust array size to be 'dst' items */
790                 ARR_DIMS(new_acl)[0] = dst;
791                 ARR_SIZE(new_acl) = ACL_N_SIZE(dst);
792         }
793
794         return new_acl;
795 }
796
797
798 /*
799  * When granting grant options, we must disallow attempts to set up circular
800  * chains of grant options.  Suppose A (the object owner) grants B some
801  * privileges with grant option, and B re-grants them to C.  If C could
802  * grant the privileges to B as well, then A would be unable to effectively
803  * revoke the privileges from B, since recursive_revoke would consider that
804  * B still has 'em from C.
805  *
806  * We check for this by recursively deleting all grant options belonging to
807  * the target grantee, and then seeing if the would-be grantor still has the
808  * grant option or not.
809  */
810 static void
811 check_circularity(const Acl *old_acl, const AclItem *mod_aip,
812                                   Oid ownerId)
813 {
814         Acl                *acl;
815         AclItem    *aip;
816         int                     i,
817                                 num;
818         AclMode         own_privs;
819
820         /*
821          * For now, grant options can only be granted to roles, not PUBLIC.
822          * Otherwise we'd have to work a bit harder here.
823          */
824         Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
825
826         /* The owner always has grant options, no need to check */
827         if (mod_aip->ai_grantor == ownerId)
828                 return;
829
830         /* Make a working copy */
831         acl = allocacl(ACL_NUM(old_acl));
832         memcpy(acl, old_acl, ACL_SIZE(old_acl));
833
834         /* Zap all grant options of target grantee, plus what depends on 'em */
835 cc_restart:
836         num = ACL_NUM(acl);
837         aip = ACL_DAT(acl);
838         for (i = 0; i < num; i++)
839         {
840                 if (aip[i].ai_grantee == mod_aip->ai_grantee &&
841                         ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
842                 {
843                         Acl                *new_acl;
844
845                         /* We'll actually zap ordinary privs too, but no matter */
846                         new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
847                                                                 ownerId, DROP_CASCADE);
848
849                         pfree(acl);
850                         acl = new_acl;
851
852                         goto cc_restart;
853                 }
854         }
855
856         /* Now we can compute grantor's independently-derived privileges */
857         own_privs = aclmask(acl,
858                                                 mod_aip->ai_grantor,
859                                                 ownerId,
860                                         ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
861                                                 ACLMASK_ALL);
862         own_privs = ACL_OPTION_TO_PRIVS(own_privs);
863
864         if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
865                 ereport(ERROR,
866                                 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
867                                  errmsg("grant options cannot be granted back to your own grantor")));
868
869         pfree(acl);
870 }
871
872
873 /*
874  * Ensure that no privilege is "abandoned".  A privilege is abandoned
875  * if the user that granted the privilege loses the grant option.  (So
876  * the chain through which it was granted is broken.)  Either the
877  * abandoned privileges are revoked as well, or an error message is
878  * printed, depending on the drop behavior option.
879  *
880  *      acl: the input ACL list
881  *      grantee: the user from whom some grant options have been revoked
882  *      revoke_privs: the grant options being revoked
883  *      ownerId: Oid of object owner
884  *      behavior: RESTRICT or CASCADE behavior for recursive removal
885  *
886  * The input Acl object is pfree'd if replaced.
887  */
888 static Acl *
889 recursive_revoke(Acl *acl,
890                                  Oid grantee,
891                                  AclMode revoke_privs,
892                                  Oid ownerId,
893                                  DropBehavior behavior)
894 {
895         AclMode         still_has;
896         AclItem    *aip;
897         int                     i,
898                                 num;
899
900         /* The owner can never truly lose grant options, so short-circuit */
901         if (grantee == ownerId)
902                 return acl;
903
904         /* The grantee might still have the privileges via another grantor */
905         still_has = aclmask(acl, grantee, ownerId,
906                                                 ACL_GRANT_OPTION_FOR(revoke_privs),
907                                                 ACLMASK_ALL);
908         revoke_privs &= ~still_has;
909         if (revoke_privs == ACL_NO_RIGHTS)
910                 return acl;
911
912 restart:
913         num = ACL_NUM(acl);
914         aip = ACL_DAT(acl);
915         for (i = 0; i < num; i++)
916         {
917                 if (aip[i].ai_grantor == grantee
918                         && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
919                 {
920                         AclItem         mod_acl;
921                         Acl                *new_acl;
922
923                         if (behavior == DROP_RESTRICT)
924                                 ereport(ERROR,
925                                                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
926                                                  errmsg("dependent privileges exist"),
927                                                  errhint("Use CASCADE to revoke them too.")));
928
929                         mod_acl.ai_grantor = grantee;
930                         mod_acl.ai_grantee = aip[i].ai_grantee;
931                         ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
932                                                                            revoke_privs,
933                                                                            revoke_privs);
934
935                         new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
936                                                                 ownerId, behavior);
937
938                         pfree(acl);
939                         acl = new_acl;
940
941                         goto restart;
942                 }
943         }
944
945         return acl;
946 }
947
948
949 /*
950  * aclmask --- compute bitmask of all privileges held by roleid.
951  *
952  * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
953  * held by the given roleid according to the given ACL list, ANDed
954  * with 'mask'.  (The point of passing 'mask' is to let the routine
955  * exit early if all privileges of interest have been found.)
956  *
957  * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
958  * is known true.  (This lets us exit soonest in cases where the
959  * caller is only going to test for zero or nonzero result.)
960  *
961  * Usage patterns:
962  *
963  * To see if any of a set of privileges are held:
964  *              if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
965  *
966  * To see if all of a set of privileges are held:
967  *              if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
968  *
969  * To determine exactly which of a set of privileges are held:
970  *              heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
971  *
972  */
973 AclMode
974 aclmask(const Acl *acl, Oid roleid, Oid ownerId,
975                 AclMode mask, AclMaskHow how)
976 {
977         AclMode         result;
978         AclItem    *aidat;
979         int                     i,
980                                 num;
981
982         /*
983          * Null ACL should not happen, since caller should have inserted
984          * appropriate default
985          */
986         if (acl == NULL)
987                 elog(ERROR, "null ACL");
988
989         /* Quick exit for mask == 0 */
990         if (mask == 0)
991                 return 0;
992
993         result = 0;
994
995         /* Owner always implicitly has all grant options */
996         if (is_member_of_role(roleid,ownerId))
997         {
998                 result = mask & ACLITEM_ALL_GOPTION_BITS;
999                 if (result == mask)
1000                         return result;
1001         }
1002
1003         num = ACL_NUM(acl);
1004         aidat = ACL_DAT(acl);
1005
1006         /*
1007          * Check privileges granted directly to role, indirectly
1008          * via role membership or to public
1009          */
1010         for (i = 0; i < num; i++)
1011         {
1012                 AclItem    *aidata = &aidat[i];
1013
1014                 if (aidata->ai_grantee == ACL_ID_PUBLIC ||
1015                         is_member_of_role(roleid, aidata->ai_grantee))
1016                 {
1017                         result |= (aidata->ai_privs & mask);
1018                         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1019                                 return result;
1020                 }
1021         }
1022
1023         return result;
1024 }
1025
1026
1027 /*
1028  * Is member a member of role?
1029  * relmemcache includes the role itself too
1030  */
1031 bool
1032 is_member_of_role(Oid member, Oid role)
1033 {
1034         /* Fast path for simple case */
1035         if (member == role)
1036                 return true;
1037
1038         recomputeRolMemCache(member);
1039
1040         return list_member_oid(rolmemcache, role);
1041 }
1042
1043
1044 /*
1045  * aclinsert (exported function)
1046  */
1047 Datum
1048 aclinsert(PG_FUNCTION_ARGS)
1049 {
1050         ereport(ERROR,
1051                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1052                          errmsg("aclinsert is no longer supported")));
1053
1054         PG_RETURN_NULL();                       /* keep compiler quiet */
1055 }
1056
1057 Datum
1058 aclremove(PG_FUNCTION_ARGS)
1059 {
1060         ereport(ERROR,
1061                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1062                          errmsg("aclremove is no longer supported")));
1063
1064         PG_RETURN_NULL();                       /* keep compiler quiet */
1065 }
1066
1067 Datum
1068 aclcontains(PG_FUNCTION_ARGS)
1069 {
1070         Acl                *acl = PG_GETARG_ACL_P(0);
1071         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
1072         AclItem    *aidat;
1073         int                     i,
1074                                 num;
1075
1076         num = ACL_NUM(acl);
1077         aidat = ACL_DAT(acl);
1078         for (i = 0; i < num; ++i)
1079         {
1080                 if (aip->ai_grantee == aidat[i].ai_grantee &&
1081                         aip->ai_grantor == aidat[i].ai_grantor &&
1082                         (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
1083                         PG_RETURN_BOOL(true);
1084         }
1085         PG_RETURN_BOOL(false);
1086 }
1087
1088 Datum
1089 makeaclitem(PG_FUNCTION_ARGS)
1090 {
1091         Oid                     grantee = PG_GETARG_OID(0);
1092         Oid             grantor = PG_GETARG_OID(1);
1093         text       *privtext = PG_GETARG_TEXT_P(2);
1094         bool            goption = PG_GETARG_BOOL(3);
1095         AclItem    *aclitem;
1096         AclMode         priv;
1097
1098         priv = convert_priv_string(privtext);
1099
1100         aclitem = (AclItem *) palloc(sizeof(AclItem));
1101
1102         aclitem->ai_grantee = grantee;
1103         aclitem->ai_grantor = grantor;
1104
1105         ACLITEM_SET_PRIVS_GOPTIONS(*aclitem, priv,
1106                                                            (goption ? priv : ACL_NO_RIGHTS));
1107
1108         PG_RETURN_ACLITEM_P(aclitem);
1109 }
1110
1111 static AclMode
1112 convert_priv_string(text *priv_type_text)
1113 {
1114         char       *priv_type;
1115
1116         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1117                                                                            PointerGetDatum(priv_type_text)));
1118
1119         if (pg_strcasecmp(priv_type, "SELECT") == 0)
1120                 return ACL_SELECT;
1121         if (pg_strcasecmp(priv_type, "INSERT") == 0)
1122                 return ACL_INSERT;
1123         if (pg_strcasecmp(priv_type, "UPDATE") == 0)
1124                 return ACL_UPDATE;
1125         if (pg_strcasecmp(priv_type, "DELETE") == 0)
1126                 return ACL_DELETE;
1127         if (pg_strcasecmp(priv_type, "RULE") == 0)
1128                 return ACL_RULE;
1129         if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
1130                 return ACL_REFERENCES;
1131         if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
1132                 return ACL_TRIGGER;
1133         if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
1134                 return ACL_EXECUTE;
1135         if (pg_strcasecmp(priv_type, "USAGE") == 0)
1136                 return ACL_USAGE;
1137         if (pg_strcasecmp(priv_type, "CREATE") == 0)
1138                 return ACL_CREATE;
1139         if (pg_strcasecmp(priv_type, "TEMP") == 0)
1140                 return ACL_CREATE_TEMP;
1141         if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
1142                 return ACL_CREATE_TEMP;
1143
1144         ereport(ERROR,
1145                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1146                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1147         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1148 }
1149
1150
1151 /*
1152  * has_table_privilege variants
1153  *              These are all named "has_table_privilege" at the SQL level.
1154  *              They take various combinations of relation name, relation OID,
1155  *              user name, user OID, or implicit user = current_user.
1156  *
1157  *              The result is a boolean value: true if user has the indicated
1158  *              privilege, false if not.
1159  */
1160
1161 /*
1162  * has_table_privilege_name_name
1163  *              Check user privileges on a table given
1164  *              name username, text tablename, and text priv name.
1165  */
1166 Datum
1167 has_table_privilege_name_name(PG_FUNCTION_ARGS)
1168 {
1169         Name            rolename = PG_GETARG_NAME(0);
1170         text       *tablename = PG_GETARG_TEXT_P(1);
1171         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1172         Oid                     roleid;
1173         Oid                     tableoid;
1174         AclMode         mode;
1175         AclResult       aclresult;
1176
1177         roleid = get_roleid_checked(NameStr(*rolename));
1178
1179         tableoid = convert_table_name(tablename);
1180         mode = convert_table_priv_string(priv_type_text);
1181
1182         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1183
1184         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1185 }
1186
1187 /*
1188  * has_table_privilege_name
1189  *              Check user privileges on a table given
1190  *              text tablename and text priv name.
1191  *              current_user is assumed
1192  */
1193 Datum
1194 has_table_privilege_name(PG_FUNCTION_ARGS)
1195 {
1196         text       *tablename = PG_GETARG_TEXT_P(0);
1197         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1198         Oid                     roleid;
1199         Oid                     tableoid;
1200         AclMode         mode;
1201         AclResult       aclresult;
1202
1203         roleid = GetUserId();
1204         tableoid = convert_table_name(tablename);
1205         mode = convert_table_priv_string(priv_type_text);
1206
1207         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1208
1209         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1210 }
1211
1212 /*
1213  * has_table_privilege_name_id
1214  *              Check user privileges on a table given
1215  *              name usename, table oid, and text priv name.
1216  */
1217 Datum
1218 has_table_privilege_name_id(PG_FUNCTION_ARGS)
1219 {
1220         Name            username = PG_GETARG_NAME(0);
1221         Oid                     tableoid = PG_GETARG_OID(1);
1222         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1223         Oid                     roleid;
1224         AclMode         mode;
1225         AclResult       aclresult;
1226
1227         roleid = get_roleid_checked(NameStr(*username));
1228
1229         mode = convert_table_priv_string(priv_type_text);
1230
1231         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1232
1233         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1234 }
1235
1236 /*
1237  * has_table_privilege_id
1238  *              Check user privileges on a table given
1239  *              table oid, and text priv name.
1240  *              current_user is assumed
1241  */
1242 Datum
1243 has_table_privilege_id(PG_FUNCTION_ARGS)
1244 {
1245         Oid                     tableoid = PG_GETARG_OID(0);
1246         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1247         Oid             roleid;
1248         AclMode         mode;
1249         AclResult       aclresult;
1250
1251         roleid = GetUserId();
1252         mode = convert_table_priv_string(priv_type_text);
1253
1254         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1255
1256         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1257 }
1258
1259 /*
1260  * has_table_privilege_id_name
1261  *              Check user privileges on a table given
1262  *              roleid, text tablename, and text priv name.
1263  */
1264 Datum
1265 has_table_privilege_id_name(PG_FUNCTION_ARGS)
1266 {
1267         Oid                     roleid = PG_GETARG_OID(0);
1268         text       *tablename = PG_GETARG_TEXT_P(1);
1269         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1270         Oid                     tableoid;
1271         AclMode         mode;
1272         AclResult       aclresult;
1273
1274         tableoid = convert_table_name(tablename);
1275         mode = convert_table_priv_string(priv_type_text);
1276
1277         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1278
1279         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1280 }
1281
1282 /*
1283  * has_table_privilege_id_id
1284  *              Check user privileges on a table given
1285  *              roleid, table oid, and text priv name.
1286  */
1287 Datum
1288 has_table_privilege_id_id(PG_FUNCTION_ARGS)
1289 {
1290         Oid                     roleid = PG_GETARG_OID(0);
1291         Oid                     tableoid = PG_GETARG_OID(1);
1292         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1293         AclMode         mode;
1294         AclResult       aclresult;
1295
1296         mode = convert_table_priv_string(priv_type_text);
1297
1298         aclresult = pg_class_aclcheck(tableoid, roleid, mode);
1299
1300         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1301 }
1302
1303 /*
1304  *              Support routines for has_table_privilege family.
1305  */
1306
1307 /*
1308  * Given a table name expressed as a string, look it up and return Oid
1309  */
1310 static Oid
1311 convert_table_name(text *tablename)
1312 {
1313         RangeVar   *relrv;
1314
1315         relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
1316
1317         return RangeVarGetRelid(relrv, false);
1318 }
1319
1320 /*
1321  * convert_table_priv_string
1322  *              Convert text string to AclMode value.
1323  */
1324 static AclMode
1325 convert_table_priv_string(text *priv_type_text)
1326 {
1327         char       *priv_type;
1328
1329         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1330                                                                            PointerGetDatum(priv_type_text)));
1331
1332         /*
1333          * Return mode from priv_type string
1334          */
1335         if (pg_strcasecmp(priv_type, "SELECT") == 0)
1336                 return ACL_SELECT;
1337         if (pg_strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
1338                 return ACL_GRANT_OPTION_FOR(ACL_SELECT);
1339
1340         if (pg_strcasecmp(priv_type, "INSERT") == 0)
1341                 return ACL_INSERT;
1342         if (pg_strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
1343                 return ACL_GRANT_OPTION_FOR(ACL_INSERT);
1344
1345         if (pg_strcasecmp(priv_type, "UPDATE") == 0)
1346                 return ACL_UPDATE;
1347         if (pg_strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
1348                 return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
1349
1350         if (pg_strcasecmp(priv_type, "DELETE") == 0)
1351                 return ACL_DELETE;
1352         if (pg_strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
1353                 return ACL_GRANT_OPTION_FOR(ACL_DELETE);
1354
1355         if (pg_strcasecmp(priv_type, "RULE") == 0)
1356                 return ACL_RULE;
1357         if (pg_strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
1358                 return ACL_GRANT_OPTION_FOR(ACL_RULE);
1359
1360         if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
1361                 return ACL_REFERENCES;
1362         if (pg_strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
1363                 return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
1364
1365         if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
1366                 return ACL_TRIGGER;
1367         if (pg_strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
1368                 return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
1369
1370         ereport(ERROR,
1371                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1372                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1373         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1374 }
1375
1376
1377 /*
1378  * has_database_privilege variants
1379  *              These are all named "has_database_privilege" at the SQL level.
1380  *              They take various combinations of database name, database OID,
1381  *              user name, user OID, or implicit user = current_user.
1382  *
1383  *              The result is a boolean value: true if user has the indicated
1384  *              privilege, false if not.
1385  */
1386
1387 /*
1388  * has_database_privilege_name_name
1389  *              Check user privileges on a database given
1390  *              name username, text databasename, and text priv name.
1391  */
1392 Datum
1393 has_database_privilege_name_name(PG_FUNCTION_ARGS)
1394 {
1395         Name            username = PG_GETARG_NAME(0);
1396         text       *databasename = PG_GETARG_TEXT_P(1);
1397         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1398         Oid                     roleid;
1399         Oid                     databaseoid;
1400         AclMode         mode;
1401         AclResult       aclresult;
1402
1403         roleid = get_roleid_checked(NameStr(*username));
1404
1405         databaseoid = convert_database_name(databasename);
1406         mode = convert_database_priv_string(priv_type_text);
1407
1408         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1409
1410         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1411 }
1412
1413 /*
1414  * has_database_privilege_name
1415  *              Check user privileges on a database given
1416  *              text databasename and text priv name.
1417  *              current_user is assumed
1418  */
1419 Datum
1420 has_database_privilege_name(PG_FUNCTION_ARGS)
1421 {
1422         text       *databasename = PG_GETARG_TEXT_P(0);
1423         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1424         Oid                     roleid;
1425         Oid                     databaseoid;
1426         AclMode         mode;
1427         AclResult       aclresult;
1428
1429         roleid = GetUserId();
1430         databaseoid = convert_database_name(databasename);
1431         mode = convert_database_priv_string(priv_type_text);
1432
1433         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1434
1435         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1436 }
1437
1438 /*
1439  * has_database_privilege_name_id
1440  *              Check user privileges on a database given
1441  *              name usename, database oid, and text priv name.
1442  */
1443 Datum
1444 has_database_privilege_name_id(PG_FUNCTION_ARGS)
1445 {
1446         Name            username = PG_GETARG_NAME(0);
1447         Oid                     databaseoid = PG_GETARG_OID(1);
1448         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1449         Oid                     roleid;
1450         AclMode         mode;
1451         AclResult       aclresult;
1452
1453         roleid = get_roleid_checked(NameStr(*username));
1454
1455         mode = convert_database_priv_string(priv_type_text);
1456
1457         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1458
1459         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1460 }
1461
1462 /*
1463  * has_database_privilege_id
1464  *              Check user privileges on a database given
1465  *              database oid, and text priv name.
1466  *              current_user is assumed
1467  */
1468 Datum
1469 has_database_privilege_id(PG_FUNCTION_ARGS)
1470 {
1471         Oid                     databaseoid = PG_GETARG_OID(0);
1472         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1473         Oid                     roleid;
1474         AclMode         mode;
1475         AclResult       aclresult;
1476
1477         roleid = GetUserId();
1478         mode = convert_database_priv_string(priv_type_text);
1479
1480         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1481
1482         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1483 }
1484
1485 /*
1486  * has_database_privilege_id_name
1487  *              Check user privileges on a database given
1488  *              roleid, text databasename, and text priv name.
1489  */
1490 Datum
1491 has_database_privilege_id_name(PG_FUNCTION_ARGS)
1492 {
1493         Oid                     roleid = PG_GETARG_OID(0);
1494         text       *databasename = PG_GETARG_TEXT_P(1);
1495         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1496         Oid                     databaseoid;
1497         AclMode         mode;
1498         AclResult       aclresult;
1499
1500         databaseoid = convert_database_name(databasename);
1501         mode = convert_database_priv_string(priv_type_text);
1502
1503         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1504
1505         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1506 }
1507
1508 /*
1509  * has_database_privilege_id_id
1510  *              Check user privileges on a database given
1511  *              roleid, database oid, and text priv name.
1512  */
1513 Datum
1514 has_database_privilege_id_id(PG_FUNCTION_ARGS)
1515 {
1516         Oid                     roleid = PG_GETARG_OID(0);
1517         Oid                     databaseoid = PG_GETARG_OID(1);
1518         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1519         AclMode         mode;
1520         AclResult       aclresult;
1521
1522         mode = convert_database_priv_string(priv_type_text);
1523
1524         aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
1525
1526         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1527 }
1528
1529 /*
1530  *              Support routines for has_database_privilege family.
1531  */
1532
1533 /*
1534  * Given a database name expressed as a string, look it up and return Oid
1535  */
1536 static Oid
1537 convert_database_name(text *databasename)
1538 {
1539         char       *dbname;
1540         Oid                     oid;
1541
1542         dbname = DatumGetCString(DirectFunctionCall1(textout,
1543                                                                                  PointerGetDatum(databasename)));
1544
1545         oid = get_database_oid(dbname);
1546         if (!OidIsValid(oid))
1547                 ereport(ERROR,
1548                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
1549                                  errmsg("database \"%s\" does not exist", dbname)));
1550
1551         return oid;
1552 }
1553
1554 /*
1555  * convert_database_priv_string
1556  *              Convert text string to AclMode value.
1557  */
1558 static AclMode
1559 convert_database_priv_string(text *priv_type_text)
1560 {
1561         char       *priv_type;
1562
1563         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1564                                                                            PointerGetDatum(priv_type_text)));
1565
1566         /*
1567          * Return mode from priv_type string
1568          */
1569         if (pg_strcasecmp(priv_type, "CREATE") == 0)
1570                 return ACL_CREATE;
1571         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1572                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1573
1574         if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
1575                 return ACL_CREATE_TEMP;
1576         if (pg_strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
1577                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1578
1579         if (pg_strcasecmp(priv_type, "TEMP") == 0)
1580                 return ACL_CREATE_TEMP;
1581         if (pg_strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
1582                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1583
1584         ereport(ERROR,
1585                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1586                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1587         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1588 }
1589
1590
1591 /*
1592  * has_function_privilege variants
1593  *              These are all named "has_function_privilege" at the SQL level.
1594  *              They take various combinations of function name, function OID,
1595  *              user name, user OID, or implicit user = current_user.
1596  *
1597  *              The result is a boolean value: true if user has the indicated
1598  *              privilege, false if not.
1599  */
1600
1601 /*
1602  * has_function_privilege_name_name
1603  *              Check user privileges on a function given
1604  *              name username, text functionname, and text priv name.
1605  */
1606 Datum
1607 has_function_privilege_name_name(PG_FUNCTION_ARGS)
1608 {
1609         Name            username = PG_GETARG_NAME(0);
1610         text       *functionname = PG_GETARG_TEXT_P(1);
1611         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1612         Oid                     roleid;
1613         Oid                     functionoid;
1614         AclMode         mode;
1615         AclResult       aclresult;
1616
1617         roleid = get_roleid_checked(NameStr(*username));
1618
1619         functionoid = convert_function_name(functionname);
1620         mode = convert_function_priv_string(priv_type_text);
1621
1622         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1623
1624         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1625 }
1626
1627 /*
1628  * has_function_privilege_name
1629  *              Check user privileges on a function given
1630  *              text functionname and text priv name.
1631  *              current_user is assumed
1632  */
1633 Datum
1634 has_function_privilege_name(PG_FUNCTION_ARGS)
1635 {
1636         text       *functionname = PG_GETARG_TEXT_P(0);
1637         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1638         Oid                     roleid;
1639         Oid                     functionoid;
1640         AclMode         mode;
1641         AclResult       aclresult;
1642
1643         roleid = GetUserId();
1644         functionoid = convert_function_name(functionname);
1645         mode = convert_function_priv_string(priv_type_text);
1646
1647         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1648
1649         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1650 }
1651
1652 /*
1653  * has_function_privilege_name_id
1654  *              Check user privileges on a function given
1655  *              name usename, function oid, and text priv name.
1656  */
1657 Datum
1658 has_function_privilege_name_id(PG_FUNCTION_ARGS)
1659 {
1660         Name            username = PG_GETARG_NAME(0);
1661         Oid                     functionoid = PG_GETARG_OID(1);
1662         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1663         Oid                     roleid;
1664         AclMode         mode;
1665         AclResult       aclresult;
1666
1667         roleid = get_roleid_checked(NameStr(*username));
1668
1669         mode = convert_function_priv_string(priv_type_text);
1670
1671         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1672
1673         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1674 }
1675
1676 /*
1677  * has_function_privilege_id
1678  *              Check user privileges on a function given
1679  *              function oid, and text priv name.
1680  *              current_user is assumed
1681  */
1682 Datum
1683 has_function_privilege_id(PG_FUNCTION_ARGS)
1684 {
1685         Oid                     functionoid = PG_GETARG_OID(0);
1686         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1687         Oid                     roleid;
1688         AclMode         mode;
1689         AclResult       aclresult;
1690
1691         roleid = GetUserId();
1692         mode = convert_function_priv_string(priv_type_text);
1693
1694         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1695
1696         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1697 }
1698
1699 /*
1700  * has_function_privilege_id_name
1701  *              Check user privileges on a function given
1702  *              roleid, text functionname, and text priv name.
1703  */
1704 Datum
1705 has_function_privilege_id_name(PG_FUNCTION_ARGS)
1706 {
1707         Oid                     roleid = PG_GETARG_OID(0);
1708         text       *functionname = PG_GETARG_TEXT_P(1);
1709         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1710         Oid                     functionoid;
1711         AclMode         mode;
1712         AclResult       aclresult;
1713
1714         functionoid = convert_function_name(functionname);
1715         mode = convert_function_priv_string(priv_type_text);
1716
1717         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1718
1719         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1720 }
1721
1722 /*
1723  * has_function_privilege_id_id
1724  *              Check user privileges on a function given
1725  *              roleid, function oid, and text priv name.
1726  */
1727 Datum
1728 has_function_privilege_id_id(PG_FUNCTION_ARGS)
1729 {
1730         Oid                     roleid = PG_GETARG_OID(0);
1731         Oid                     functionoid = PG_GETARG_OID(1);
1732         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1733         AclMode         mode;
1734         AclResult       aclresult;
1735
1736         mode = convert_function_priv_string(priv_type_text);
1737
1738         aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
1739
1740         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1741 }
1742
1743 /*
1744  *              Support routines for has_function_privilege family.
1745  */
1746
1747 /*
1748  * Given a function name expressed as a string, look it up and return Oid
1749  */
1750 static Oid
1751 convert_function_name(text *functionname)
1752 {
1753         char       *funcname;
1754         Oid                     oid;
1755
1756         funcname = DatumGetCString(DirectFunctionCall1(textout,
1757                                                                                  PointerGetDatum(functionname)));
1758
1759         oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
1760                                                                                          CStringGetDatum(funcname)));
1761
1762         if (!OidIsValid(oid))
1763                 ereport(ERROR,
1764                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1765                                  errmsg("function \"%s\" does not exist", funcname)));
1766
1767         return oid;
1768 }
1769
1770 /*
1771  * convert_function_priv_string
1772  *              Convert text string to AclMode value.
1773  */
1774 static AclMode
1775 convert_function_priv_string(text *priv_type_text)
1776 {
1777         char       *priv_type;
1778
1779         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1780                                                                            PointerGetDatum(priv_type_text)));
1781
1782         /*
1783          * Return mode from priv_type string
1784          */
1785         if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
1786                 return ACL_EXECUTE;
1787         if (pg_strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
1788                 return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
1789
1790         ereport(ERROR,
1791                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1792                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1793         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1794 }
1795
1796
1797 /*
1798  * has_language_privilege variants
1799  *              These are all named "has_language_privilege" at the SQL level.
1800  *              They take various combinations of language name, language OID,
1801  *              user name, user OID, or implicit user = current_user.
1802  *
1803  *              The result is a boolean value: true if user has the indicated
1804  *              privilege, false if not.
1805  */
1806
1807 /*
1808  * has_language_privilege_name_name
1809  *              Check user privileges on a language given
1810  *              name username, text languagename, and text priv name.
1811  */
1812 Datum
1813 has_language_privilege_name_name(PG_FUNCTION_ARGS)
1814 {
1815         Name            username = PG_GETARG_NAME(0);
1816         text       *languagename = PG_GETARG_TEXT_P(1);
1817         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1818         Oid                     roleid;
1819         Oid                     languageoid;
1820         AclMode         mode;
1821         AclResult       aclresult;
1822
1823         roleid = get_roleid_checked(NameStr(*username));
1824
1825         languageoid = convert_language_name(languagename);
1826         mode = convert_language_priv_string(priv_type_text);
1827
1828         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1829
1830         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1831 }
1832
1833 /*
1834  * has_language_privilege_name
1835  *              Check user privileges on a language given
1836  *              text languagename and text priv name.
1837  *              current_user is assumed
1838  */
1839 Datum
1840 has_language_privilege_name(PG_FUNCTION_ARGS)
1841 {
1842         text       *languagename = PG_GETARG_TEXT_P(0);
1843         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1844         Oid                     roleid;
1845         Oid                     languageoid;
1846         AclMode         mode;
1847         AclResult       aclresult;
1848
1849         roleid = GetUserId();
1850         languageoid = convert_language_name(languagename);
1851         mode = convert_language_priv_string(priv_type_text);
1852
1853         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1854
1855         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1856 }
1857
1858 /*
1859  * has_language_privilege_name_id
1860  *              Check user privileges on a language given
1861  *              name usename, language oid, and text priv name.
1862  */
1863 Datum
1864 has_language_privilege_name_id(PG_FUNCTION_ARGS)
1865 {
1866         Name            username = PG_GETARG_NAME(0);
1867         Oid                     languageoid = PG_GETARG_OID(1);
1868         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1869         Oid                     roleid;
1870         AclMode         mode;
1871         AclResult       aclresult;
1872
1873         roleid = get_roleid_checked(NameStr(*username));
1874
1875         mode = convert_language_priv_string(priv_type_text);
1876
1877         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1878
1879         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1880 }
1881
1882 /*
1883  * has_language_privilege_id
1884  *              Check user privileges on a language given
1885  *              language oid, and text priv name.
1886  *              current_user is assumed
1887  */
1888 Datum
1889 has_language_privilege_id(PG_FUNCTION_ARGS)
1890 {
1891         Oid                     languageoid = PG_GETARG_OID(0);
1892         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1893         Oid                     roleid;
1894         AclMode         mode;
1895         AclResult       aclresult;
1896
1897         roleid = GetUserId();
1898         mode = convert_language_priv_string(priv_type_text);
1899
1900         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1901
1902         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1903 }
1904
1905 /*
1906  * has_language_privilege_id_name
1907  *              Check user privileges on a language given
1908  *              roleid, text languagename, and text priv name.
1909  */
1910 Datum
1911 has_language_privilege_id_name(PG_FUNCTION_ARGS)
1912 {
1913         Oid                     roleid = PG_GETARG_OID(0);
1914         text       *languagename = PG_GETARG_TEXT_P(1);
1915         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1916         Oid                     languageoid;
1917         AclMode         mode;
1918         AclResult       aclresult;
1919
1920         languageoid = convert_language_name(languagename);
1921         mode = convert_language_priv_string(priv_type_text);
1922
1923         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1924
1925         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1926 }
1927
1928 /*
1929  * has_language_privilege_id_id
1930  *              Check user privileges on a language given
1931  *              roleid, language oid, and text priv name.
1932  */
1933 Datum
1934 has_language_privilege_id_id(PG_FUNCTION_ARGS)
1935 {
1936         Oid                     roleid = PG_GETARG_OID(0);
1937         Oid                     languageoid = PG_GETARG_OID(1);
1938         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1939         AclMode         mode;
1940         AclResult       aclresult;
1941
1942         mode = convert_language_priv_string(priv_type_text);
1943
1944         aclresult = pg_language_aclcheck(languageoid, roleid, mode);
1945
1946         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1947 }
1948
1949 /*
1950  *              Support routines for has_language_privilege family.
1951  */
1952
1953 /*
1954  * Given a language name expressed as a string, look it up and return Oid
1955  */
1956 static Oid
1957 convert_language_name(text *languagename)
1958 {
1959         char       *langname;
1960         Oid                     oid;
1961
1962         langname = DatumGetCString(DirectFunctionCall1(textout,
1963                                                                                  PointerGetDatum(languagename)));
1964
1965         oid = GetSysCacheOid(LANGNAME,
1966                                                  CStringGetDatum(langname),
1967                                                  0, 0, 0);
1968         if (!OidIsValid(oid))
1969                 ereport(ERROR,
1970                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1971                                  errmsg("language \"%s\" does not exist", langname)));
1972
1973         return oid;
1974 }
1975
1976 /*
1977  * convert_language_priv_string
1978  *              Convert text string to AclMode value.
1979  */
1980 static AclMode
1981 convert_language_priv_string(text *priv_type_text)
1982 {
1983         char       *priv_type;
1984
1985         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1986                                                                            PointerGetDatum(priv_type_text)));
1987
1988         /*
1989          * Return mode from priv_type string
1990          */
1991         if (pg_strcasecmp(priv_type, "USAGE") == 0)
1992                 return ACL_USAGE;
1993         if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
1994                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
1995
1996         ereport(ERROR,
1997                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1998                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1999         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2000 }
2001
2002
2003 /*
2004  * has_schema_privilege variants
2005  *              These are all named "has_schema_privilege" at the SQL level.
2006  *              They take various combinations of schema name, schema OID,
2007  *              user name, user OID, or implicit user = current_user.
2008  *
2009  *              The result is a boolean value: true if user has the indicated
2010  *              privilege, false if not.
2011  */
2012
2013 /*
2014  * has_schema_privilege_name_name
2015  *              Check user privileges on a schema given
2016  *              name username, text schemaname, and text priv name.
2017  */
2018 Datum
2019 has_schema_privilege_name_name(PG_FUNCTION_ARGS)
2020 {
2021         Name            username = PG_GETARG_NAME(0);
2022         text       *schemaname = PG_GETARG_TEXT_P(1);
2023         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2024         Oid                     roleid;
2025         Oid                     schemaoid;
2026         AclMode         mode;
2027         AclResult       aclresult;
2028
2029         roleid = get_roleid_checked(NameStr(*username));
2030
2031         schemaoid = convert_schema_name(schemaname);
2032         mode = convert_schema_priv_string(priv_type_text);
2033
2034         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2035
2036         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2037 }
2038
2039 /*
2040  * has_schema_privilege_name
2041  *              Check user privileges on a schema given
2042  *              text schemaname and text priv name.
2043  *              current_user is assumed
2044  */
2045 Datum
2046 has_schema_privilege_name(PG_FUNCTION_ARGS)
2047 {
2048         text       *schemaname = PG_GETARG_TEXT_P(0);
2049         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2050         Oid                     roleid;
2051         Oid                     schemaoid;
2052         AclMode         mode;
2053         AclResult       aclresult;
2054
2055         roleid = GetUserId();
2056         schemaoid = convert_schema_name(schemaname);
2057         mode = convert_schema_priv_string(priv_type_text);
2058
2059         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2060
2061         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2062 }
2063
2064 /*
2065  * has_schema_privilege_name_id
2066  *              Check user privileges on a schema given
2067  *              name usename, schema oid, and text priv name.
2068  */
2069 Datum
2070 has_schema_privilege_name_id(PG_FUNCTION_ARGS)
2071 {
2072         Name            username = PG_GETARG_NAME(0);
2073         Oid                     schemaoid = PG_GETARG_OID(1);
2074         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2075         Oid                     roleid;
2076         AclMode         mode;
2077         AclResult       aclresult;
2078
2079         roleid = get_roleid_checked(NameStr(*username));
2080
2081         mode = convert_schema_priv_string(priv_type_text);
2082
2083         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2084
2085         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2086 }
2087
2088 /*
2089  * has_schema_privilege_id
2090  *              Check user privileges on a schema given
2091  *              schema oid, and text priv name.
2092  *              current_user is assumed
2093  */
2094 Datum
2095 has_schema_privilege_id(PG_FUNCTION_ARGS)
2096 {
2097         Oid                     schemaoid = PG_GETARG_OID(0);
2098         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2099         Oid                     roleid;
2100         AclMode         mode;
2101         AclResult       aclresult;
2102
2103         roleid = GetUserId();
2104         mode = convert_schema_priv_string(priv_type_text);
2105
2106         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2107
2108         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2109 }
2110
2111 /*
2112  * has_schema_privilege_id_name
2113  *              Check user privileges on a schema given
2114  *              roleid, text schemaname, and text priv name.
2115  */
2116 Datum
2117 has_schema_privilege_id_name(PG_FUNCTION_ARGS)
2118 {
2119         Oid                     roleid = PG_GETARG_OID(0);
2120         text       *schemaname = PG_GETARG_TEXT_P(1);
2121         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2122         Oid                     schemaoid;
2123         AclMode         mode;
2124         AclResult       aclresult;
2125
2126         schemaoid = convert_schema_name(schemaname);
2127         mode = convert_schema_priv_string(priv_type_text);
2128
2129         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2130
2131         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2132 }
2133
2134 /*
2135  * has_schema_privilege_id_id
2136  *              Check user privileges on a schema given
2137  *              roleid, schema oid, and text priv name.
2138  */
2139 Datum
2140 has_schema_privilege_id_id(PG_FUNCTION_ARGS)
2141 {
2142         Oid                     roleid = PG_GETARG_OID(0);
2143         Oid                     schemaoid = PG_GETARG_OID(1);
2144         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2145         AclMode         mode;
2146         AclResult       aclresult;
2147
2148         mode = convert_schema_priv_string(priv_type_text);
2149
2150         aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
2151
2152         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2153 }
2154
2155 /*
2156  *              Support routines for has_schema_privilege family.
2157  */
2158
2159 /*
2160  * Given a schema name expressed as a string, look it up and return Oid
2161  */
2162 static Oid
2163 convert_schema_name(text *schemaname)
2164 {
2165         char       *nspname;
2166         Oid                     oid;
2167
2168         nspname = DatumGetCString(DirectFunctionCall1(textout,
2169                                                                                    PointerGetDatum(schemaname)));
2170
2171         oid = GetSysCacheOid(NAMESPACENAME,
2172                                                  CStringGetDatum(nspname),
2173                                                  0, 0, 0);
2174         if (!OidIsValid(oid))
2175                 ereport(ERROR,
2176                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
2177                                  errmsg("schema \"%s\" does not exist", nspname)));
2178
2179         return oid;
2180 }
2181
2182 /*
2183  * convert_schema_priv_string
2184  *              Convert text string to AclMode value.
2185  */
2186 static AclMode
2187 convert_schema_priv_string(text *priv_type_text)
2188 {
2189         char       *priv_type;
2190
2191         priv_type = DatumGetCString(DirectFunctionCall1(textout,
2192                                                                            PointerGetDatum(priv_type_text)));
2193
2194         /*
2195          * Return mode from priv_type string
2196          */
2197         if (pg_strcasecmp(priv_type, "CREATE") == 0)
2198                 return ACL_CREATE;
2199         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
2200                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
2201
2202         if (pg_strcasecmp(priv_type, "USAGE") == 0)
2203                 return ACL_USAGE;
2204         if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
2205                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
2206
2207         ereport(ERROR,
2208                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2209                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
2210         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2211 }
2212
2213 /*
2214  * has_tablespace_privilege variants
2215  *              These are all named "has_tablespace_privilege" at the SQL level.
2216  *              They take various combinations of tablespace name, tablespace OID,
2217  *              user name, user OID, or implicit user = current_user.
2218  *
2219  *              The result is a boolean value: true if user has the indicated
2220  *              privilege, false if not.
2221  */
2222
2223 /*
2224  * has_tablespace_privilege_name_name
2225  *              Check user privileges on a tablespace given
2226  *              name username, text tablespacename, and text priv name.
2227  */
2228 Datum
2229 has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
2230 {
2231         Name            username = PG_GETARG_NAME(0);
2232         text       *tablespacename = PG_GETARG_TEXT_P(1);
2233         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2234         Oid                     roleid;
2235         Oid                     tablespaceoid;
2236         AclMode         mode;
2237         AclResult       aclresult;
2238
2239         roleid = get_roleid_checked(NameStr(*username));
2240
2241         tablespaceoid = convert_tablespace_name(tablespacename);
2242         mode = convert_tablespace_priv_string(priv_type_text);
2243
2244         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2245
2246         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2247 }
2248
2249 /*
2250  * has_tablespace_privilege_name
2251  *              Check user privileges on a tablespace given
2252  *              text tablespacename and text priv name.
2253  *              current_user is assumed
2254  */
2255 Datum
2256 has_tablespace_privilege_name(PG_FUNCTION_ARGS)
2257 {
2258         text       *tablespacename = PG_GETARG_TEXT_P(0);
2259         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2260         Oid                     roleid;
2261         Oid                     tablespaceoid;
2262         AclMode         mode;
2263         AclResult       aclresult;
2264
2265         roleid = GetUserId();
2266         tablespaceoid = convert_tablespace_name(tablespacename);
2267         mode = convert_tablespace_priv_string(priv_type_text);
2268
2269         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2270
2271         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2272 }
2273
2274 /*
2275  * has_tablespace_privilege_name_id
2276  *              Check user privileges on a tablespace given
2277  *              name usename, tablespace oid, and text priv name.
2278  */
2279 Datum
2280 has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
2281 {
2282         Name            username = PG_GETARG_NAME(0);
2283         Oid                     tablespaceoid = PG_GETARG_OID(1);
2284         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2285         Oid                     roleid;
2286         AclMode         mode;
2287         AclResult       aclresult;
2288
2289         roleid = get_roleid_checked(NameStr(*username));
2290
2291         mode = convert_tablespace_priv_string(priv_type_text);
2292
2293         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2294
2295         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2296 }
2297
2298 /*
2299  * has_tablespace_privilege_id
2300  *              Check user privileges on a tablespace given
2301  *              tablespace oid, and text priv name.
2302  *              current_user is assumed
2303  */
2304 Datum
2305 has_tablespace_privilege_id(PG_FUNCTION_ARGS)
2306 {
2307         Oid                     tablespaceoid = PG_GETARG_OID(0);
2308         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2309         Oid                             roleid;
2310         AclMode         mode;
2311         AclResult       aclresult;
2312
2313         roleid = GetUserId();
2314         mode = convert_tablespace_priv_string(priv_type_text);
2315
2316         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2317
2318         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2319 }
2320
2321 /*
2322  * has_tablespace_privilege_id_name
2323  *              Check user privileges on a tablespace given
2324  *              roleid, text tablespacename, and text priv name.
2325  */
2326 Datum
2327 has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
2328 {
2329         Oid                     roleid = PG_GETARG_OID(0);
2330         text       *tablespacename = PG_GETARG_TEXT_P(1);
2331         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2332         Oid                     tablespaceoid;
2333         AclMode         mode;
2334         AclResult       aclresult;
2335
2336         tablespaceoid = convert_tablespace_name(tablespacename);
2337         mode = convert_tablespace_priv_string(priv_type_text);
2338
2339         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2340
2341         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2342 }
2343
2344 /*
2345  * has_tablespace_privilege_id_id
2346  *              Check user privileges on a tablespace given
2347  *              roleid, tablespace oid, and text priv name.
2348  */
2349 Datum
2350 has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
2351 {
2352         Oid                     roleid = PG_GETARG_OID(0);
2353         Oid                     tablespaceoid = PG_GETARG_OID(1);
2354         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2355         AclMode         mode;
2356         AclResult       aclresult;
2357
2358         mode = convert_tablespace_priv_string(priv_type_text);
2359
2360         aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
2361
2362         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2363 }
2364
2365 /*
2366  *              Support routines for has_tablespace_privilege family.
2367  */
2368
2369 /*
2370  * Given a tablespace name expressed as a string, look it up and return Oid
2371  */
2372 static Oid
2373 convert_tablespace_name(text *tablespacename)
2374 {
2375         char       *spcname;
2376         Oid                     oid;
2377
2378         spcname = DatumGetCString(DirectFunctionCall1(textout,
2379                                                                            PointerGetDatum(tablespacename)));
2380         oid = get_tablespace_oid(spcname);
2381
2382         if (!OidIsValid(oid))
2383                 ereport(ERROR,
2384                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2385                                  errmsg("tablespace \"%s\" does not exist", spcname)));
2386
2387         return oid;
2388 }
2389
2390 /*
2391  * convert_tablespace_priv_string
2392  *              Convert text string to AclMode value.
2393  */
2394 static AclMode
2395 convert_tablespace_priv_string(text *priv_type_text)
2396 {
2397         char       *priv_type;
2398
2399         priv_type = DatumGetCString(DirectFunctionCall1(textout,
2400                                                                            PointerGetDatum(priv_type_text)));
2401
2402         /*
2403          * Return mode from priv_type string
2404          */
2405         if (pg_strcasecmp(priv_type, "CREATE") == 0)
2406                 return ACL_CREATE;
2407         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
2408                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
2409
2410         ereport(ERROR,
2411                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2412                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
2413         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2414 }
2415
2416 void
2417 InitializeAcl(void)
2418 {
2419         if (!IsBootstrapProcessingMode())
2420         {
2421                 /*
2422                  * In normal mode, set a callback on any syscache
2423                  * invalidation of pg_auth_members rows
2424                  */
2425                 CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
2426                                                                           RolMemCacheCallback,
2427                                                                           (Datum) 0);
2428
2429             /* Force role/member cache to be recomputed on next use */
2430             rolmemcacheValid = false;
2431         }
2432 }
2433
2434 /*
2435  * RolMemCacheCallback
2436  *              Syscache inval callback function
2437  */
2438 static void
2439 RolMemCacheCallback(Datum arg, Oid relid)
2440 {
2441             /* Force role/member cache to be recomputed on next use */
2442             rolmemcacheValid = false;
2443 }
2444
2445
2446 /* 
2447  * recomputeRolMemCache - recompute the role/member cache if needed 
2448  */
2449 static void
2450 recomputeRolMemCache(Oid roleid)
2451 {
2452         int             i;
2453         Oid                     memberOid;
2454         List            *roles_list_hunt = NIL;
2455         List            *roles_list = NIL;
2456         List            *newrolmemcache;
2457         CatCList        *memlist;
2458         MemoryContext   oldctx;
2459
2460         /* Do nothing if rolmemcache is already valid */
2461         if (rolmemcacheValid && rolmemRole == roleid)
2462                 return;
2463
2464         if (rolmemRole != roleid)
2465                 rolmemcacheValid = false;
2466
2467         /* 
2468          * Find all the roles which this role is a member of,
2469          * including multi-level recursion
2470          */
2471
2472         /*
2473          * Include the current role itself to simplify checks
2474          * later on, also should be at the head so lookup should
2475          * be fast.
2476          */
2477         roles_list = lappend_oid(roles_list, roleid);
2478         roles_list_hunt = lappend_oid(roles_list_hunt, roleid);
2479         
2480         while (roles_list_hunt)
2481         {
2482                 memberOid = linitial_oid(roles_list_hunt);
2483                 memlist = SearchSysCacheList(AUTHMEMMEMROLE, 1,
2484                                                                          ObjectIdGetDatum(memberOid),
2485                                                                          0, 0, 0);
2486                 for (i = 0; i < memlist->n_members; i++) {
2487                         HeapTuple roletup = &memlist->members[i]->tuple;
2488                         Form_pg_auth_members rolemem = (Form_pg_auth_members) GETSTRUCT(roletup);
2489
2490                         if (!list_member_oid(roles_list,rolemem->roleid)) {
2491                                 roles_list = lappend_oid(roles_list,rolemem->roleid);
2492                                 roles_list_hunt = lappend_oid(roles_list_hunt,rolemem->roleid);
2493                         }
2494                 }
2495                 roles_list_hunt = list_delete_oid(roles_list_hunt, memberOid);
2496                 ReleaseSysCacheList(memlist);
2497         }
2498
2499         /*
2500          * Now that we've built the list of role Oids this
2501          * role is a member of, save it in permanent storage
2502          */
2503         oldctx = MemoryContextSwitchTo(TopMemoryContext);
2504         newrolmemcache = list_copy(roles_list);
2505         MemoryContextSwitchTo(oldctx);
2506
2507         /*
2508          * Now safe to assign to state variable
2509          */
2510         list_free(rolmemcache);
2511         rolmemcache = newrolmemcache;
2512
2513         /*
2514          * Mark as valid
2515          */
2516         rolmemRole = roleid;
2517         rolmemcacheValid = true;
2518
2519         /* Clean up */
2520         list_free(roles_list);
2521 }