]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Update copyrights to 2003.
[postgresql] / src / backend / utils / adt / acl.c
1 /*-------------------------------------------------------------------------
2  *
3  * acl.c
4  *        Basic access control list data structures manipulation routines.
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.94 2003/08/04 02:40:04 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "catalog/namespace.h"
20 #include "catalog/pg_shadow.h"
21 #include "catalog/pg_type.h"
22 #include "commands/dbcommands.h"
23 #include "miscadmin.h"
24 #include "utils/acl.h"
25 #include "utils/builtins.h"
26 #include "utils/lsyscache.h"
27 #include "utils/syscache.h"
28
29
30 #define ACL_IDTYPE_GID_KEYWORD  "group"
31 #define ACL_IDTYPE_UID_KEYWORD  "user"
32
33 static const char *getid(const char *s, char *n);
34 static void putid(char *p, const char *s);
35 static Acl *allocacl(int n);
36 static const char *aclparse(const char *s, AclItem *aip);
37 static bool aclitem_match(const AclItem *a1, const AclItem *a2);
38 static Acl *recursive_revoke(Acl *acl, AclId grantee,
39                                  AclMode revoke_privs, DropBehavior behavior);
40
41 static AclMode convert_priv_string(text *priv_type_text);
42
43 static Oid      convert_table_name(text *tablename);
44 static AclMode convert_table_priv_string(text *priv_type_text);
45 static Oid      convert_database_name(text *databasename);
46 static AclMode convert_database_priv_string(text *priv_type_text);
47 static Oid      convert_function_name(text *functionname);
48 static AclMode convert_function_priv_string(text *priv_type_text);
49 static Oid      convert_language_name(text *languagename);
50 static AclMode convert_language_priv_string(text *priv_type_text);
51 static Oid      convert_schema_name(text *schemaname);
52 static AclMode convert_schema_priv_string(text *priv_type_text);
53
54
55 /*
56  * getid
57  *              Consumes the first alphanumeric string (identifier) found in string
58  *              's', ignoring any leading white space.  If it finds a double quote
59  *              it returns the word inside the quotes.
60  *
61  * RETURNS:
62  *              the string position in 's' that points to the next non-space character
63  *              in 's', after any quotes.  Also:
64  *              - loads the identifier into 'name'.  (If no identifier is found, 'name'
65  *                contains an empty string.)  name must be NAMEDATALEN bytes.
66  */
67 static const char *
68 getid(const char *s, char *n)
69 {
70         int                     len = 0;
71         bool            in_quotes = false;
72
73         Assert(s && n);
74
75         while (isspace((unsigned char) *s))
76                 s++;
77         /* This test had better match what putid() does, below */
78         for (;
79                  *s != '\0' &&
80                  (isalnum((unsigned char) *s) ||
81                   *s == '_' ||
82                   *s == '"' ||
83                   in_quotes);
84                  s++)
85         {
86                 if (*s == '"')
87                         in_quotes = !in_quotes;
88                 else
89                 {
90                         if (len >= NAMEDATALEN - 1)
91                                 ereport(ERROR,
92                                                 (errcode(ERRCODE_NAME_TOO_LONG),
93                                                  errmsg("identifier too long"),
94                                  errdetail("Identifier must be less than %d characters.",
95                                                    NAMEDATALEN)));
96
97                         n[len++] = *s;
98                 }
99         }
100         n[len] = '\0';
101         while (isspace((unsigned char) *s))
102                 s++;
103         return s;
104 }
105
106 /*
107  * Write a user or group Name at *p, surrounding it with double quotes if
108  * needed.      There must be at least NAMEDATALEN+2 bytes available at *p.
109  */
110 static void
111 putid(char *p, const char *s)
112 {
113         const char *src;
114         bool            safe = true;
115
116         for (src = s; *src; src++)
117         {
118                 /* This test had better match what getid() does, above */
119                 if (!isalnum((unsigned char) *src) && *src != '_')
120                 {
121                         safe = false;
122                         break;
123                 }
124         }
125         if (!safe)
126                 *p++ = '"';
127         for (src = s; *src; src++)
128                 *p++ = *src;
129         if (!safe)
130                 *p++ = '"';
131         *p = '\0';
132 }
133
134 /*
135  * aclparse
136  *              Consumes and parses an ACL specification of the form:
137  *                              [group|user] [A-Za-z0-9]*=[rwaR]*
138  *              from string 's', ignoring any leading white space or white space
139  *              between the optional id type keyword (group|user) and the actual
140  *              ACL specification.
141  *
142  *              This routine is called by the parser as well as aclitemin(), hence
143  *              the added generality.
144  *
145  * RETURNS:
146  *              the string position in 's' immediately following the ACL
147  *              specification.  Also:
148  *              - loads the structure pointed to by 'aip' with the appropriate
149  *                UID/GID, id type identifier and mode type values.
150  */
151 static const char *
152 aclparse(const char *s, AclItem *aip)
153 {
154         AclMode         privs,
155                                 goption,
156                                 read;
157         uint32          idtype;
158         char            name[NAMEDATALEN];
159         char            name2[NAMEDATALEN];
160
161         Assert(s && aip);
162
163 #ifdef ACLDEBUG
164         elog(LOG, "aclparse: input = \"%s\"", s);
165 #endif
166         idtype = ACL_IDTYPE_UID;
167         s = getid(s, name);
168         if (*s != '=')
169         {
170                 /* we just read a keyword, not a name */
171                 if (strcmp(name, ACL_IDTYPE_GID_KEYWORD) == 0)
172                         idtype = ACL_IDTYPE_GID;
173                 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD) != 0)
174                         ereport(ERROR,
175                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
176                                          errmsg("unrecognized keyword: \"%s\"", name),
177                                  errhint("ACL keyword must be \"group\" or \"user\".")));
178                 s = getid(s, name);             /* move s to the name beyond the keyword */
179                 if (name[0] == '\0')
180                         ereport(ERROR,
181                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
182                                          errmsg("missing name"),
183                            errhint("A name must follow the [group|user] keyword.")));
184         }
185         if (name[0] == '\0')
186                 idtype = ACL_IDTYPE_WORLD;
187
188         if (*s != '=')
189                 ereport(ERROR,
190                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
191                                  errmsg("missing \"=\" sign")));
192
193         privs = goption = ACL_NO_RIGHTS;
194
195         for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
196         {
197                 switch (*s)
198                 {
199                         case '*':
200                                 goption |= read;
201                                 break;
202                         case ACL_INSERT_CHR:
203                                 read = ACL_INSERT;
204                                 break;
205                         case ACL_SELECT_CHR:
206                                 read = ACL_SELECT;
207                                 break;
208                         case ACL_UPDATE_CHR:
209                                 read = ACL_UPDATE;
210                                 break;
211                         case ACL_DELETE_CHR:
212                                 read = ACL_DELETE;
213                                 break;
214                         case ACL_RULE_CHR:
215                                 read = ACL_RULE;
216                                 break;
217                         case ACL_REFERENCES_CHR:
218                                 read = ACL_REFERENCES;
219                                 break;
220                         case ACL_TRIGGER_CHR:
221                                 read = ACL_TRIGGER;
222                                 break;
223                         case ACL_EXECUTE_CHR:
224                                 read = ACL_EXECUTE;
225                                 break;
226                         case ACL_USAGE_CHR:
227                                 read = ACL_USAGE;
228                                 break;
229                         case ACL_CREATE_CHR:
230                                 read = ACL_CREATE;
231                                 break;
232                         case ACL_CREATE_TEMP_CHR:
233                                 read = ACL_CREATE_TEMP;
234                                 break;
235                         default:
236                                 ereport(ERROR,
237                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
238                                   errmsg("invalid mode character: must be one of \"%s\"",
239                                                  ACL_ALL_RIGHTS_STR)));
240                 }
241
242                 privs |= read;
243         }
244
245         switch (idtype)
246         {
247                 case ACL_IDTYPE_UID:
248                         aip->ai_grantee = get_usesysid(name);
249                         break;
250                 case ACL_IDTYPE_GID:
251                         aip->ai_grantee = get_grosysid(name);
252                         break;
253                 case ACL_IDTYPE_WORLD:
254                         aip->ai_grantee = ACL_ID_WORLD;
255                         break;
256         }
257
258         /*
259          * XXX Allow a degree of backward compatibility by defaulting the
260          * grantor to the superuser.
261          */
262         if (*s == '/')
263         {
264                 s = getid(s + 1, name2);
265                 if (name2[0] == '\0')
266                         ereport(ERROR,
267                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
268                                          errmsg("a name must follow the \"/\" sign")));
269
270                 aip->ai_grantor = get_usesysid(name2);
271         }
272         else
273         {
274                 aip->ai_grantor = BOOTSTRAP_USESYSID;
275                 ereport(WARNING,
276                                 (errcode(ERRCODE_INVALID_GRANTOR),
277                                  errmsg("defaulting grantor to %u", BOOTSTRAP_USESYSID)));
278         }
279
280         ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, goption, idtype);
281
282 #ifdef ACLDEBUG
283         elog(LOG, "aclparse: correctly read [%x %d %x]",
284                  idtype, aip->ai_grantee, privs);
285 #endif
286         return s;
287 }
288
289 /*
290  * allocacl
291  *              Allocates storage for a new Acl with 'n' entries.
292  *
293  * RETURNS:
294  *              the new Acl
295  */
296 static Acl *
297 allocacl(int n)
298 {
299         Acl                *new_acl;
300         Size            size;
301
302         if (n < 0)
303                 elog(ERROR, "invalid size: %d", n);
304         size = ACL_N_SIZE(n);
305         new_acl = (Acl *) palloc0(size);
306         new_acl->size = size;
307         new_acl->ndim = 1;
308         new_acl->flags = 0;
309         new_acl->elemtype = ACLITEMOID;
310         ARR_LBOUND(new_acl)[0] = 0;
311         ARR_DIMS(new_acl)[0] = n;
312         return new_acl;
313 }
314
315 /*
316  * aclitemin
317  *              Allocates storage for, and fills in, a new AclItem given a string
318  *              's' that contains an ACL specification.  See aclparse for details.
319  *
320  * RETURNS:
321  *              the new AclItem
322  */
323 Datum
324 aclitemin(PG_FUNCTION_ARGS)
325 {
326         const char *s = PG_GETARG_CSTRING(0);
327         AclItem    *aip;
328
329         aip = (AclItem *) palloc(sizeof(AclItem));
330         s = aclparse(s, aip);
331         while (isspace((unsigned char) *s))
332                 ++s;
333         if (*s)
334                 ereport(ERROR,
335                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
336                    errmsg("extra garbage at the end of the ACL specification")));
337
338         PG_RETURN_ACLITEM_P(aip);
339 }
340
341 /*
342  * aclitemout
343  *              Allocates storage for, and fills in, a new null-delimited string
344  *              containing a formatted ACL specification.  See aclparse for details.
345  *
346  * RETURNS:
347  *              the new string
348  */
349 Datum
350 aclitemout(PG_FUNCTION_ARGS)
351 {
352         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
353         char       *p;
354         char       *out;
355         HeapTuple       htup;
356         unsigned        i;
357         char       *tmpname;
358
359         out = palloc(strlen("group =/") +
360                                  2 * N_ACL_RIGHTS +
361                                  2 * (NAMEDATALEN + 2) +
362                                  1);
363
364         p = out;
365         *p = '\0';
366
367         switch (ACLITEM_GET_IDTYPE(*aip))
368         {
369                 case ACL_IDTYPE_UID:
370                         htup = SearchSysCache(SHADOWSYSID,
371                                                                   ObjectIdGetDatum(aip->ai_grantee),
372                                                                   0, 0, 0);
373                         if (HeapTupleIsValid(htup))
374                         {
375                                 putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
376                                 ReleaseSysCache(htup);
377                         }
378                         else
379                         {
380                                 /* Generate numeric UID if we don't find an entry */
381                                 sprintf(p, "%d", aip->ai_grantee);
382                         }
383                         break;
384                 case ACL_IDTYPE_GID:
385                         strcpy(p, "group ");
386                         p += strlen(p);
387                         tmpname = get_groname(aip->ai_grantee);
388                         if (tmpname != NULL)
389                                 putid(p, tmpname);
390                         else
391                         {
392                                 /* Generate numeric GID if we don't find an entry */
393                                 sprintf(p, "%d", aip->ai_grantee);
394                         }
395                         break;
396                 case ACL_IDTYPE_WORLD:
397                         break;
398                 default:
399                         elog(ERROR, "unrecognized idtype: %d",
400                                  (int) ACLITEM_GET_IDTYPE(*aip));
401                         break;
402         }
403         while (*p)
404                 ++p;
405
406         *p++ = '=';
407
408         for (i = 0; i < N_ACL_RIGHTS; ++i)
409         {
410                 if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
411                         *p++ = ACL_ALL_RIGHTS_STR[i];
412                 if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
413                         *p++ = '*';
414         }
415
416         *p++ = '/';
417         *p = '\0';
418
419         htup = SearchSysCache(SHADOWSYSID,
420                                                   ObjectIdGetDatum(aip->ai_grantor),
421                                                   0, 0, 0);
422         if (HeapTupleIsValid(htup))
423         {
424                 putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
425                 ReleaseSysCache(htup);
426         }
427         else
428         {
429                 /* Generate numeric UID if we don't find an entry */
430                 sprintf(p, "%d", aip->ai_grantor);
431         }
432
433         while (*p)
434                 ++p;
435         *p = '\0';
436
437         PG_RETURN_CSTRING(out);
438 }
439
440 /*
441  * aclitem_match
442  *              Two AclItems are considered to match iff they have the same
443  *              grantee and grantor; the privileges are ignored.
444  */
445 static bool
446 aclitem_match(const AclItem *a1, const AclItem *a2)
447 {
448         return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
449                 a1->ai_grantee == a2->ai_grantee &&
450                 a1->ai_grantor == a2->ai_grantor;
451 }
452
453 /*
454  * aclitem equality operator
455  */
456 Datum
457 aclitem_eq(PG_FUNCTION_ARGS)
458 {
459         AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
460         AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
461         bool            result;
462
463         result = a1->ai_privs == a2->ai_privs &&
464                 a1->ai_grantee == a2->ai_grantee &&
465                 a1->ai_grantor == a2->ai_grantor;
466         PG_RETURN_BOOL(result);
467 }
468
469 /*
470  * acldefault()  --- create an ACL describing default access permissions
471  *
472  * Change this routine if you want to alter the default access policy for
473  * newly-created objects (or any object with a NULL acl entry).
474  */
475 Acl *
476 acldefault(GrantObjectType objtype, AclId ownerid)
477 {
478         AclMode         world_default;
479         AclMode         owner_default;
480         Acl                *acl;
481         AclItem    *aip;
482
483         switch (objtype)
484         {
485                 case ACL_OBJECT_RELATION:
486                         world_default = ACL_NO_RIGHTS;
487                         owner_default = ACL_ALL_RIGHTS_RELATION;
488                         break;
489                 case ACL_OBJECT_DATABASE:
490                         world_default = ACL_CREATE_TEMP;        /* not NO_RIGHTS! */
491                         owner_default = ACL_ALL_RIGHTS_DATABASE;
492                         break;
493                 case ACL_OBJECT_FUNCTION:
494                         /* Grant EXECUTE by default, for now */
495                         world_default = ACL_EXECUTE;
496                         owner_default = ACL_ALL_RIGHTS_FUNCTION;
497                         break;
498                 case ACL_OBJECT_LANGUAGE:
499                         /* Grant USAGE by default, for now */
500                         world_default = ACL_USAGE;
501                         owner_default = ACL_ALL_RIGHTS_LANGUAGE;
502                         break;
503                 case ACL_OBJECT_NAMESPACE:
504                         world_default = ACL_NO_RIGHTS;
505                         owner_default = ACL_ALL_RIGHTS_NAMESPACE;
506                         break;
507                 default:
508                         elog(ERROR, "unrecognized objtype: %d", (int) objtype);
509                         world_default = ACL_NO_RIGHTS;          /* keep compiler quiet */
510                         owner_default = ACL_NO_RIGHTS;
511                         break;
512         }
513
514         acl = allocacl((world_default != ACL_NO_RIGHTS ? 1 : 0)
515                                    + (ownerid ? 1 : 0));
516         aip = ACL_DAT(acl);
517
518         if (world_default != ACL_NO_RIGHTS)
519         {
520                 aip[0].ai_grantee = ACL_ID_WORLD;
521                 aip[0].ai_grantor = ownerid;
522                 ACLITEM_SET_PRIVS_IDTYPE(aip[0], world_default, ACL_NO_RIGHTS, ACL_IDTYPE_WORLD);
523         }
524
525         if (ownerid)
526         {
527                 int                     index = (world_default != ACL_NO_RIGHTS ? 1 : 0);
528
529                 aip[index].ai_grantee = ownerid;
530                 aip[index].ai_grantor = ownerid;
531                 /* owner gets default privileges with grant option */
532                 ACLITEM_SET_PRIVS_IDTYPE(aip[index], owner_default, owner_default, ACL_IDTYPE_UID);
533         }
534
535         return acl;
536 }
537
538
539 /*
540  * Add or replace an item in an ACL array.      The result is a modified copy;
541  * the input object is not changed.
542  *
543  * NB: caller is responsible for having detoasted the input ACL, if needed.
544  */
545 Acl *
546 aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBehavior behavior)
547 {
548         Acl                *new_acl = NULL;
549         AclItem    *old_aip,
550                            *new_aip = NULL;
551         int                     dst,
552                                 num;
553
554         /* These checks for null input are probably dead code, but... */
555         if (!old_acl || ACL_NUM(old_acl) < 1)
556                 old_acl = allocacl(1);
557         if (!mod_aip)
558         {
559                 new_acl = allocacl(ACL_NUM(old_acl));
560                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
561                 return new_acl;
562         }
563
564         num = ACL_NUM(old_acl);
565         old_aip = ACL_DAT(old_acl);
566
567         /*
568          * Search the ACL for an existing entry for this grantee and grantor.
569          * If one exists, just modify the entry in-place (well, in the same
570          * position, since we actually return a copy); otherwise, insert the
571          * new entry at the end.
572          */
573
574         for (dst = 0; dst < num; ++dst)
575         {
576                 if (aclitem_match(mod_aip, old_aip + dst))
577                 {
578                         /* found a match, so modify existing item */
579                         new_acl = allocacl(num);
580                         new_aip = ACL_DAT(new_acl);
581                         memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
582                         break;
583                 }
584         }
585
586         if (dst == num)
587         {
588                 /* need to append a new item */
589                 new_acl = allocacl(num + 1);
590                 new_aip = ACL_DAT(new_acl);
591                 memcpy(new_aip, old_aip, num * sizeof(AclItem));
592
593                 /* initialize the new entry with no permissions */
594                 new_aip[dst].ai_grantee = mod_aip->ai_grantee;
595                 new_aip[dst].ai_grantor = mod_aip->ai_grantor;
596                 ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS, ACL_NO_RIGHTS,
597                                                                  ACLITEM_GET_IDTYPE(*mod_aip));
598                 num++;                                  /* set num to the size of new_acl */
599         }
600
601         /* apply the permissions mod */
602         switch (modechg)
603         {
604                 case ACL_MODECHG_ADD:
605                         ACLITEM_SET_PRIVS(new_aip[dst], ACLITEM_GET_PRIVS(new_aip[dst]) | ACLITEM_GET_PRIVS(*mod_aip));
606                         ACLITEM_SET_GOPTIONS(new_aip[dst], ACLITEM_GET_GOPTIONS(new_aip[dst]) | ACLITEM_GET_GOPTIONS(*mod_aip));
607                         break;
608                 case ACL_MODECHG_DEL:
609                         ACLITEM_SET_PRIVS(new_aip[dst], ACLITEM_GET_PRIVS(new_aip[dst]) & ~ACLITEM_GET_PRIVS(*mod_aip));
610                         ACLITEM_SET_GOPTIONS(new_aip[dst], ACLITEM_GET_GOPTIONS(new_aip[dst]) & ~ACLITEM_GET_GOPTIONS(*mod_aip));
611                         break;
612                 case ACL_MODECHG_EQL:
613                         ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
614                                                                          ACLITEM_GET_PRIVS(*mod_aip),
615                                                                          ACLITEM_GET_GOPTIONS(*mod_aip),
616                                                                          ACLITEM_GET_IDTYPE(new_aip[dst]));
617                         break;
618         }
619
620         /*
621          * If the adjusted entry has no permissions, delete it from the list.
622          */
623         if (ACLITEM_GET_PRIVS(new_aip[dst]) == ACL_NO_RIGHTS)
624         {
625                 memmove(new_aip + dst,
626                                 new_aip + dst + 1,
627                                 (num - dst - 1) * sizeof(AclItem));
628                 ARR_DIMS(new_acl)[0] = num - 1;
629                 ARR_SIZE(new_acl) -= sizeof(AclItem);
630         }
631
632         /*
633          * Remove abandoned privileges (cascading revoke)
634          */
635         if (modechg != ACL_MODECHG_ADD
636                 && ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID
637                 && ACLITEM_GET_GOPTIONS(*mod_aip))
638                 new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee, ACLITEM_GET_GOPTIONS(*mod_aip), behavior);
639
640         return new_acl;
641 }
642
643
644 /*
645  * Ensure that no privilege is "abandoned".  A privilege is abandoned
646  * if the user that granted the privilege loses the grant option.  (So
647  * the chain through which it was granted is broken.)  Either the
648  * abandoned privileges are revoked as well, or an error message is
649  * printed, depending on the drop behavior option.
650  */
651 static Acl *
652 recursive_revoke(Acl *acl,
653                                  AclId grantee,
654                                  AclMode revoke_privs,
655                                  DropBehavior behavior)
656 {
657         int                     i;
658
659 restart:
660         for (i = 0; i < ACL_NUM(acl); i++)
661         {
662                 AclItem    *aip = ACL_DAT(acl);
663
664                 if (aip[i].ai_grantor == grantee
665                         && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
666                 {
667                         AclItem         mod_acl;
668
669                         if (behavior == DROP_RESTRICT)
670                                 ereport(ERROR,
671                                                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
672                                                  errmsg("dependent privileges exist"),
673                                                  errhint("Use CASCADE to revoke them too.")));
674
675                         mod_acl.ai_grantor = grantee;
676                         mod_acl.ai_grantee = aip[i].ai_grantee;
677                         ACLITEM_SET_PRIVS_IDTYPE(mod_acl,
678                                                                          revoke_privs,
679                                                                          revoke_privs,
680                                                                          ACLITEM_GET_IDTYPE(aip[i]));
681
682                         acl = aclinsert3(acl, &mod_acl, ACL_MODECHG_DEL, behavior);
683                         goto restart;
684                 }
685         }
686
687         return acl;
688 }
689
690
691 /*
692  * aclinsert (exported function)
693  */
694 Datum
695 aclinsert(PG_FUNCTION_ARGS)
696 {
697         Acl                *old_acl = PG_GETARG_ACL_P(0);
698         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
699
700         PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL, DROP_CASCADE));
701 }
702
703 Datum
704 aclremove(PG_FUNCTION_ARGS)
705 {
706         Acl                *old_acl = PG_GETARG_ACL_P(0);
707         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
708         Acl                *new_acl;
709         AclItem    *old_aip,
710                            *new_aip;
711         int                     dst,
712                                 old_num,
713                                 new_num;
714
715         /* These checks for null input should be dead code, but... */
716         if (!old_acl || ACL_NUM(old_acl) < 1)
717                 old_acl = allocacl(1);
718         if (!mod_aip)
719         {
720                 new_acl = allocacl(ACL_NUM(old_acl));
721                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
722                 PG_RETURN_ACL_P(new_acl);
723         }
724
725         old_num = ACL_NUM(old_acl);
726         old_aip = ACL_DAT(old_acl);
727
728         /* Search for the matching entry */
729         for (dst = 0;
730                  dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
731                  ++dst)
732                  /* continue */ ;
733
734         if (dst >= old_num)
735         {
736                 /* Not found, so return copy of source ACL */
737                 new_acl = allocacl(old_num);
738                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
739         }
740         else
741         {
742                 new_num = old_num - 1;
743                 new_acl = allocacl(new_num);
744                 new_aip = ACL_DAT(new_acl);
745                 if (dst == 0)
746                 {                                               /* start */
747                         ereport(ERROR,
748                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
749                                          errmsg("cannot remove the world ACL")));
750                 }
751                 else if (dst == old_num - 1)
752                 {                                               /* end */
753                         memcpy((char *) new_aip,
754                                    (char *) old_aip,
755                                    new_num * sizeof(AclItem));
756                 }
757                 else
758                 {                                               /* middle */
759                         memcpy((char *) new_aip,
760                                    (char *) old_aip,
761                                    dst * sizeof(AclItem));
762                         memcpy((char *) (new_aip + dst),
763                                    (char *) (old_aip + dst + 1),
764                                    (new_num - dst) * sizeof(AclItem));
765                 }
766         }
767
768         PG_RETURN_ACL_P(new_acl);
769 }
770
771 Datum
772 aclcontains(PG_FUNCTION_ARGS)
773 {
774         Acl                *acl = PG_GETARG_ACL_P(0);
775         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
776         AclItem    *aidat;
777         int                     i,
778                                 num;
779
780         num = ACL_NUM(acl);
781         aidat = ACL_DAT(acl);
782         for (i = 0; i < num; ++i)
783         {
784                 if (aip->ai_grantee == aidat[i].ai_grantee
785                         && ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
786                         && aip->ai_grantor == aidat[i].ai_grantor
787                         && (ACLITEM_GET_PRIVS(*aip) & ACLITEM_GET_PRIVS(aidat[i])) == ACLITEM_GET_PRIVS(*aip)
788                         && (ACLITEM_GET_GOPTIONS(*aip) & ACLITEM_GET_GOPTIONS(aidat[i])) == ACLITEM_GET_GOPTIONS(*aip))
789                         PG_RETURN_BOOL(true);
790         }
791         PG_RETURN_BOOL(false);
792 }
793
794 Datum
795 makeaclitem(PG_FUNCTION_ARGS)
796 {
797         int32           u_grantee = PG_GETARG_INT32(0);
798         int32           g_grantee = PG_GETARG_INT32(1);
799         int32           grantor = PG_GETARG_INT32(2);
800         text       *privtext = PG_GETARG_TEXT_P(3);
801         bool            goption = PG_GETARG_BOOL(4);
802         AclItem    *aclitem;
803         AclMode         priv;
804
805         priv = convert_priv_string(privtext);
806
807         aclitem = (AclItem *) palloc(sizeof(*aclitem));
808
809         if (u_grantee == 0 && g_grantee == 0)
810         {
811                 aclitem   ->ai_grantee = 0;
812
813                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_WORLD);
814         }
815         else if (u_grantee != 0 && g_grantee != 0)
816         {
817                 ereport(ERROR,
818                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819                                  errmsg("cannot specify both user and group")));
820         }
821         else if (u_grantee != 0)
822         {
823                 aclitem   ->ai_grantee = u_grantee;
824
825                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_UID);
826         }
827         else if (g_grantee != 0)
828         {
829                 aclitem   ->ai_grantee = g_grantee;
830
831                 ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_GID);
832         }
833
834         aclitem   ->ai_grantor = grantor;
835
836         ACLITEM_SET_PRIVS(*aclitem, priv);
837         if (goption)
838                 ACLITEM_SET_GOPTIONS(*aclitem, priv);
839         else
840                 ACLITEM_SET_GOPTIONS(*aclitem, ACL_NO_RIGHTS);
841
842         PG_RETURN_ACLITEM_P(aclitem);
843 }
844
845 static AclMode
846 convert_priv_string(text *priv_type_text)
847 {
848         char       *priv_type;
849
850         priv_type = DatumGetCString(DirectFunctionCall1(textout,
851                                                                            PointerGetDatum(priv_type_text)));
852
853         if (strcasecmp(priv_type, "SELECT") == 0)
854                 return ACL_SELECT;
855         if (strcasecmp(priv_type, "INSERT") == 0)
856                 return ACL_INSERT;
857         if (strcasecmp(priv_type, "UPDATE") == 0)
858                 return ACL_UPDATE;
859         if (strcasecmp(priv_type, "DELETE") == 0)
860                 return ACL_DELETE;
861         if (strcasecmp(priv_type, "RULE") == 0)
862                 return ACL_RULE;
863         if (strcasecmp(priv_type, "REFERENCES") == 0)
864                 return ACL_REFERENCES;
865         if (strcasecmp(priv_type, "TRIGGER") == 0)
866                 return ACL_TRIGGER;
867         if (strcasecmp(priv_type, "EXECUTE") == 0)
868                 return ACL_EXECUTE;
869         if (strcasecmp(priv_type, "USAGE") == 0)
870                 return ACL_USAGE;
871         if (strcasecmp(priv_type, "CREATE") == 0)
872                 return ACL_CREATE;
873         if (strcasecmp(priv_type, "TEMP") == 0)
874                 return ACL_CREATE_TEMP;
875         if (strcasecmp(priv_type, "TEMPORARY") == 0)
876                 return ACL_CREATE_TEMP;
877
878         ereport(ERROR,
879                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
880                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
881         return ACL_NO_RIGHTS;           /* keep compiler quiet */
882 }
883
884
885 /*
886  * has_table_privilege variants
887  *              These are all named "has_table_privilege" at the SQL level.
888  *              They take various combinations of relation name, relation OID,
889  *              user name, user sysid, or implicit user = current_user.
890  *
891  *              The result is a boolean value: true if user has the indicated
892  *              privilege, false if not.
893  */
894
895 /*
896  * has_table_privilege_name_name
897  *              Check user privileges on a table given
898  *              name username, text tablename, and text priv name.
899  */
900 Datum
901 has_table_privilege_name_name(PG_FUNCTION_ARGS)
902 {
903         Name            username = PG_GETARG_NAME(0);
904         text       *tablename = PG_GETARG_TEXT_P(1);
905         text       *priv_type_text = PG_GETARG_TEXT_P(2);
906         int32           usesysid;
907         Oid                     tableoid;
908         AclMode         mode;
909         AclResult       aclresult;
910
911         usesysid = get_usesysid(NameStr(*username));
912         tableoid = convert_table_name(tablename);
913         mode = convert_table_priv_string(priv_type_text);
914
915         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
916
917         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
918 }
919
920 /*
921  * has_table_privilege_name
922  *              Check user privileges on a table given
923  *              text tablename and text priv name.
924  *              current_user is assumed
925  */
926 Datum
927 has_table_privilege_name(PG_FUNCTION_ARGS)
928 {
929         text       *tablename = PG_GETARG_TEXT_P(0);
930         text       *priv_type_text = PG_GETARG_TEXT_P(1);
931         AclId           usesysid;
932         Oid                     tableoid;
933         AclMode         mode;
934         AclResult       aclresult;
935
936         usesysid = GetUserId();
937         tableoid = convert_table_name(tablename);
938         mode = convert_table_priv_string(priv_type_text);
939
940         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
941
942         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
943 }
944
945 /*
946  * has_table_privilege_name_id
947  *              Check user privileges on a table given
948  *              name usename, table oid, and text priv name.
949  */
950 Datum
951 has_table_privilege_name_id(PG_FUNCTION_ARGS)
952 {
953         Name            username = PG_GETARG_NAME(0);
954         Oid                     tableoid = PG_GETARG_OID(1);
955         text       *priv_type_text = PG_GETARG_TEXT_P(2);
956         int32           usesysid;
957         AclMode         mode;
958         AclResult       aclresult;
959
960         usesysid = get_usesysid(NameStr(*username));
961         mode = convert_table_priv_string(priv_type_text);
962
963         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
964
965         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
966 }
967
968 /*
969  * has_table_privilege_id
970  *              Check user privileges on a table given
971  *              table oid, and text priv name.
972  *              current_user is assumed
973  */
974 Datum
975 has_table_privilege_id(PG_FUNCTION_ARGS)
976 {
977         Oid                     tableoid = PG_GETARG_OID(0);
978         text       *priv_type_text = PG_GETARG_TEXT_P(1);
979         AclId           usesysid;
980         AclMode         mode;
981         AclResult       aclresult;
982
983         usesysid = GetUserId();
984         mode = convert_table_priv_string(priv_type_text);
985
986         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
987
988         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
989 }
990
991 /*
992  * has_table_privilege_id_name
993  *              Check user privileges on a table given
994  *              usesysid, text tablename, and text priv name.
995  */
996 Datum
997 has_table_privilege_id_name(PG_FUNCTION_ARGS)
998 {
999         int32           usesysid = PG_GETARG_INT32(0);
1000         text       *tablename = PG_GETARG_TEXT_P(1);
1001         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1002         Oid                     tableoid;
1003         AclMode         mode;
1004         AclResult       aclresult;
1005
1006         tableoid = convert_table_name(tablename);
1007         mode = convert_table_priv_string(priv_type_text);
1008
1009         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1010
1011         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1012 }
1013
1014 /*
1015  * has_table_privilege_id_id
1016  *              Check user privileges on a table given
1017  *              usesysid, table oid, and text priv name.
1018  */
1019 Datum
1020 has_table_privilege_id_id(PG_FUNCTION_ARGS)
1021 {
1022         int32           usesysid = PG_GETARG_INT32(0);
1023         Oid                     tableoid = PG_GETARG_OID(1);
1024         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1025         AclMode         mode;
1026         AclResult       aclresult;
1027
1028         mode = convert_table_priv_string(priv_type_text);
1029
1030         aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
1031
1032         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1033 }
1034
1035 /*
1036  *              Support routines for has_table_privilege family.
1037  */
1038
1039 /*
1040  * Given a table name expressed as a string, look it up and return Oid
1041  */
1042 static Oid
1043 convert_table_name(text *tablename)
1044 {
1045         RangeVar   *relrv;
1046
1047         relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename,
1048                                                                                                  "has_table_privilege"));
1049
1050         return RangeVarGetRelid(relrv, false);
1051 }
1052
1053 /*
1054  * convert_table_priv_string
1055  *              Convert text string to AclMode value.
1056  */
1057 static AclMode
1058 convert_table_priv_string(text *priv_type_text)
1059 {
1060         char       *priv_type;
1061
1062         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1063                                                                            PointerGetDatum(priv_type_text)));
1064
1065         /*
1066          * Return mode from priv_type string
1067          */
1068         if (strcasecmp(priv_type, "SELECT") == 0)
1069                 return ACL_SELECT;
1070         if (strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
1071                 return ACL_GRANT_OPTION_FOR(ACL_SELECT);
1072
1073         if (strcasecmp(priv_type, "INSERT") == 0)
1074                 return ACL_INSERT;
1075         if (strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
1076                 return ACL_GRANT_OPTION_FOR(ACL_INSERT);
1077
1078         if (strcasecmp(priv_type, "UPDATE") == 0)
1079                 return ACL_UPDATE;
1080         if (strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
1081                 return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
1082
1083         if (strcasecmp(priv_type, "DELETE") == 0)
1084                 return ACL_DELETE;
1085         if (strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
1086                 return ACL_GRANT_OPTION_FOR(ACL_DELETE);
1087
1088         if (strcasecmp(priv_type, "RULE") == 0)
1089                 return ACL_RULE;
1090         if (strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
1091                 return ACL_GRANT_OPTION_FOR(ACL_RULE);
1092
1093         if (strcasecmp(priv_type, "REFERENCES") == 0)
1094                 return ACL_REFERENCES;
1095         if (strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
1096                 return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
1097
1098         if (strcasecmp(priv_type, "TRIGGER") == 0)
1099                 return ACL_TRIGGER;
1100         if (strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
1101                 return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
1102
1103         ereport(ERROR,
1104                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1105                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1106         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1107 }
1108
1109
1110 /*
1111  * has_database_privilege variants
1112  *              These are all named "has_database_privilege" at the SQL level.
1113  *              They take various combinations of database name, database OID,
1114  *              user name, user sysid, or implicit user = current_user.
1115  *
1116  *              The result is a boolean value: true if user has the indicated
1117  *              privilege, false if not.
1118  */
1119
1120 /*
1121  * has_database_privilege_name_name
1122  *              Check user privileges on a database given
1123  *              name username, text databasename, and text priv name.
1124  */
1125 Datum
1126 has_database_privilege_name_name(PG_FUNCTION_ARGS)
1127 {
1128         Name            username = PG_GETARG_NAME(0);
1129         text       *databasename = PG_GETARG_TEXT_P(1);
1130         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1131         int32           usesysid;
1132         Oid                     databaseoid;
1133         AclMode         mode;
1134         AclResult       aclresult;
1135
1136         usesysid = get_usesysid(NameStr(*username));
1137         databaseoid = convert_database_name(databasename);
1138         mode = convert_database_priv_string(priv_type_text);
1139
1140         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1141
1142         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1143 }
1144
1145 /*
1146  * has_database_privilege_name
1147  *              Check user privileges on a database given
1148  *              text databasename and text priv name.
1149  *              current_user is assumed
1150  */
1151 Datum
1152 has_database_privilege_name(PG_FUNCTION_ARGS)
1153 {
1154         text       *databasename = PG_GETARG_TEXT_P(0);
1155         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1156         AclId           usesysid;
1157         Oid                     databaseoid;
1158         AclMode         mode;
1159         AclResult       aclresult;
1160
1161         usesysid = GetUserId();
1162         databaseoid = convert_database_name(databasename);
1163         mode = convert_database_priv_string(priv_type_text);
1164
1165         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1166
1167         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1168 }
1169
1170 /*
1171  * has_database_privilege_name_id
1172  *              Check user privileges on a database given
1173  *              name usename, database oid, and text priv name.
1174  */
1175 Datum
1176 has_database_privilege_name_id(PG_FUNCTION_ARGS)
1177 {
1178         Name            username = PG_GETARG_NAME(0);
1179         Oid                     databaseoid = PG_GETARG_OID(1);
1180         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1181         int32           usesysid;
1182         AclMode         mode;
1183         AclResult       aclresult;
1184
1185         usesysid = get_usesysid(NameStr(*username));
1186         mode = convert_database_priv_string(priv_type_text);
1187
1188         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1189
1190         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1191 }
1192
1193 /*
1194  * has_database_privilege_id
1195  *              Check user privileges on a database given
1196  *              database oid, and text priv name.
1197  *              current_user is assumed
1198  */
1199 Datum
1200 has_database_privilege_id(PG_FUNCTION_ARGS)
1201 {
1202         Oid                     databaseoid = PG_GETARG_OID(0);
1203         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1204         AclId           usesysid;
1205         AclMode         mode;
1206         AclResult       aclresult;
1207
1208         usesysid = GetUserId();
1209         mode = convert_database_priv_string(priv_type_text);
1210
1211         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1212
1213         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1214 }
1215
1216 /*
1217  * has_database_privilege_id_name
1218  *              Check user privileges on a database given
1219  *              usesysid, text databasename, and text priv name.
1220  */
1221 Datum
1222 has_database_privilege_id_name(PG_FUNCTION_ARGS)
1223 {
1224         int32           usesysid = PG_GETARG_INT32(0);
1225         text       *databasename = PG_GETARG_TEXT_P(1);
1226         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1227         Oid                     databaseoid;
1228         AclMode         mode;
1229         AclResult       aclresult;
1230
1231         databaseoid = convert_database_name(databasename);
1232         mode = convert_database_priv_string(priv_type_text);
1233
1234         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1235
1236         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1237 }
1238
1239 /*
1240  * has_database_privilege_id_id
1241  *              Check user privileges on a database given
1242  *              usesysid, database oid, and text priv name.
1243  */
1244 Datum
1245 has_database_privilege_id_id(PG_FUNCTION_ARGS)
1246 {
1247         int32           usesysid = PG_GETARG_INT32(0);
1248         Oid                     databaseoid = PG_GETARG_OID(1);
1249         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1250         AclMode         mode;
1251         AclResult       aclresult;
1252
1253         mode = convert_database_priv_string(priv_type_text);
1254
1255         aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
1256
1257         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1258 }
1259
1260 /*
1261  *              Support routines for has_database_privilege family.
1262  */
1263
1264 /*
1265  * Given a database name expressed as a string, look it up and return Oid
1266  */
1267 static Oid
1268 convert_database_name(text *databasename)
1269 {
1270         char       *dbname;
1271         Oid                     oid;
1272
1273         dbname = DatumGetCString(DirectFunctionCall1(textout,
1274                                                                                  PointerGetDatum(databasename)));
1275
1276         oid = get_database_oid(dbname);
1277         if (!OidIsValid(oid))
1278                 ereport(ERROR,
1279                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
1280                                  errmsg("database \"%s\" does not exist", dbname)));
1281
1282         return oid;
1283 }
1284
1285 /*
1286  * convert_database_priv_string
1287  *              Convert text string to AclMode value.
1288  */
1289 static AclMode
1290 convert_database_priv_string(text *priv_type_text)
1291 {
1292         char       *priv_type;
1293
1294         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1295                                                                            PointerGetDatum(priv_type_text)));
1296
1297         /*
1298          * Return mode from priv_type string
1299          */
1300         if (strcasecmp(priv_type, "CREATE") == 0)
1301                 return ACL_CREATE;
1302         if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1303                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1304
1305         if (strcasecmp(priv_type, "TEMPORARY") == 0)
1306                 return ACL_CREATE_TEMP;
1307         if (strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
1308                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1309
1310         if (strcasecmp(priv_type, "TEMP") == 0)
1311                 return ACL_CREATE_TEMP;
1312         if (strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
1313                 return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
1314
1315         ereport(ERROR,
1316                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1317                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1318         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1319 }
1320
1321
1322 /*
1323  * has_function_privilege variants
1324  *              These are all named "has_function_privilege" at the SQL level.
1325  *              They take various combinations of function name, function OID,
1326  *              user name, user sysid, or implicit user = current_user.
1327  *
1328  *              The result is a boolean value: true if user has the indicated
1329  *              privilege, false if not.
1330  */
1331
1332 /*
1333  * has_function_privilege_name_name
1334  *              Check user privileges on a function given
1335  *              name username, text functionname, and text priv name.
1336  */
1337 Datum
1338 has_function_privilege_name_name(PG_FUNCTION_ARGS)
1339 {
1340         Name            username = PG_GETARG_NAME(0);
1341         text       *functionname = PG_GETARG_TEXT_P(1);
1342         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1343         int32           usesysid;
1344         Oid                     functionoid;
1345         AclMode         mode;
1346         AclResult       aclresult;
1347
1348         usesysid = get_usesysid(NameStr(*username));
1349         functionoid = convert_function_name(functionname);
1350         mode = convert_function_priv_string(priv_type_text);
1351
1352         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1353
1354         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1355 }
1356
1357 /*
1358  * has_function_privilege_name
1359  *              Check user privileges on a function given
1360  *              text functionname and text priv name.
1361  *              current_user is assumed
1362  */
1363 Datum
1364 has_function_privilege_name(PG_FUNCTION_ARGS)
1365 {
1366         text       *functionname = PG_GETARG_TEXT_P(0);
1367         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1368         AclId           usesysid;
1369         Oid                     functionoid;
1370         AclMode         mode;
1371         AclResult       aclresult;
1372
1373         usesysid = GetUserId();
1374         functionoid = convert_function_name(functionname);
1375         mode = convert_function_priv_string(priv_type_text);
1376
1377         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1378
1379         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1380 }
1381
1382 /*
1383  * has_function_privilege_name_id
1384  *              Check user privileges on a function given
1385  *              name usename, function oid, and text priv name.
1386  */
1387 Datum
1388 has_function_privilege_name_id(PG_FUNCTION_ARGS)
1389 {
1390         Name            username = PG_GETARG_NAME(0);
1391         Oid                     functionoid = PG_GETARG_OID(1);
1392         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1393         int32           usesysid;
1394         AclMode         mode;
1395         AclResult       aclresult;
1396
1397         usesysid = get_usesysid(NameStr(*username));
1398         mode = convert_function_priv_string(priv_type_text);
1399
1400         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1401
1402         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1403 }
1404
1405 /*
1406  * has_function_privilege_id
1407  *              Check user privileges on a function given
1408  *              function oid, and text priv name.
1409  *              current_user is assumed
1410  */
1411 Datum
1412 has_function_privilege_id(PG_FUNCTION_ARGS)
1413 {
1414         Oid                     functionoid = PG_GETARG_OID(0);
1415         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1416         AclId           usesysid;
1417         AclMode         mode;
1418         AclResult       aclresult;
1419
1420         usesysid = GetUserId();
1421         mode = convert_function_priv_string(priv_type_text);
1422
1423         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1424
1425         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1426 }
1427
1428 /*
1429  * has_function_privilege_id_name
1430  *              Check user privileges on a function given
1431  *              usesysid, text functionname, and text priv name.
1432  */
1433 Datum
1434 has_function_privilege_id_name(PG_FUNCTION_ARGS)
1435 {
1436         int32           usesysid = PG_GETARG_INT32(0);
1437         text       *functionname = PG_GETARG_TEXT_P(1);
1438         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1439         Oid                     functionoid;
1440         AclMode         mode;
1441         AclResult       aclresult;
1442
1443         functionoid = convert_function_name(functionname);
1444         mode = convert_function_priv_string(priv_type_text);
1445
1446         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1447
1448         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1449 }
1450
1451 /*
1452  * has_function_privilege_id_id
1453  *              Check user privileges on a function given
1454  *              usesysid, function oid, and text priv name.
1455  */
1456 Datum
1457 has_function_privilege_id_id(PG_FUNCTION_ARGS)
1458 {
1459         int32           usesysid = PG_GETARG_INT32(0);
1460         Oid                     functionoid = PG_GETARG_OID(1);
1461         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1462         AclMode         mode;
1463         AclResult       aclresult;
1464
1465         mode = convert_function_priv_string(priv_type_text);
1466
1467         aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
1468
1469         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1470 }
1471
1472 /*
1473  *              Support routines for has_function_privilege family.
1474  */
1475
1476 /*
1477  * Given a function name expressed as a string, look it up and return Oid
1478  */
1479 static Oid
1480 convert_function_name(text *functionname)
1481 {
1482         char       *funcname;
1483         Oid                     oid;
1484
1485         funcname = DatumGetCString(DirectFunctionCall1(textout,
1486                                                                                  PointerGetDatum(functionname)));
1487
1488         oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
1489                                                                                          CStringGetDatum(funcname)));
1490
1491         if (!OidIsValid(oid))
1492                 ereport(ERROR,
1493                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1494                                  errmsg("function \"%s\" does not exist", funcname)));
1495
1496         return oid;
1497 }
1498
1499 /*
1500  * convert_function_priv_string
1501  *              Convert text string to AclMode value.
1502  */
1503 static AclMode
1504 convert_function_priv_string(text *priv_type_text)
1505 {
1506         char       *priv_type;
1507
1508         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1509                                                                            PointerGetDatum(priv_type_text)));
1510
1511         /*
1512          * Return mode from priv_type string
1513          */
1514         if (strcasecmp(priv_type, "EXECUTE") == 0)
1515                 return ACL_EXECUTE;
1516         if (strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
1517                 return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
1518
1519         ereport(ERROR,
1520                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1521                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1522         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1523 }
1524
1525
1526 /*
1527  * has_language_privilege variants
1528  *              These are all named "has_language_privilege" at the SQL level.
1529  *              They take various combinations of language name, language OID,
1530  *              user name, user sysid, or implicit user = current_user.
1531  *
1532  *              The result is a boolean value: true if user has the indicated
1533  *              privilege, false if not.
1534  */
1535
1536 /*
1537  * has_language_privilege_name_name
1538  *              Check user privileges on a language given
1539  *              name username, text languagename, and text priv name.
1540  */
1541 Datum
1542 has_language_privilege_name_name(PG_FUNCTION_ARGS)
1543 {
1544         Name            username = PG_GETARG_NAME(0);
1545         text       *languagename = PG_GETARG_TEXT_P(1);
1546         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1547         int32           usesysid;
1548         Oid                     languageoid;
1549         AclMode         mode;
1550         AclResult       aclresult;
1551
1552         usesysid = get_usesysid(NameStr(*username));
1553         languageoid = convert_language_name(languagename);
1554         mode = convert_language_priv_string(priv_type_text);
1555
1556         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1557
1558         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1559 }
1560
1561 /*
1562  * has_language_privilege_name
1563  *              Check user privileges on a language given
1564  *              text languagename and text priv name.
1565  *              current_user is assumed
1566  */
1567 Datum
1568 has_language_privilege_name(PG_FUNCTION_ARGS)
1569 {
1570         text       *languagename = PG_GETARG_TEXT_P(0);
1571         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1572         AclId           usesysid;
1573         Oid                     languageoid;
1574         AclMode         mode;
1575         AclResult       aclresult;
1576
1577         usesysid = GetUserId();
1578         languageoid = convert_language_name(languagename);
1579         mode = convert_language_priv_string(priv_type_text);
1580
1581         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1582
1583         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1584 }
1585
1586 /*
1587  * has_language_privilege_name_id
1588  *              Check user privileges on a language given
1589  *              name usename, language oid, and text priv name.
1590  */
1591 Datum
1592 has_language_privilege_name_id(PG_FUNCTION_ARGS)
1593 {
1594         Name            username = PG_GETARG_NAME(0);
1595         Oid                     languageoid = PG_GETARG_OID(1);
1596         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1597         int32           usesysid;
1598         AclMode         mode;
1599         AclResult       aclresult;
1600
1601         usesysid = get_usesysid(NameStr(*username));
1602         mode = convert_language_priv_string(priv_type_text);
1603
1604         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1605
1606         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1607 }
1608
1609 /*
1610  * has_language_privilege_id
1611  *              Check user privileges on a language given
1612  *              language oid, and text priv name.
1613  *              current_user is assumed
1614  */
1615 Datum
1616 has_language_privilege_id(PG_FUNCTION_ARGS)
1617 {
1618         Oid                     languageoid = PG_GETARG_OID(0);
1619         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1620         AclId           usesysid;
1621         AclMode         mode;
1622         AclResult       aclresult;
1623
1624         usesysid = GetUserId();
1625         mode = convert_language_priv_string(priv_type_text);
1626
1627         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1628
1629         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1630 }
1631
1632 /*
1633  * has_language_privilege_id_name
1634  *              Check user privileges on a language given
1635  *              usesysid, text languagename, and text priv name.
1636  */
1637 Datum
1638 has_language_privilege_id_name(PG_FUNCTION_ARGS)
1639 {
1640         int32           usesysid = PG_GETARG_INT32(0);
1641         text       *languagename = PG_GETARG_TEXT_P(1);
1642         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1643         Oid                     languageoid;
1644         AclMode         mode;
1645         AclResult       aclresult;
1646
1647         languageoid = convert_language_name(languagename);
1648         mode = convert_language_priv_string(priv_type_text);
1649
1650         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1651
1652         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1653 }
1654
1655 /*
1656  * has_language_privilege_id_id
1657  *              Check user privileges on a language given
1658  *              usesysid, language oid, and text priv name.
1659  */
1660 Datum
1661 has_language_privilege_id_id(PG_FUNCTION_ARGS)
1662 {
1663         int32           usesysid = PG_GETARG_INT32(0);
1664         Oid                     languageoid = PG_GETARG_OID(1);
1665         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1666         AclMode         mode;
1667         AclResult       aclresult;
1668
1669         mode = convert_language_priv_string(priv_type_text);
1670
1671         aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
1672
1673         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1674 }
1675
1676 /*
1677  *              Support routines for has_language_privilege family.
1678  */
1679
1680 /*
1681  * Given a language name expressed as a string, look it up and return Oid
1682  */
1683 static Oid
1684 convert_language_name(text *languagename)
1685 {
1686         char       *langname;
1687         Oid                     oid;
1688
1689         langname = DatumGetCString(DirectFunctionCall1(textout,
1690                                                                                  PointerGetDatum(languagename)));
1691
1692         oid = GetSysCacheOid(LANGNAME,
1693                                                  CStringGetDatum(langname),
1694                                                  0, 0, 0);
1695         if (!OidIsValid(oid))
1696                 ereport(ERROR,
1697                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1698                                  errmsg("language \"%s\" does not exist", langname)));
1699
1700         return oid;
1701 }
1702
1703 /*
1704  * convert_language_priv_string
1705  *              Convert text string to AclMode value.
1706  */
1707 static AclMode
1708 convert_language_priv_string(text *priv_type_text)
1709 {
1710         char       *priv_type;
1711
1712         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1713                                                                            PointerGetDatum(priv_type_text)));
1714
1715         /*
1716          * Return mode from priv_type string
1717          */
1718         if (strcasecmp(priv_type, "USAGE") == 0)
1719                 return ACL_USAGE;
1720         if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
1721                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
1722
1723         ereport(ERROR,
1724                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1725                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1726         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1727 }
1728
1729
1730 /*
1731  * has_schema_privilege variants
1732  *              These are all named "has_schema_privilege" at the SQL level.
1733  *              They take various combinations of schema name, schema OID,
1734  *              user name, user sysid, or implicit user = current_user.
1735  *
1736  *              The result is a boolean value: true if user has the indicated
1737  *              privilege, false if not.
1738  */
1739
1740 /*
1741  * has_schema_privilege_name_name
1742  *              Check user privileges on a schema given
1743  *              name username, text schemaname, and text priv name.
1744  */
1745 Datum
1746 has_schema_privilege_name_name(PG_FUNCTION_ARGS)
1747 {
1748         Name            username = PG_GETARG_NAME(0);
1749         text       *schemaname = PG_GETARG_TEXT_P(1);
1750         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1751         int32           usesysid;
1752         Oid                     schemaoid;
1753         AclMode         mode;
1754         AclResult       aclresult;
1755
1756         usesysid = get_usesysid(NameStr(*username));
1757         schemaoid = convert_schema_name(schemaname);
1758         mode = convert_schema_priv_string(priv_type_text);
1759
1760         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1761
1762         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1763 }
1764
1765 /*
1766  * has_schema_privilege_name
1767  *              Check user privileges on a schema given
1768  *              text schemaname and text priv name.
1769  *              current_user is assumed
1770  */
1771 Datum
1772 has_schema_privilege_name(PG_FUNCTION_ARGS)
1773 {
1774         text       *schemaname = PG_GETARG_TEXT_P(0);
1775         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1776         AclId           usesysid;
1777         Oid                     schemaoid;
1778         AclMode         mode;
1779         AclResult       aclresult;
1780
1781         usesysid = GetUserId();
1782         schemaoid = convert_schema_name(schemaname);
1783         mode = convert_schema_priv_string(priv_type_text);
1784
1785         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1786
1787         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1788 }
1789
1790 /*
1791  * has_schema_privilege_name_id
1792  *              Check user privileges on a schema given
1793  *              name usename, schema oid, and text priv name.
1794  */
1795 Datum
1796 has_schema_privilege_name_id(PG_FUNCTION_ARGS)
1797 {
1798         Name            username = PG_GETARG_NAME(0);
1799         Oid                     schemaoid = PG_GETARG_OID(1);
1800         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1801         int32           usesysid;
1802         AclMode         mode;
1803         AclResult       aclresult;
1804
1805         usesysid = get_usesysid(NameStr(*username));
1806         mode = convert_schema_priv_string(priv_type_text);
1807
1808         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1809
1810         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1811 }
1812
1813 /*
1814  * has_schema_privilege_id
1815  *              Check user privileges on a schema given
1816  *              schema oid, and text priv name.
1817  *              current_user is assumed
1818  */
1819 Datum
1820 has_schema_privilege_id(PG_FUNCTION_ARGS)
1821 {
1822         Oid                     schemaoid = PG_GETARG_OID(0);
1823         text       *priv_type_text = PG_GETARG_TEXT_P(1);
1824         AclId           usesysid;
1825         AclMode         mode;
1826         AclResult       aclresult;
1827
1828         usesysid = GetUserId();
1829         mode = convert_schema_priv_string(priv_type_text);
1830
1831         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1832
1833         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1834 }
1835
1836 /*
1837  * has_schema_privilege_id_name
1838  *              Check user privileges on a schema given
1839  *              usesysid, text schemaname, and text priv name.
1840  */
1841 Datum
1842 has_schema_privilege_id_name(PG_FUNCTION_ARGS)
1843 {
1844         int32           usesysid = PG_GETARG_INT32(0);
1845         text       *schemaname = PG_GETARG_TEXT_P(1);
1846         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1847         Oid                     schemaoid;
1848         AclMode         mode;
1849         AclResult       aclresult;
1850
1851         schemaoid = convert_schema_name(schemaname);
1852         mode = convert_schema_priv_string(priv_type_text);
1853
1854         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1855
1856         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1857 }
1858
1859 /*
1860  * has_schema_privilege_id_id
1861  *              Check user privileges on a schema given
1862  *              usesysid, schema oid, and text priv name.
1863  */
1864 Datum
1865 has_schema_privilege_id_id(PG_FUNCTION_ARGS)
1866 {
1867         int32           usesysid = PG_GETARG_INT32(0);
1868         Oid                     schemaoid = PG_GETARG_OID(1);
1869         text       *priv_type_text = PG_GETARG_TEXT_P(2);
1870         AclMode         mode;
1871         AclResult       aclresult;
1872
1873         mode = convert_schema_priv_string(priv_type_text);
1874
1875         aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
1876
1877         PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
1878 }
1879
1880 /*
1881  *              Support routines for has_schema_privilege family.
1882  */
1883
1884 /*
1885  * Given a schema name expressed as a string, look it up and return Oid
1886  */
1887 static Oid
1888 convert_schema_name(text *schemaname)
1889 {
1890         char       *nspname;
1891         Oid                     oid;
1892
1893         nspname = DatumGetCString(DirectFunctionCall1(textout,
1894                                                                                    PointerGetDatum(schemaname)));
1895
1896         oid = GetSysCacheOid(NAMESPACENAME,
1897                                                  CStringGetDatum(nspname),
1898                                                  0, 0, 0);
1899         if (!OidIsValid(oid))
1900                 ereport(ERROR,
1901                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1902                                  errmsg("schema \"%s\" does not exist", nspname)));
1903
1904         return oid;
1905 }
1906
1907 /*
1908  * convert_schema_priv_string
1909  *              Convert text string to AclMode value.
1910  */
1911 static AclMode
1912 convert_schema_priv_string(text *priv_type_text)
1913 {
1914         char       *priv_type;
1915
1916         priv_type = DatumGetCString(DirectFunctionCall1(textout,
1917                                                                            PointerGetDatum(priv_type_text)));
1918
1919         /*
1920          * Return mode from priv_type string
1921          */
1922         if (strcasecmp(priv_type, "CREATE") == 0)
1923                 return ACL_CREATE;
1924         if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
1925                 return ACL_GRANT_OPTION_FOR(ACL_CREATE);
1926
1927         if (strcasecmp(priv_type, "USAGE") == 0)
1928                 return ACL_USAGE;
1929         if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
1930                 return ACL_GRANT_OPTION_FOR(ACL_USAGE);
1931
1932         ereport(ERROR,
1933                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1934                          errmsg("unrecognized privilege type: \"%s\"", priv_type)));
1935         return ACL_NO_RIGHTS;           /* keep compiler quiet */
1936 }