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