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