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