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