]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Tag appropriate files for rc3
[postgresql] / src / backend / utils / adt / acl.c
1 /*-------------------------------------------------------------------------
2  *
3  * acl.c
4  *        Basic access control list data structures manipulation routines.
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.113 2004/12/31 22:01:21 pgsql 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 the
572          * 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 can
707          * 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 the
749          * 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
783          * the input.)  dst is the next output slot, targ is the currently
784          * considered input slot (always >= dst), and src scans entries to the
785          * right of targ looking for duplicates.  Once an entry has been
786          * emitted to dst it is known duplicate-free and need not be
787          * considered anymore.
788          */
789         if (newpresent)
790         {
791                 dst = 0;
792                 for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
793                 {
794                         /* ignore if deleted in an earlier pass */
795                         if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
796                                 continue;
797                         /* find and merge any duplicates */
798                         for (src = targ + 1, src_aip = targ_aip + 1; src < num;
799                                  src++, src_aip++)
800                         {
801                                 if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
802                                         continue;
803                                 if (aclitem_match(targ_aip, src_aip))
804                                 {
805                                         ACLITEM_SET_RIGHTS(*targ_aip,
806                                                                            ACLITEM_GET_RIGHTS(*targ_aip) |
807                                                                            ACLITEM_GET_RIGHTS(*src_aip));
808                                         /* mark the duplicate deleted */
809                                         ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
810                                 }
811                         }
812                         /* and emit to output */
813                         new_aip[dst] = *targ_aip;
814                         dst++;
815                 }
816                 /* Adjust array size to be 'dst' items */
817                 ARR_DIMS(new_acl)[0] = dst;
818                 ARR_SIZE(new_acl) = ACL_N_SIZE(dst);
819         }
820
821         return new_acl;
822 }
823
824
825 /*
826  * When granting grant options, we must disallow attempts to set up circular
827  * chains of grant options.  Suppose A (the object owner) grants B some
828  * privileges with grant option, and B re-grants them to C.  If C could
829  * grant the privileges to B as well, then A would be unable to effectively
830  * revoke the privileges from B, since recursive_revoke would consider that
831  * B still has 'em from C.
832  *
833  * We check for this by recursively deleting all grant options belonging to
834  * the target grantee, and then seeing if the would-be grantor still has the
835  * grant option or not.
836  */
837 static void
838 check_circularity(const Acl *old_acl, const AclItem *mod_aip,
839                                   AclId ownerid)
840 {
841         Acl                *acl;
842         AclItem    *aip;
843         int                     i,
844                                 num;
845         AclMode         own_privs;
846
847         /*
848          * For now, grant options can only be granted to users, not groups or
849          * PUBLIC.      Otherwise we'd have to work a bit harder here.
850          */
851         Assert(ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID);
852
853         /* The owner always has grant options, no need to check */
854         if (mod_aip->ai_grantor == ownerid)
855                 return;
856
857         /* Make a working copy */
858         acl = allocacl(ACL_NUM(old_acl));
859         memcpy(acl, old_acl, ACL_SIZE(old_acl));
860
861         /* Zap all grant options of target grantee, plus what depends on 'em */
862 cc_restart:
863         num = ACL_NUM(acl);
864         aip = ACL_DAT(acl);
865         for (i = 0; i < num; i++)
866         {
867                 if (ACLITEM_GET_IDTYPE(aip[i]) == ACL_IDTYPE_UID &&
868                         aip[i].ai_grantee == mod_aip->ai_grantee &&
869                         ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
870                 {
871                         Acl                *new_acl;
872
873                         /* We'll actually zap ordinary privs too, but no matter */
874                         new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
875                                                                 ownerid, DROP_CASCADE);
876
877                         pfree(acl);
878                         acl = new_acl;
879
880                         goto cc_restart;
881                 }
882         }
883
884         /* Now we can compute grantor's independently-derived privileges */
885         own_privs = aclmask(acl,
886                                                 mod_aip->ai_grantor,
887                                                 ownerid,
888                                         ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
889                                                 ACLMASK_ALL);
890         own_privs = ACL_OPTION_TO_PRIVS(own_privs);
891
892         if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
893                 ereport(ERROR,
894                                 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
895                                  errmsg("grant options cannot be granted back to your own grantor")));
896
897         pfree(acl);
898 }
899
900
901 /*
902  * Ensure that no privilege is "abandoned".  A privilege is abandoned
903  * if the user that granted the privilege loses the grant option.  (So
904  * the chain through which it was granted is broken.)  Either the
905  * abandoned privileges are revoked as well, or an error message is
906  * printed, depending on the drop behavior option.
907  *
908  *      acl: the input ACL list
909  *      grantee: the user from whom some grant options have been revoked
910  *      revoke_privs: the grant options being revoked
911  *      ownerid: AclId of object owner
912  *      behavior: RESTRICT or CASCADE behavior for recursive removal
913  *
914  * The input Acl object is pfree'd if replaced.
915  */
916 static Acl *
917 recursive_revoke(Acl *acl,
918                                  AclId grantee,
919                                  AclMode revoke_privs,
920                                  AclId ownerid,
921                                  DropBehavior behavior)
922 {
923         AclMode         still_has;
924         AclItem    *aip;
925         int                     i,
926                                 num;
927
928         /* The owner can never truly lose grant options, so short-circuit */
929         if (grantee == ownerid)
930                 return acl;
931
932         /* The grantee might still have the privileges via another grantor */
933         still_has = aclmask(acl, grantee, ownerid,
934                                                 ACL_GRANT_OPTION_FOR(revoke_privs),
935                                                 ACLMASK_ALL);
936         revoke_privs &= ~still_has;
937         if (revoke_privs == ACL_NO_RIGHTS)
938                 return acl;
939
940 restart:
941         num = ACL_NUM(acl);
942         aip = ACL_DAT(acl);
943         for (i = 0; i < num; i++)
944         {
945                 if (aip[i].ai_grantor == grantee
946                         && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
947                 {
948                         AclItem         mod_acl;
949                         Acl                *new_acl;
950
951                         if (behavior == DROP_RESTRICT)
952                                 ereport(ERROR,
953                                                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
954                                                  errmsg("dependent privileges exist"),
955                                                  errhint("Use CASCADE to revoke them too.")));
956
957                         mod_acl.ai_grantor = grantee;
958                         mod_acl.ai_grantee = aip[i].ai_grantee;
959                         ACLITEM_SET_PRIVS_IDTYPE(mod_acl,
960                                                                          revoke_privs,
961                                                                          revoke_privs,
962                                                                          ACLITEM_GET_IDTYPE(aip[i]));
963
964                         new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
965                                                                 ownerid, behavior);
966
967                         pfree(acl);
968                         acl = new_acl;
969
970                         goto restart;
971                 }
972         }
973
974         return acl;
975 }
976
977
978 /*
979  * aclmask --- compute bitmask of all privileges held by userid.
980  *
981  * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
982  * held by the given userid according to the given ACL list, ANDed
983  * with 'mask'.  (The point of passing 'mask' is to let the routine
984  * exit early if all privileges of interest have been found.)
985  *
986  * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
987  * is known true.  (This lets us exit soonest in cases where the
988  * caller is only going to test for zero or nonzero result.)
989  *
990  * Usage patterns:
991  *
992  * To see if any of a set of privileges are held:
993  *              if (aclmask(acl, userid, ownerid, privs, ACLMASK_ANY) != 0)
994  *
995  * To see if all of a set of privileges are held:
996  *              if (aclmask(acl, userid, ownerid, privs, ACLMASK_ALL) == privs)
997  *
998  * To determine exactly which of a set of privileges are held:
999  *              heldprivs = aclmask(acl, userid, ownerid, privs, ACLMASK_ALL);
1000  */
1001 AclMode
1002 aclmask(const Acl *acl, AclId userid, AclId ownerid,
1003                 AclMode mask, AclMaskHow how)
1004 {
1005         AclMode         result;
1006         AclMode         remaining;
1007         AclItem    *aidat;
1008         int                     i,
1009                                 num;
1010
1011         /*
1012          * Null ACL should not happen, since caller should have inserted
1013          * appropriate default
1014          */
1015         if (acl == NULL)
1016                 elog(ERROR, "null ACL");
1017
1018         /* Quick exit for mask == 0 */
1019         if (mask == 0)
1020                 return 0;
1021
1022         result = 0;
1023
1024         /* Owner always implicitly has all grant options */
1025         if (userid == ownerid)
1026         {
1027                 result = mask & ACLITEM_ALL_GOPTION_BITS;
1028                 if (result == mask)
1029                         return result;
1030         }
1031
1032         num = ACL_NUM(acl);
1033         aidat = ACL_DAT(acl);
1034
1035         /*
1036          * Check privileges granted directly to user or to public
1037          */
1038         for (i = 0; i < num; i++)
1039         {
1040                 AclItem    *aidata = &aidat[i];
1041
1042                 if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
1043                         || (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
1044                                 && aidata->ai_grantee == userid))
1045                 {
1046                         result |= (aidata->ai_privs & mask);
1047                         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1048                                 return result;
1049                 }
1050         }
1051
1052         /*
1053          * Check privileges granted via groups.  We do this in a separate pass
1054          * to minimize expensive lookups in pg_group.
1055          */
1056         remaining = (mask & ~result);
1057         for (i = 0; i < num; i++)
1058         {
1059                 AclItem    *aidata = &aidat[i];
1060
1061                 if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_GID
1062                         && (aidata->ai_privs & remaining)
1063                         && in_group(userid, aidata->ai_grantee))
1064                 {
1065                         result |= (aidata->ai_privs & mask);
1066                         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
1067                                 return result;
1068                         remaining = (mask & ~result);
1069                 }
1070         }
1071
1072         return result;
1073 }
1074
1075
1076 /*
1077  * Is user a member of group?
1078  */
1079 static bool
1080 in_group(AclId uid, AclId gid)
1081 {
1082         bool            result = false;
1083         HeapTuple       tuple;
1084         Datum           att;
1085         bool            isNull;
1086         IdList     *glist;
1087         AclId      *aidp;
1088         int                     i,
1089                                 num;
1090
1091         tuple = SearchSysCache(GROSYSID,
1092                                                    ObjectIdGetDatum(gid),
1093                                                    0, 0, 0);
1094         if (HeapTupleIsValid(tuple))
1095         {
1096                 att = SysCacheGetAttr(GROSYSID,
1097                                                           tuple,
1098                                                           Anum_pg_group_grolist,
1099                                                           &isNull);
1100                 if (!isNull)
1101                 {
1102                         /* be sure the IdList is not toasted */
1103                         glist = DatumGetIdListP(att);
1104                         /* scan it */
1105                         num = IDLIST_NUM(glist);
1106                         aidp = IDLIST_DAT(glist);
1107                         for (i = 0; i < num; ++i)
1108                         {
1109                                 if (aidp[i] == uid)
1110                                 {
1111                                         result = true;
1112                                         break;
1113                                 }
1114                         }
1115                         /* if IdList was toasted, free detoasted copy */
1116                         if ((Pointer) glist != DatumGetPointer(att))
1117                                 pfree(glist);
1118                 }
1119                 ReleaseSysCache(tuple);
1120         }
1121         else
1122                 ereport(WARNING,
1123                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1124                                  errmsg("group with ID %u does not exist", gid)));
1125         return result;
1126 }
1127
1128
1129 /*
1130  * aclinsert (exported function)
1131  */
1132 Datum
1133 aclinsert(PG_FUNCTION_ARGS)
1134 {
1135         ereport(ERROR,
1136                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1137                          errmsg("aclinsert is no longer supported")));
1138
1139         PG_RETURN_NULL();                       /* keep compiler quiet */
1140 }
1141
1142 Datum
1143 aclremove(PG_FUNCTION_ARGS)
1144 {
1145         ereport(ERROR,
1146                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1147                          errmsg("aclremove is no longer supported")));
1148
1149         PG_RETURN_NULL();                       /* keep compiler quiet */
1150 }
1151
1152 Datum
1153 aclcontains(PG_FUNCTION_ARGS)
1154 {
1155         Acl                *acl = PG_GETARG_ACL_P(0);
1156         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
1157         AclItem    *aidat;
1158         int                     i,
1159                                 num;
1160
1161         num = ACL_NUM(acl);
1162         aidat = ACL_DAT(acl);
1163         for (i = 0; i < num; ++i)
1164         {
1165                 if (aip->ai_grantee == aidat[i].ai_grantee
1166                         && ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
1167                         && aip->ai_grantor == aidat[i].ai_grantor
1168                         && (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
1169                         PG_RETURN_BOOL(true);
1170         }
1171         PG_RETURN_BOOL(false);
1172 }
1173
1174 Datum
1175 makeaclitem(PG_FUNCTION_ARGS)
1176 {
1177         int32           u_grantee = PG_GETARG_INT32(0);
1178         int32           g_grantee = PG_GETARG_INT32(1);
1179         int32           grantor = PG_GETARG_INT32(2);
1180         text       *privtext = PG_GETARG_TEXT_P(3);
1181         bool            goption = PG_GETARG_BOOL(4);
1182         AclItem    *aclitem;
1183         AclMode         priv;
1184
1185         priv = convert_priv_string(privtext);
1186
1187         aclitem = (AclItem *) palloc(sizeof(*aclitem));
1188
1189         if (u_grantee == 0 && g_grantee == 0)
1190         {
1191                 aclitem   ->ai_grantee = ACL_ID_WORLD;
1192
1193                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_WORLD);
1194         }
1195         else if (u_grantee != 0 && g_grantee != 0)
1196         {
1197                 ereport(ERROR,
1198                                 (errcode(ERRCODE_DATA_EXCEPTION),
1199                                  errmsg("cannot specify both user and group")));
1200         }
1201         else if (u_grantee != 0)
1202         {
1203                 aclitem   ->ai_grantee = u_grantee;
1204
1205                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_UID);
1206         }
1207         else
1208         /* (g_grantee != 0) */
1209         {
1210                 aclitem   ->ai_grantee = g_grantee;
1211
1212                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_GID);
1213         }
1214
1215         aclitem   ->ai_grantor = grantor;
1216
1217         ACLITEM_SET_PRIVS(*aclitem, priv);
1218         if (goption)
1219                 ACLITEM_SET_GOPTIONS(*aclitem, priv);
1220         else
1221                 ACLITEM_SET_GOPTIONS(*aclitem, ACL_NO_RIGHTS);
1222
1223         PG_RETURN_ACLITEM_P(aclitem);
1224 }
1225
1226 static AclMode
1227 convert_priv_string(text *priv_type_text)
1228 {
1229         char       *priv_type;
1230
1231         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1232                                                                            PointerGetDatum(priv_type_text)));
1233
1234         if (pg_strcasecmp(priv_type, "SELECT") == 0)
1235                 return ACL_SELECT;
1236         if (pg_strcasecmp(priv_type, "INSERT") == 0)
1237                 return ACL_INSERT;
1238         if (pg_strcasecmp(priv_type, "UPDATE") == 0)
1239                 return ACL_UPDATE;
1240         if (pg_strcasecmp(priv_type, "DELETE") == 0)
1241                 return ACL_DELETE;
1242         if (pg_strcasecmp(priv_type, "RULE") == 0)
1243                 return ACL_RULE;
1244         if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
1245                 return ACL_REFERENCES;
1246         if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
1247                 return ACL_TRIGGER;
1248         if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
1249                 return ACL_EXECUTE;
1250         if (pg_strcasecmp(priv_type, "USAGE") == 0)
1251                 return ACL_USAGE;
1252         if (pg_strcasecmp(priv_type, "CREATE") == 0)
1253                 return ACL_CREATE;
1254         if (pg_strcasecmp(priv_type, "TEMP") == 0)
1255                 return ACL_CREATE_TEMP;
1256         if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
1257                 return ACL_CREATE_TEMP;
1258
1259         ereport(ERROR,
1260                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1261                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1262         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1263 }
1264
1265
1266 /*
1267  * has_table_privilege variants
1268  *              These are all named "has_table_privilege" at the SQL level.
1269  *              They take various combinations of relation name, relation OID,
1270  *              user name, user sysid, or implicit user = current_user.
1271  *
1272  *              The result is a boolean value: true if user has the indicated
1273  *              privilege, false if not.
1274  */
1275
1276 /*
1277  * has_table_privilege_name_name
1278  *              Check user privileges on a table given
1279  *              name username, text tablename, and text priv name.
1280  */
1281 Datum
1282 has_table_privilege_name_name(PG_FUNCTION_ARGS)
1283 {
1284         Name            username = PG_GETARG_NAME(0);
1285         text       *tablename = PG_GETARG_TEXT_P(1);
1286         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1287         int32           usesysid;
1288         Oid                     tableoid;
1289         AclMode         mode;
1290         AclResult       aclresult;
1291
1292         usesysid = get_usesysid(NameStr(*username));
1293         tableoid = convert_table_name(tablename);
1294         mode = convert_table_priv_string(priv_type_text);
1295
1296         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1297
1298         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1299 }
1300
1301 /*
1302  * has_table_privilege_name
1303  *              Check user privileges on a table given
1304  *              text tablename and text priv name.
1305  *              current_user is assumed
1306  */
1307 Datum
1308 has_table_privilege_name(PG_FUNCTION_ARGS)
1309 {
1310         text       *tablename = PG_GETARG_TEXT_P(0);
1311         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1312         AclId           usesysid;
1313         Oid                     tableoid;
1314         AclMode         mode;
1315         AclResult       aclresult;
1316
1317         usesysid = GetUserId();
1318         tableoid = convert_table_name(tablename);
1319         mode = convert_table_priv_string(priv_type_text);
1320
1321         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1322
1323         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1324 }
1325
1326 /*
1327  * has_table_privilege_name_id
1328  *              Check user privileges on a table given
1329  *              name usename, table oid, and text priv name.
1330  */
1331 Datum
1332 has_table_privilege_name_id(PG_FUNCTION_ARGS)
1333 {
1334         Name            username = PG_GETARG_NAME(0);
1335         Oid                     tableoid = PG_GETARG_OID(1);
1336         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1337         int32           usesysid;
1338         AclMode         mode;
1339         AclResult       aclresult;
1340
1341         usesysid = get_usesysid(NameStr(*username));
1342         mode = convert_table_priv_string(priv_type_text);
1343
1344         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1345
1346         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1347 }
1348
1349 /*
1350  * has_table_privilege_id
1351  *              Check user privileges on a table given
1352  *              table oid, and text priv name.
1353  *              current_user is assumed
1354  */
1355 Datum
1356 has_table_privilege_id(PG_FUNCTION_ARGS)
1357 {
1358         Oid                     tableoid = PG_GETARG_OID(0);
1359         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1360         AclId           usesysid;
1361         AclMode         mode;
1362         AclResult       aclresult;
1363
1364         usesysid = GetUserId();
1365         mode = convert_table_priv_string(priv_type_text);
1366
1367         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1368
1369         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1370 }
1371
1372 /*
1373  * has_table_privilege_id_name
1374  *              Check user privileges on a table given
1375  *              usesysid, text tablename, and text priv name.
1376  */
1377 Datum
1378 has_table_privilege_id_name(PG_FUNCTION_ARGS)
1379 {
1380         int32           usesysid = PG_GETARG_INT32(0);
1381         text       *tablename = PG_GETARG_TEXT_P(1);
1382         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1383         Oid                     tableoid;
1384         AclMode         mode;
1385         AclResult       aclresult;
1386
1387         tableoid = convert_table_name(tablename);
1388         mode = convert_table_priv_string(priv_type_text);
1389
1390         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1391
1392         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1393 }
1394
1395 /*
1396  * has_table_privilege_id_id
1397  *              Check user privileges on a table given
1398  *              usesysid, table oid, and text priv name.
1399  */
1400 Datum
1401 has_table_privilege_id_id(PG_FUNCTION_ARGS)
1402 {
1403         int32           usesysid = PG_GETARG_INT32(0);
1404         Oid                     tableoid = PG_GETARG_OID(1);
1405         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1406         AclMode         mode;
1407         AclResult       aclresult;
1408
1409         mode = convert_table_priv_string(priv_type_text);
1410
1411         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1412
1413         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1414 }
1415
1416 /*
1417  *              Support routines for has_table_privilege family.
1418  */
1419
1420 /*
1421  * Given a table name expressed as a string, look it up and return Oid
1422  */
1423 static Oid
1424 convert_table_name(text *tablename)
1425 {
1426         RangeVar   *relrv;
1427
1428         relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename,
1429                                                                                                  "has_table_privilege"));
1430
1431         return RangeVarGetRelid(relrv, false);
1432 }
1433
1434 /*
1435  * convert_table_priv_string
1436  *              Convert text string to AclMode value.
1437  */
1438 static AclMode
1439 convert_table_priv_string(text *priv_type_text)
1440 {
1441         char       *priv_type;
1442
1443         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1444                                                                            PointerGetDatum(priv_type_text)));
1445
1446         /*
1447          * Return mode from priv_type string
1448          */
1449         if (pg_strcasecmp(priv_type, "SELECT") == 0)
1450                 return ACL_SELECT;
1451         if (pg_strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
1452                 return ACL_GRANT_OPTION_FOR(ACL_SELECT);
1453
1454         if (pg_strcasecmp(priv_type, "INSERT") == 0)
1455                 return ACL_INSERT;
1456         if (pg_strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
1457                 return ACL_GRANT_OPTION_FOR(ACL_INSERT);
1458
1459         if (pg_strcasecmp(priv_type, "UPDATE") == 0)
1460                 return ACL_UPDATE;
1461         if (pg_strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
1462                 return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
1463
1464         if (pg_strcasecmp(priv_type, "DELETE") == 0)
1465                 return ACL_DELETE;
1466         if (pg_strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
1467                 return ACL_GRANT_OPTION_FOR(ACL_DELETE);
1468
1469         if (pg_strcasecmp(priv_type, "RULE") == 0)
1470                 return ACL_RULE;
1471         if (pg_strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
1472                 return ACL_GRANT_OPTION_FOR(ACL_RULE);
1473
1474         if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
1475                 return ACL_REFERENCES;
1476         if (pg_strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
1477                 return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
1478
1479         if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
1480                 return ACL_TRIGGER;
1481         if (pg_strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
1482                 return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
1483
1484         ereport(ERROR,
1485                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1486                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1487         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1488 }
1489
1490
1491 /*
1492  * has_database_privilege variants
1493  *              These are all named "has_database_privilege" at the SQL level.
1494  *              They take various combinations of database name, database OID,
1495  *              user name, user sysid, or implicit user = current_user.
1496  *
1497  *              The result is a boolean value: true if user has the indicated
1498  *              privilege, false if not.
1499  */
1500
1501 /*
1502  * has_database_privilege_name_name
1503  *              Check user privileges on a database given
1504  *              name username, text databasename, and text priv name.
1505  */
1506 Datum
1507 has_database_privilege_name_name(PG_FUNCTION_ARGS)
1508 {
1509         Name            username = PG_GETARG_NAME(0);
1510         text       *databasename = PG_GETARG_TEXT_P(1);
1511         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1512         int32           usesysid;
1513         Oid                     databaseoid;
1514         AclMode         mode;
1515         AclResult       aclresult;
1516
1517         usesysid = get_usesysid(NameStr(*username));
1518         databaseoid = convert_database_name(databasename);
1519         mode = convert_database_priv_string(priv_type_text);
1520
1521         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1522
1523         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1524 }
1525
1526 /*
1527  * has_database_privilege_name
1528  *              Check user privileges on a database given
1529  *              text databasename and text priv name.
1530  *              current_user is assumed
1531  */
1532 Datum
1533 has_database_privilege_name(PG_FUNCTION_ARGS)
1534 {
1535         text       *databasename = PG_GETARG_TEXT_P(0);
1536         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1537         AclId           usesysid;
1538         Oid                     databaseoid;
1539         AclMode         mode;
1540         AclResult       aclresult;
1541
1542         usesysid = GetUserId();
1543         databaseoid = convert_database_name(databasename);
1544         mode = convert_database_priv_string(priv_type_text);
1545
1546         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1547
1548         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1549 }
1550
1551 /*
1552  * has_database_privilege_name_id
1553  *              Check user privileges on a database given
1554  *              name usename, database oid, and text priv name.
1555  */
1556 Datum
1557 has_database_privilege_name_id(PG_FUNCTION_ARGS)
1558 {
1559         Name            username = PG_GETARG_NAME(0);
1560         Oid                     databaseoid = PG_GETARG_OID(1);
1561         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1562         int32           usesysid;
1563         AclMode         mode;
1564         AclResult       aclresult;
1565
1566         usesysid = get_usesysid(NameStr(*username));
1567         mode = convert_database_priv_string(priv_type_text);
1568
1569         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1570
1571         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1572 }
1573
1574 /*
1575  * has_database_privilege_id
1576  *              Check user privileges on a database given
1577  *              database oid, and text priv name.
1578  *              current_user is assumed
1579  */
1580 Datum
1581 has_database_privilege_id(PG_FUNCTION_ARGS)
1582 {
1583         Oid                     databaseoid = PG_GETARG_OID(0);
1584         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1585         AclId           usesysid;
1586         AclMode         mode;
1587         AclResult       aclresult;
1588
1589         usesysid = GetUserId();
1590         mode = convert_database_priv_string(priv_type_text);
1591
1592         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1593
1594         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1595 }
1596
1597 /*
1598  * has_database_privilege_id_name
1599  *              Check user privileges on a database given
1600  *              usesysid, text databasename, and text priv name.
1601  */
1602 Datum
1603 has_database_privilege_id_name(PG_FUNCTION_ARGS)
1604 {
1605         int32           usesysid = PG_GETARG_INT32(0);
1606         text       *databasename = PG_GETARG_TEXT_P(1);
1607         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1608         Oid                     databaseoid;
1609         AclMode         mode;
1610         AclResult       aclresult;
1611
1612         databaseoid = convert_database_name(databasename);
1613         mode = convert_database_priv_string(priv_type_text);
1614
1615         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1616
1617         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1618 }
1619
1620 /*
1621  * has_database_privilege_id_id
1622  *              Check user privileges on a database given
1623  *              usesysid, database oid, and text priv name.
1624  */
1625 Datum
1626 has_database_privilege_id_id(PG_FUNCTION_ARGS)
1627 {
1628         int32           usesysid = PG_GETARG_INT32(0);
1629         Oid                     databaseoid = PG_GETARG_OID(1);
1630         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1631         AclMode         mode;
1632         AclResult       aclresult;
1633
1634         mode = convert_database_priv_string(priv_type_text);
1635
1636         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1637
1638         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1639 }
1640
1641 /*
1642  *              Support routines for has_database_privilege family.
1643  */
1644
1645 /*
1646  * Given a database name expressed as a string, look it up and return Oid
1647  */
1648 static Oid
1649 convert_database_name(text *databasename)
1650 {
1651         char       *dbname;
1652         Oid                     oid;
1653
1654         dbname = DatumGetCString(DirectFunctionCall1(textout,
1655                                                                                  PointerGetDatum(databasename)));
1656
1657         oid = get_database_oid(dbname);
1658         if (!OidIsValid(oid))
1659                 ereport(ERROR,
1660                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
1661                                  errmsg("database \"%s\" does not exist", dbname)));
1662
1663         return oid;
1664 }
1665
1666 /*
1667  * convert_database_priv_string
1668  *              Convert text string to AclMode value.
1669  */
1670 static AclMode
1671 convert_database_priv_string(text *priv_type_text)
1672 {
1673         char       *priv_type;
1674
1675         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1676                                                                            PointerGetDatum(priv_type_text)));
1677
1678         /*
1679          * Return mode from priv_type string
1680          */
1681         if (pg_strcasecmp(priv_type, "CREATE") == 0)
1682                 return ACL_CREATE;
1683         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1684                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1685
1686         if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
1687                 return ACL_CREATE_TEMP;
1688         if (pg_strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
1689                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1690
1691         if (pg_strcasecmp(priv_type, "TEMP") == 0)
1692                 return ACL_CREATE_TEMP;
1693         if (pg_strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
1694                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1695
1696         ereport(ERROR,
1697                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1698                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1699         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1700 }
1701
1702
1703 /*
1704  * has_function_privilege variants
1705  *              These are all named "has_function_privilege" at the SQL level.
1706  *              They take various combinations of function name, function OID,
1707  *              user name, user sysid, or implicit user = current_user.
1708  *
1709  *              The result is a boolean value: true if user has the indicated
1710  *              privilege, false if not.
1711  */
1712
1713 /*
1714  * has_function_privilege_name_name
1715  *              Check user privileges on a function given
1716  *              name username, text functionname, and text priv name.
1717  */
1718 Datum
1719 has_function_privilege_name_name(PG_FUNCTION_ARGS)
1720 {
1721         Name            username = PG_GETARG_NAME(0);
1722         text       *functionname = PG_GETARG_TEXT_P(1);
1723         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1724         int32           usesysid;
1725         Oid                     functionoid;
1726         AclMode         mode;
1727         AclResult       aclresult;
1728
1729         usesysid = get_usesysid(NameStr(*username));
1730         functionoid = convert_function_name(functionname);
1731         mode = convert_function_priv_string(priv_type_text);
1732
1733         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1734
1735         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1736 }
1737
1738 /*
1739  * has_function_privilege_name
1740  *              Check user privileges on a function given
1741  *              text functionname and text priv name.
1742  *              current_user is assumed
1743  */
1744 Datum
1745 has_function_privilege_name(PG_FUNCTION_ARGS)
1746 {
1747         text       *functionname = PG_GETARG_TEXT_P(0);
1748         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1749         AclId           usesysid;
1750         Oid                     functionoid;
1751         AclMode         mode;
1752         AclResult       aclresult;
1753
1754         usesysid = GetUserId();
1755         functionoid = convert_function_name(functionname);
1756         mode = convert_function_priv_string(priv_type_text);
1757
1758         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1759
1760         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1761 }
1762
1763 /*
1764  * has_function_privilege_name_id
1765  *              Check user privileges on a function given
1766  *              name usename, function oid, and text priv name.
1767  */
1768 Datum
1769 has_function_privilege_name_id(PG_FUNCTION_ARGS)
1770 {
1771         Name            username = PG_GETARG_NAME(0);
1772         Oid                     functionoid = PG_GETARG_OID(1);
1773         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1774         int32           usesysid;
1775         AclMode         mode;
1776         AclResult       aclresult;
1777
1778         usesysid = get_usesysid(NameStr(*username));
1779         mode = convert_function_priv_string(priv_type_text);
1780
1781         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1782
1783         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1784 }
1785
1786 /*
1787  * has_function_privilege_id
1788  *              Check user privileges on a function given
1789  *              function oid, and text priv name.
1790  *              current_user is assumed
1791  */
1792 Datum
1793 has_function_privilege_id(PG_FUNCTION_ARGS)
1794 {
1795         Oid                     functionoid = PG_GETARG_OID(0);
1796         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1797         AclId           usesysid;
1798         AclMode         mode;
1799         AclResult       aclresult;
1800
1801         usesysid = GetUserId();
1802         mode = convert_function_priv_string(priv_type_text);
1803
1804         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1805
1806         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1807 }
1808
1809 /*
1810  * has_function_privilege_id_name
1811  *              Check user privileges on a function given
1812  *              usesysid, text functionname, and text priv name.
1813  */
1814 Datum
1815 has_function_privilege_id_name(PG_FUNCTION_ARGS)
1816 {
1817         int32           usesysid = PG_GETARG_INT32(0);
1818         text       *functionname = PG_GETARG_TEXT_P(1);
1819         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1820         Oid                     functionoid;
1821         AclMode         mode;
1822         AclResult       aclresult;
1823
1824         functionoid = convert_function_name(functionname);
1825         mode = convert_function_priv_string(priv_type_text);
1826
1827         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1828
1829         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1830 }
1831
1832 /*
1833  * has_function_privilege_id_id
1834  *              Check user privileges on a function given
1835  *              usesysid, function oid, and text priv name.
1836  */
1837 Datum
1838 has_function_privilege_id_id(PG_FUNCTION_ARGS)
1839 {
1840         int32           usesysid = PG_GETARG_INT32(0);
1841         Oid                     functionoid = PG_GETARG_OID(1);
1842         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1843         AclMode         mode;
1844         AclResult       aclresult;
1845
1846         mode = convert_function_priv_string(priv_type_text);
1847
1848         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1849
1850         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1851 }
1852
1853 /*
1854  *              Support routines for has_function_privilege family.
1855  */
1856
1857 /*
1858  * Given a function name expressed as a string, look it up and return Oid
1859  */
1860 static Oid
1861 convert_function_name(text *functionname)
1862 {
1863         char       *funcname;
1864         Oid                     oid;
1865
1866         funcname = DatumGetCString(DirectFunctionCall1(textout,
1867                                                                                  PointerGetDatum(functionname)));
1868
1869         oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
1870                                                                                          CStringGetDatum(funcname)));
1871
1872         if (!OidIsValid(oid))
1873                 ereport(ERROR,
1874                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1875                                  errmsg("function \"%s\" does not exist", funcname)));
1876
1877         return oid;
1878 }
1879
1880 /*
1881  * convert_function_priv_string
1882  *              Convert text string to AclMode value.
1883  */
1884 static AclMode
1885 convert_function_priv_string(text *priv_type_text)
1886 {
1887         char       *priv_type;
1888
1889         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1890                                                                            PointerGetDatum(priv_type_text)));
1891
1892         /*
1893          * Return mode from priv_type string
1894          */
1895         if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
1896                 return ACL_EXECUTE;
1897         if (pg_strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
1898                 return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
1899
1900         ereport(ERROR,
1901                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1902                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1903         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1904 }
1905
1906
1907 /*
1908  * has_language_privilege variants
1909  *              These are all named "has_language_privilege" at the SQL level.
1910  *              They take various combinations of language name, language OID,
1911  *              user name, user sysid, or implicit user = current_user.
1912  *
1913  *              The result is a boolean value: true if user has the indicated
1914  *              privilege, false if not.
1915  */
1916
1917 /*
1918  * has_language_privilege_name_name
1919  *              Check user privileges on a language given
1920  *              name username, text languagename, and text priv name.
1921  */
1922 Datum
1923 has_language_privilege_name_name(PG_FUNCTION_ARGS)
1924 {
1925         Name            username = PG_GETARG_NAME(0);
1926         text       *languagename = PG_GETARG_TEXT_P(1);
1927         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1928         int32           usesysid;
1929         Oid                     languageoid;
1930         AclMode         mode;
1931         AclResult       aclresult;
1932
1933         usesysid = get_usesysid(NameStr(*username));
1934         languageoid = convert_language_name(languagename);
1935         mode = convert_language_priv_string(priv_type_text);
1936
1937         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1938
1939         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1940 }
1941
1942 /*
1943  * has_language_privilege_name
1944  *              Check user privileges on a language given
1945  *              text languagename and text priv name.
1946  *              current_user is assumed
1947  */
1948 Datum
1949 has_language_privilege_name(PG_FUNCTION_ARGS)
1950 {
1951         text       *languagename = PG_GETARG_TEXT_P(0);
1952         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1953         AclId           usesysid;
1954         Oid                     languageoid;
1955         AclMode         mode;
1956         AclResult       aclresult;
1957
1958         usesysid = GetUserId();
1959         languageoid = convert_language_name(languagename);
1960         mode = convert_language_priv_string(priv_type_text);
1961
1962         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1963
1964         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1965 }
1966
1967 /*
1968  * has_language_privilege_name_id
1969  *              Check user privileges on a language given
1970  *              name usename, language oid, and text priv name.
1971  */
1972 Datum
1973 has_language_privilege_name_id(PG_FUNCTION_ARGS)
1974 {
1975         Name            username = PG_GETARG_NAME(0);
1976         Oid                     languageoid = PG_GETARG_OID(1);
1977         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1978         int32           usesysid;
1979         AclMode         mode;
1980         AclResult       aclresult;
1981
1982         usesysid = get_usesysid(NameStr(*username));
1983         mode = convert_language_priv_string(priv_type_text);
1984
1985         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1986
1987         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1988 }
1989
1990 /*
1991  * has_language_privilege_id
1992  *              Check user privileges on a language given
1993  *              language oid, and text priv name.
1994  *              current_user is assumed
1995  */
1996 Datum
1997 has_language_privilege_id(PG_FUNCTION_ARGS)
1998 {
1999         Oid                     languageoid = PG_GETARG_OID(0);
2000         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2001         AclId           usesysid;
2002         AclMode         mode;
2003         AclResult       aclresult;
2004
2005         usesysid = GetUserId();
2006         mode = convert_language_priv_string(priv_type_text);
2007
2008         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
2009
2010         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2011 }
2012
2013 /*
2014  * has_language_privilege_id_name
2015  *              Check user privileges on a language given
2016  *              usesysid, text languagename, and text priv name.
2017  */
2018 Datum
2019 has_language_privilege_id_name(PG_FUNCTION_ARGS)
2020 {
2021         int32           usesysid = PG_GETARG_INT32(0);
2022         text       *languagename = PG_GETARG_TEXT_P(1);
2023         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2024         Oid                     languageoid;
2025         AclMode         mode;
2026         AclResult       aclresult;
2027
2028         languageoid = convert_language_name(languagename);
2029         mode = convert_language_priv_string(priv_type_text);
2030
2031         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
2032
2033         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2034 }
2035
2036 /*
2037  * has_language_privilege_id_id
2038  *              Check user privileges on a language given
2039  *              usesysid, language oid, and text priv name.
2040  */
2041 Datum
2042 has_language_privilege_id_id(PG_FUNCTION_ARGS)
2043 {
2044         int32           usesysid = PG_GETARG_INT32(0);
2045         Oid                     languageoid = PG_GETARG_OID(1);
2046         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2047         AclMode         mode;
2048         AclResult       aclresult;
2049
2050         mode = convert_language_priv_string(priv_type_text);
2051
2052         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
2053
2054         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2055 }
2056
2057 /*
2058  *              Support routines for has_language_privilege family.
2059  */
2060
2061 /*
2062  * Given a language name expressed as a string, look it up and return Oid
2063  */
2064 static Oid
2065 convert_language_name(text *languagename)
2066 {
2067         char       *langname;
2068         Oid                     oid;
2069
2070         langname = DatumGetCString(DirectFunctionCall1(textout,
2071                                                                                  PointerGetDatum(languagename)));
2072
2073         oid = GetSysCacheOid(LANGNAME,
2074                                                  CStringGetDatum(langname),
2075                                                  0, 0, 0);
2076         if (!OidIsValid(oid))
2077                 ereport(ERROR,
2078                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2079                                  errmsg("language \"%s\" does not exist", langname)));
2080
2081         return oid;
2082 }
2083
2084 /*
2085  * convert_language_priv_string
2086  *              Convert text string to AclMode value.
2087  */
2088 static AclMode
2089 convert_language_priv_string(text *priv_type_text)
2090 {
2091         char       *priv_type;
2092
2093         priv_type = DatumGetCString(DirectFunctionCall1(textout,
2094                                                                            PointerGetDatum(priv_type_text)));
2095
2096         /*
2097          * Return mode from priv_type string
2098          */
2099         if (pg_strcasecmp(priv_type, "USAGE") == 0)
2100                 return ACL_USAGE;
2101         if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
2102                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
2103
2104         ereport(ERROR,
2105                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2106                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
2107         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2108 }
2109
2110
2111 /*
2112  * has_schema_privilege variants
2113  *              These are all named "has_schema_privilege" at the SQL level.
2114  *              They take various combinations of schema name, schema OID,
2115  *              user name, user sysid, or implicit user = current_user.
2116  *
2117  *              The result is a boolean value: true if user has the indicated
2118  *              privilege, false if not.
2119  */
2120
2121 /*
2122  * has_schema_privilege_name_name
2123  *              Check user privileges on a schema given
2124  *              name username, text schemaname, and text priv name.
2125  */
2126 Datum
2127 has_schema_privilege_name_name(PG_FUNCTION_ARGS)
2128 {
2129         Name            username = PG_GETARG_NAME(0);
2130         text       *schemaname = PG_GETARG_TEXT_P(1);
2131         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2132         int32           usesysid;
2133         Oid                     schemaoid;
2134         AclMode         mode;
2135         AclResult       aclresult;
2136
2137         usesysid = get_usesysid(NameStr(*username));
2138         schemaoid = convert_schema_name(schemaname);
2139         mode = convert_schema_priv_string(priv_type_text);
2140
2141         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2142
2143         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2144 }
2145
2146 /*
2147  * has_schema_privilege_name
2148  *              Check user privileges on a schema given
2149  *              text schemaname and text priv name.
2150  *              current_user is assumed
2151  */
2152 Datum
2153 has_schema_privilege_name(PG_FUNCTION_ARGS)
2154 {
2155         text       *schemaname = PG_GETARG_TEXT_P(0);
2156         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2157         AclId           usesysid;
2158         Oid                     schemaoid;
2159         AclMode         mode;
2160         AclResult       aclresult;
2161
2162         usesysid = GetUserId();
2163         schemaoid = convert_schema_name(schemaname);
2164         mode = convert_schema_priv_string(priv_type_text);
2165
2166         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2167
2168         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2169 }
2170
2171 /*
2172  * has_schema_privilege_name_id
2173  *              Check user privileges on a schema given
2174  *              name usename, schema oid, and text priv name.
2175  */
2176 Datum
2177 has_schema_privilege_name_id(PG_FUNCTION_ARGS)
2178 {
2179         Name            username = PG_GETARG_NAME(0);
2180         Oid                     schemaoid = PG_GETARG_OID(1);
2181         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2182         int32           usesysid;
2183         AclMode         mode;
2184         AclResult       aclresult;
2185
2186         usesysid = get_usesysid(NameStr(*username));
2187         mode = convert_schema_priv_string(priv_type_text);
2188
2189         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2190
2191         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2192 }
2193
2194 /*
2195  * has_schema_privilege_id
2196  *              Check user privileges on a schema given
2197  *              schema oid, and text priv name.
2198  *              current_user is assumed
2199  */
2200 Datum
2201 has_schema_privilege_id(PG_FUNCTION_ARGS)
2202 {
2203         Oid                     schemaoid = PG_GETARG_OID(0);
2204         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2205         AclId           usesysid;
2206         AclMode         mode;
2207         AclResult       aclresult;
2208
2209         usesysid = GetUserId();
2210         mode = convert_schema_priv_string(priv_type_text);
2211
2212         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2213
2214         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2215 }
2216
2217 /*
2218  * has_schema_privilege_id_name
2219  *              Check user privileges on a schema given
2220  *              usesysid, text schemaname, and text priv name.
2221  */
2222 Datum
2223 has_schema_privilege_id_name(PG_FUNCTION_ARGS)
2224 {
2225         int32           usesysid = PG_GETARG_INT32(0);
2226         text       *schemaname = PG_GETARG_TEXT_P(1);
2227         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2228         Oid                     schemaoid;
2229         AclMode         mode;
2230         AclResult       aclresult;
2231
2232         schemaoid = convert_schema_name(schemaname);
2233         mode = convert_schema_priv_string(priv_type_text);
2234
2235         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2236
2237         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2238 }
2239
2240 /*
2241  * has_schema_privilege_id_id
2242  *              Check user privileges on a schema given
2243  *              usesysid, schema oid, and text priv name.
2244  */
2245 Datum
2246 has_schema_privilege_id_id(PG_FUNCTION_ARGS)
2247 {
2248         int32           usesysid = PG_GETARG_INT32(0);
2249         Oid                     schemaoid = PG_GETARG_OID(1);
2250         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2251         AclMode         mode;
2252         AclResult       aclresult;
2253
2254         mode = convert_schema_priv_string(priv_type_text);
2255
2256         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
2257
2258         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2259 }
2260
2261 /*
2262  *              Support routines for has_schema_privilege family.
2263  */
2264
2265 /*
2266  * Given a schema name expressed as a string, look it up and return Oid
2267  */
2268 static Oid
2269 convert_schema_name(text *schemaname)
2270 {
2271         char       *nspname;
2272         Oid                     oid;
2273
2274         nspname = DatumGetCString(DirectFunctionCall1(textout,
2275                                                                                    PointerGetDatum(schemaname)));
2276
2277         oid = GetSysCacheOid(NAMESPACENAME,
2278                                                  CStringGetDatum(nspname),
2279                                                  0, 0, 0);
2280         if (!OidIsValid(oid))
2281                 ereport(ERROR,
2282                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
2283                                  errmsg("schema \"%s\" does not exist", nspname)));
2284
2285         return oid;
2286 }
2287
2288 /*
2289  * convert_schema_priv_string
2290  *              Convert text string to AclMode value.
2291  */
2292 static AclMode
2293 convert_schema_priv_string(text *priv_type_text)
2294 {
2295         char       *priv_type;
2296
2297         priv_type = DatumGetCString(DirectFunctionCall1(textout,
2298                                                                            PointerGetDatum(priv_type_text)));
2299
2300         /*
2301          * Return mode from priv_type string
2302          */
2303         if (pg_strcasecmp(priv_type, "CREATE") == 0)
2304                 return ACL_CREATE;
2305         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
2306                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
2307
2308         if (pg_strcasecmp(priv_type, "USAGE") == 0)
2309                 return ACL_USAGE;
2310         if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
2311                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
2312
2313         ereport(ERROR,
2314                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2315                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
2316         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2317 }
2318
2319 /*
2320  * has_tablespace_privilege variants
2321  *              These are all named "has_tablespace_privilege" at the SQL level.
2322  *              They take various combinations of tablespace name, tablespace OID,
2323  *              user name, user sysid, or implicit user = current_user.
2324  *
2325  *              The result is a boolean value: true if user has the indicated
2326  *              privilege, false if not.
2327  */
2328
2329 /*
2330  * has_tablespace_privilege_name_name
2331  *              Check user privileges on a tablespace given
2332  *              name username, text tablespacename, and text priv name.
2333  */
2334 Datum
2335 has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
2336 {
2337         Name            username = PG_GETARG_NAME(0);
2338         text       *tablespacename = PG_GETARG_TEXT_P(1);
2339         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2340         int32           usesysid;
2341         Oid                     tablespaceoid;
2342         AclMode         mode;
2343         AclResult       aclresult;
2344
2345         usesysid = get_usesysid(NameStr(*username));
2346         tablespaceoid = convert_tablespace_name(tablespacename);
2347         mode = convert_tablespace_priv_string(priv_type_text);
2348
2349         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2350
2351         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2352 }
2353
2354 /*
2355  * has_tablespace_privilege_name
2356  *              Check user privileges on a tablespace given
2357  *              text tablespacename and text priv name.
2358  *              current_user is assumed
2359  */
2360 Datum
2361 has_tablespace_privilege_name(PG_FUNCTION_ARGS)
2362 {
2363         text       *tablespacename = PG_GETARG_TEXT_P(0);
2364         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2365         AclId           usesysid;
2366         Oid                     tablespaceoid;
2367         AclMode         mode;
2368         AclResult       aclresult;
2369
2370         usesysid = GetUserId();
2371         tablespaceoid = convert_tablespace_name(tablespacename);
2372         mode = convert_tablespace_priv_string(priv_type_text);
2373
2374         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2375
2376         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2377 }
2378
2379 /*
2380  * has_tablespace_privilege_name_id
2381  *              Check user privileges on a tablespace given
2382  *              name usename, tablespace oid, and text priv name.
2383  */
2384 Datum
2385 has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
2386 {
2387         Name            username = PG_GETARG_NAME(0);
2388         Oid                     tablespaceoid = PG_GETARG_OID(1);
2389         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2390         int32           usesysid;
2391         AclMode         mode;
2392         AclResult       aclresult;
2393
2394         usesysid = get_usesysid(NameStr(*username));
2395         mode = convert_tablespace_priv_string(priv_type_text);
2396
2397         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2398
2399         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2400 }
2401
2402 /*
2403  * has_tablespace_privilege_id
2404  *              Check user privileges on a tablespace given
2405  *              tablespace oid, and text priv name.
2406  *              current_user is assumed
2407  */
2408 Datum
2409 has_tablespace_privilege_id(PG_FUNCTION_ARGS)
2410 {
2411         Oid                     tablespaceoid = PG_GETARG_OID(0);
2412         text       *priv_type_text = PG_GETARG_TEXT_P(1);
2413         AclId           usesysid;
2414         AclMode         mode;
2415         AclResult       aclresult;
2416
2417         usesysid = GetUserId();
2418         mode = convert_tablespace_priv_string(priv_type_text);
2419
2420         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2421
2422         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2423 }
2424
2425 /*
2426  * has_tablespace_privilege_id_name
2427  *              Check user privileges on a tablespace given
2428  *              usesysid, text tablespacename, and text priv name.
2429  */
2430 Datum
2431 has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
2432 {
2433         int32           usesysid = PG_GETARG_INT32(0);
2434         text       *tablespacename = PG_GETARG_TEXT_P(1);
2435         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2436         Oid                     tablespaceoid;
2437         AclMode         mode;
2438         AclResult       aclresult;
2439
2440         tablespaceoid = convert_tablespace_name(tablespacename);
2441         mode = convert_tablespace_priv_string(priv_type_text);
2442
2443         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2444
2445         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2446 }
2447
2448 /*
2449  * has_tablespace_privilege_id_id
2450  *              Check user privileges on a tablespace given
2451  *              usesysid, tablespace oid, and text priv name.
2452  */
2453 Datum
2454 has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
2455 {
2456         int32           usesysid = PG_GETARG_INT32(0);
2457         Oid                     tablespaceoid = PG_GETARG_OID(1);
2458         text       *priv_type_text = PG_GETARG_TEXT_P(2);
2459         AclMode         mode;
2460         AclResult       aclresult;
2461
2462         mode = convert_tablespace_priv_string(priv_type_text);
2463
2464         aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
2465
2466         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
2467 }
2468
2469 /*
2470  *              Support routines for has_tablespace_privilege family.
2471  */
2472
2473 /*
2474  * Given a tablespace name expressed as a string, look it up and return Oid
2475  */
2476 static Oid
2477 convert_tablespace_name(text *tablespacename)
2478 {
2479         char       *spcname;
2480         Oid                     oid;
2481
2482         spcname = DatumGetCString(DirectFunctionCall1(textout,
2483                                                                            PointerGetDatum(tablespacename)));
2484         oid = get_tablespace_oid(spcname);
2485
2486         if (!OidIsValid(oid))
2487                 ereport(ERROR,
2488                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2489                                  errmsg("tablespace \"%s\" does not exist", spcname)));
2490
2491         return oid;
2492 }
2493
2494 /*
2495  * convert_tablespace_priv_string
2496  *              Convert text string to AclMode value.
2497  */
2498 static AclMode
2499 convert_tablespace_priv_string(text *priv_type_text)
2500 {
2501         char       *priv_type;
2502
2503         priv_type = DatumGetCString(DirectFunctionCall1(textout,
2504                                                                            PointerGetDatum(priv_type_text)));
2505
2506         /*
2507          * Return mode from priv_type string
2508          */
2509         if (pg_strcasecmp(priv_type, "CREATE") == 0)
2510                 return ACL_CREATE;
2511         if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
2512                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
2513
2514         ereport(ERROR,
2515                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2516                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
2517         return ACL_NO_RIGHTS;           /* keep compiler quiet */
2518 }