Header And Logo

PostgreSQL
| The world's most advanced open source database.

acl.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * acl.c
00004  *    Basic access control list data structures manipulation routines.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/adt/acl.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <ctype.h>
00018 
00019 #include "access/htup_details.h"
00020 #include "catalog/namespace.h"
00021 #include "catalog/pg_authid.h"
00022 #include "catalog/pg_auth_members.h"
00023 #include "catalog/pg_type.h"
00024 #include "catalog/pg_class.h"
00025 #include "commands/dbcommands.h"
00026 #include "commands/proclang.h"
00027 #include "commands/tablespace.h"
00028 #include "foreign/foreign.h"
00029 #include "funcapi.h"
00030 #include "miscadmin.h"
00031 #include "utils/acl.h"
00032 #include "utils/builtins.h"
00033 #include "utils/catcache.h"
00034 #include "utils/inval.h"
00035 #include "utils/lsyscache.h"
00036 #include "utils/memutils.h"
00037 #include "utils/syscache.h"
00038 
00039 
00040 typedef struct
00041 {
00042     const char *name;
00043     AclMode     value;
00044 } priv_map;
00045 
00046 /*
00047  * We frequently need to test whether a given role is a member of some other
00048  * role.  In most of these tests the "given role" is the same, namely the
00049  * active current user.  So we can optimize it by keeping a cached list of
00050  * all the roles the "given role" is a member of, directly or indirectly.
00051  * The cache is flushed whenever we detect a change in pg_auth_members.
00052  *
00053  * There are actually two caches, one computed under "has_privs" rules
00054  * (do not recurse where rolinherit isn't true) and one computed under
00055  * "is_member" rules (recurse regardless of rolinherit).
00056  *
00057  * Possibly this mechanism should be generalized to allow caching membership
00058  * info for multiple roles?
00059  *
00060  * The has_privs cache is:
00061  * cached_privs_role is the role OID the cache is for.
00062  * cached_privs_roles is an OID list of roles that cached_privs_role
00063  *      has the privileges of (always including itself).
00064  * The cache is valid if cached_privs_role is not InvalidOid.
00065  *
00066  * The is_member cache is similarly:
00067  * cached_member_role is the role OID the cache is for.
00068  * cached_membership_roles is an OID list of roles that cached_member_role
00069  *      is a member of (always including itself).
00070  * The cache is valid if cached_member_role is not InvalidOid.
00071  */
00072 static Oid  cached_privs_role = InvalidOid;
00073 static List *cached_privs_roles = NIL;
00074 static Oid  cached_member_role = InvalidOid;
00075 static List *cached_membership_roles = NIL;
00076 
00077 
00078 static const char *getid(const char *s, char *n);
00079 static void putid(char *p, const char *s);
00080 static Acl *allocacl(int n);
00081 static void check_acl(const Acl *acl);
00082 static const char *aclparse(const char *s, AclItem *aip);
00083 static bool aclitem_match(const AclItem *a1, const AclItem *a2);
00084 static int  aclitemComparator(const void *arg1, const void *arg2);
00085 static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
00086                   Oid ownerId);
00087 static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
00088                  Oid ownerId, DropBehavior behavior);
00089 static int  oidComparator(const void *arg1, const void *arg2);
00090 
00091 static AclMode convert_priv_string(text *priv_type_text);
00092 static AclMode convert_any_priv_string(text *priv_type_text,
00093                         const priv_map *privileges);
00094 
00095 static Oid  convert_table_name(text *tablename);
00096 static AclMode convert_table_priv_string(text *priv_type_text);
00097 static AclMode convert_sequence_priv_string(text *priv_type_text);
00098 static AttrNumber convert_column_name(Oid tableoid, text *column);
00099 static AclMode convert_column_priv_string(text *priv_type_text);
00100 static Oid  convert_database_name(text *databasename);
00101 static AclMode convert_database_priv_string(text *priv_type_text);
00102 static Oid  convert_foreign_data_wrapper_name(text *fdwname);
00103 static AclMode convert_foreign_data_wrapper_priv_string(text *priv_type_text);
00104 static Oid  convert_function_name(text *functionname);
00105 static AclMode convert_function_priv_string(text *priv_type_text);
00106 static Oid  convert_language_name(text *languagename);
00107 static AclMode convert_language_priv_string(text *priv_type_text);
00108 static Oid  convert_schema_name(text *schemaname);
00109 static AclMode convert_schema_priv_string(text *priv_type_text);
00110 static Oid  convert_server_name(text *servername);
00111 static AclMode convert_server_priv_string(text *priv_type_text);
00112 static Oid  convert_tablespace_name(text *tablespacename);
00113 static AclMode convert_tablespace_priv_string(text *priv_type_text);
00114 static Oid  convert_type_name(text *typename);
00115 static AclMode convert_type_priv_string(text *priv_type_text);
00116 static AclMode convert_role_priv_string(text *priv_type_text);
00117 static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
00118 
00119 static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
00120 static Oid  get_role_oid_or_public(const char *rolname);
00121 
00122 
00123 /*
00124  * getid
00125  *      Consumes the first alphanumeric string (identifier) found in string
00126  *      's', ignoring any leading white space.  If it finds a double quote
00127  *      it returns the word inside the quotes.
00128  *
00129  * RETURNS:
00130  *      the string position in 's' that points to the next non-space character
00131  *      in 's', after any quotes.  Also:
00132  *      - loads the identifier into 'n'.  (If no identifier is found, 'n'
00133  *        contains an empty string.)  'n' must be NAMEDATALEN bytes.
00134  */
00135 static const char *
00136 getid(const char *s, char *n)
00137 {
00138     int         len = 0;
00139     bool        in_quotes = false;
00140 
00141     Assert(s && n);
00142 
00143     while (isspace((unsigned char) *s))
00144         s++;
00145     /* This code had better match what putid() does, below */
00146     for (;
00147          *s != '\0' &&
00148          (isalnum((unsigned char) *s) ||
00149           *s == '_' ||
00150           *s == '"' ||
00151           in_quotes);
00152          s++)
00153     {
00154         if (*s == '"')
00155         {
00156             /* safe to look at next char (could be '\0' though) */
00157             if (*(s + 1) != '"')
00158             {
00159                 in_quotes = !in_quotes;
00160                 continue;
00161             }
00162             /* it's an escaped double quote; skip the escaping char */
00163             s++;
00164         }
00165 
00166         /* Add the character to the string */
00167         if (len >= NAMEDATALEN - 1)
00168             ereport(ERROR,
00169                     (errcode(ERRCODE_NAME_TOO_LONG),
00170                      errmsg("identifier too long"),
00171                      errdetail("Identifier must be less than %d characters.",
00172                                NAMEDATALEN)));
00173 
00174         n[len++] = *s;
00175     }
00176     n[len] = '\0';
00177     while (isspace((unsigned char) *s))
00178         s++;
00179     return s;
00180 }
00181 
00182 /*
00183  * Write a role name at *p, adding double quotes if needed.
00184  * There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
00185  * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c
00186  */
00187 static void
00188 putid(char *p, const char *s)
00189 {
00190     const char *src;
00191     bool        safe = true;
00192 
00193     for (src = s; *src; src++)
00194     {
00195         /* This test had better match what getid() does, above */
00196         if (!isalnum((unsigned char) *src) && *src != '_')
00197         {
00198             safe = false;
00199             break;
00200         }
00201     }
00202     if (!safe)
00203         *p++ = '"';
00204     for (src = s; *src; src++)
00205     {
00206         /* A double quote character in a username is encoded as "" */
00207         if (*src == '"')
00208             *p++ = '"';
00209         *p++ = *src;
00210     }
00211     if (!safe)
00212         *p++ = '"';
00213     *p = '\0';
00214 }
00215 
00216 /*
00217  * aclparse
00218  *      Consumes and parses an ACL specification of the form:
00219  *              [group|user] [A-Za-z0-9]*=[rwaR]*
00220  *      from string 's', ignoring any leading white space or white space
00221  *      between the optional id type keyword (group|user) and the actual
00222  *      ACL specification.
00223  *
00224  *      The group|user decoration is unnecessary in the roles world,
00225  *      but we still accept it for backward compatibility.
00226  *
00227  *      This routine is called by the parser as well as aclitemin(), hence
00228  *      the added generality.
00229  *
00230  * RETURNS:
00231  *      the string position in 's' immediately following the ACL
00232  *      specification.  Also:
00233  *      - loads the structure pointed to by 'aip' with the appropriate
00234  *        UID/GID, id type identifier and mode type values.
00235  */
00236 static const char *
00237 aclparse(const char *s, AclItem *aip)
00238 {
00239     AclMode     privs,
00240                 goption,
00241                 read;
00242     char        name[NAMEDATALEN];
00243     char        name2[NAMEDATALEN];
00244 
00245     Assert(s && aip);
00246 
00247 #ifdef ACLDEBUG
00248     elog(LOG, "aclparse: input = \"%s\"", s);
00249 #endif
00250     s = getid(s, name);
00251     if (*s != '=')
00252     {
00253         /* we just read a keyword, not a name */
00254         if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
00255             ereport(ERROR,
00256                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00257                      errmsg("unrecognized key word: \"%s\"", name),
00258                      errhint("ACL key word must be \"group\" or \"user\".")));
00259         s = getid(s, name);     /* move s to the name beyond the keyword */
00260         if (name[0] == '\0')
00261             ereport(ERROR,
00262                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00263                      errmsg("missing name"),
00264                      errhint("A name must follow the \"group\" or \"user\" key word.")));
00265     }
00266 
00267     if (*s != '=')
00268         ereport(ERROR,
00269                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00270                  errmsg("missing \"=\" sign")));
00271 
00272     privs = goption = ACL_NO_RIGHTS;
00273 
00274     for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
00275     {
00276         switch (*s)
00277         {
00278             case '*':
00279                 goption |= read;
00280                 break;
00281             case ACL_INSERT_CHR:
00282                 read = ACL_INSERT;
00283                 break;
00284             case ACL_SELECT_CHR:
00285                 read = ACL_SELECT;
00286                 break;
00287             case ACL_UPDATE_CHR:
00288                 read = ACL_UPDATE;
00289                 break;
00290             case ACL_DELETE_CHR:
00291                 read = ACL_DELETE;
00292                 break;
00293             case ACL_TRUNCATE_CHR:
00294                 read = ACL_TRUNCATE;
00295                 break;
00296             case ACL_REFERENCES_CHR:
00297                 read = ACL_REFERENCES;
00298                 break;
00299             case ACL_TRIGGER_CHR:
00300                 read = ACL_TRIGGER;
00301                 break;
00302             case ACL_EXECUTE_CHR:
00303                 read = ACL_EXECUTE;
00304                 break;
00305             case ACL_USAGE_CHR:
00306                 read = ACL_USAGE;
00307                 break;
00308             case ACL_CREATE_CHR:
00309                 read = ACL_CREATE;
00310                 break;
00311             case ACL_CREATE_TEMP_CHR:
00312                 read = ACL_CREATE_TEMP;
00313                 break;
00314             case ACL_CONNECT_CHR:
00315                 read = ACL_CONNECT;
00316                 break;
00317             case 'R':           /* ignore old RULE privileges */
00318                 read = 0;
00319                 break;
00320             default:
00321                 ereport(ERROR,
00322                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00323                       errmsg("invalid mode character: must be one of \"%s\"",
00324                              ACL_ALL_RIGHTS_STR)));
00325         }
00326 
00327         privs |= read;
00328     }
00329 
00330     if (name[0] == '\0')
00331         aip->ai_grantee = ACL_ID_PUBLIC;
00332     else
00333         aip->ai_grantee = get_role_oid(name, false);
00334 
00335     /*
00336      * XXX Allow a degree of backward compatibility by defaulting the grantor
00337      * to the superuser.
00338      */
00339     if (*s == '/')
00340     {
00341         s = getid(s + 1, name2);
00342         if (name2[0] == '\0')
00343             ereport(ERROR,
00344                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00345                      errmsg("a name must follow the \"/\" sign")));
00346         aip->ai_grantor = get_role_oid(name2, false);
00347     }
00348     else
00349     {
00350         aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
00351         ereport(WARNING,
00352                 (errcode(ERRCODE_INVALID_GRANTOR),
00353                  errmsg("defaulting grantor to user ID %u",
00354                         BOOTSTRAP_SUPERUSERID)));
00355     }
00356 
00357     ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
00358 
00359 #ifdef ACLDEBUG
00360     elog(LOG, "aclparse: correctly read [%u %x %x]",
00361          aip->ai_grantee, privs, goption);
00362 #endif
00363 
00364     return s;
00365 }
00366 
00367 /*
00368  * allocacl
00369  *      Allocates storage for a new Acl with 'n' entries.
00370  *
00371  * RETURNS:
00372  *      the new Acl
00373  */
00374 static Acl *
00375 allocacl(int n)
00376 {
00377     Acl        *new_acl;
00378     Size        size;
00379 
00380     if (n < 0)
00381         elog(ERROR, "invalid size: %d", n);
00382     size = ACL_N_SIZE(n);
00383     new_acl = (Acl *) palloc0(size);
00384     SET_VARSIZE(new_acl, size);
00385     new_acl->ndim = 1;
00386     new_acl->dataoffset = 0;    /* we never put in any nulls */
00387     new_acl->elemtype = ACLITEMOID;
00388     ARR_LBOUND(new_acl)[0] = 1;
00389     ARR_DIMS(new_acl)[0] = n;
00390     return new_acl;
00391 }
00392 
00393 /*
00394  * Create a zero-entry ACL
00395  */
00396 Acl *
00397 make_empty_acl(void)
00398 {
00399     return allocacl(0);
00400 }
00401 
00402 /*
00403  * Copy an ACL
00404  */
00405 Acl *
00406 aclcopy(const Acl *orig_acl)
00407 {
00408     Acl        *result_acl;
00409 
00410     result_acl = allocacl(ACL_NUM(orig_acl));
00411 
00412     memcpy(ACL_DAT(result_acl),
00413            ACL_DAT(orig_acl),
00414            ACL_NUM(orig_acl) * sizeof(AclItem));
00415 
00416     return result_acl;
00417 }
00418 
00419 /*
00420  * Concatenate two ACLs
00421  *
00422  * This is a bit cheesy, since we may produce an ACL with redundant entries.
00423  * Be careful what the result is used for!
00424  */
00425 Acl *
00426 aclconcat(const Acl *left_acl, const Acl *right_acl)
00427 {
00428     Acl        *result_acl;
00429 
00430     result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
00431 
00432     memcpy(ACL_DAT(result_acl),
00433            ACL_DAT(left_acl),
00434            ACL_NUM(left_acl) * sizeof(AclItem));
00435 
00436     memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
00437            ACL_DAT(right_acl),
00438            ACL_NUM(right_acl) * sizeof(AclItem));
00439 
00440     return result_acl;
00441 }
00442 
00443 /*
00444  * Merge two ACLs
00445  *
00446  * This produces a properly merged ACL with no redundant entries.
00447  * Returns NULL on NULL input.
00448  */
00449 Acl *
00450 aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
00451 {
00452     Acl        *result_acl;
00453     AclItem    *aip;
00454     int         i,
00455                 num;
00456 
00457     /* Check for cases where one or both are empty/null */
00458     if (left_acl == NULL || ACL_NUM(left_acl) == 0)
00459     {
00460         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
00461             return NULL;
00462         else
00463             return aclcopy(right_acl);
00464     }
00465     else
00466     {
00467         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
00468             return aclcopy(left_acl);
00469     }
00470 
00471     /* Merge them the hard way, one item at a time */
00472     result_acl = aclcopy(left_acl);
00473 
00474     aip = ACL_DAT(right_acl);
00475     num = ACL_NUM(right_acl);
00476 
00477     for (i = 0; i < num; i++, aip++)
00478     {
00479         Acl        *tmp_acl;
00480 
00481         tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
00482                             ownerId, DROP_RESTRICT);
00483         pfree(result_acl);
00484         result_acl = tmp_acl;
00485     }
00486 
00487     return result_acl;
00488 }
00489 
00490 /*
00491  * Sort the items in an ACL (into an arbitrary but consistent order)
00492  */
00493 void
00494 aclitemsort(Acl *acl)
00495 {
00496     if (acl != NULL && ACL_NUM(acl) > 1)
00497         qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
00498 }
00499 
00500 /*
00501  * Check if two ACLs are exactly equal
00502  *
00503  * This will not detect equality if the two arrays contain the same items
00504  * in different orders.  To handle that case, sort both inputs first,
00505  * using aclitemsort().
00506  */
00507 bool
00508 aclequal(const Acl *left_acl, const Acl *right_acl)
00509 {
00510     /* Check for cases where one or both are empty/null */
00511     if (left_acl == NULL || ACL_NUM(left_acl) == 0)
00512     {
00513         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
00514             return true;
00515         else
00516             return false;
00517     }
00518     else
00519     {
00520         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
00521             return false;
00522     }
00523 
00524     if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
00525         return false;
00526 
00527     if (memcmp(ACL_DAT(left_acl),
00528                ACL_DAT(right_acl),
00529                ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
00530         return true;
00531 
00532     return false;
00533 }
00534 
00535 /*
00536  * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
00537  */
00538 static void
00539 check_acl(const Acl *acl)
00540 {
00541     if (ARR_ELEMTYPE(acl) != ACLITEMOID)
00542         ereport(ERROR,
00543                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00544                  errmsg("ACL array contains wrong data type")));
00545     if (ARR_NDIM(acl) != 1)
00546         ereport(ERROR,
00547                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00548                  errmsg("ACL arrays must be one-dimensional")));
00549     if (ARR_HASNULL(acl))
00550         ereport(ERROR,
00551                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
00552                  errmsg("ACL arrays must not contain null values")));
00553 }
00554 
00555 /*
00556  * aclitemin
00557  *      Allocates storage for, and fills in, a new AclItem given a string
00558  *      's' that contains an ACL specification.  See aclparse for details.
00559  *
00560  * RETURNS:
00561  *      the new AclItem
00562  */
00563 Datum
00564 aclitemin(PG_FUNCTION_ARGS)
00565 {
00566     const char *s = PG_GETARG_CSTRING(0);
00567     AclItem    *aip;
00568 
00569     aip = (AclItem *) palloc(sizeof(AclItem));
00570     s = aclparse(s, aip);
00571     while (isspace((unsigned char) *s))
00572         ++s;
00573     if (*s)
00574         ereport(ERROR,
00575                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00576                errmsg("extra garbage at the end of the ACL specification")));
00577 
00578     PG_RETURN_ACLITEM_P(aip);
00579 }
00580 
00581 /*
00582  * aclitemout
00583  *      Allocates storage for, and fills in, a new null-delimited string
00584  *      containing a formatted ACL specification.  See aclparse for details.
00585  *
00586  * RETURNS:
00587  *      the new string
00588  */
00589 Datum
00590 aclitemout(PG_FUNCTION_ARGS)
00591 {
00592     AclItem    *aip = PG_GETARG_ACLITEM_P(0);
00593     char       *p;
00594     char       *out;
00595     HeapTuple   htup;
00596     unsigned    i;
00597 
00598     out = palloc(strlen("=/") +
00599                  2 * N_ACL_RIGHTS +
00600                  2 * (2 * NAMEDATALEN + 2) +
00601                  1);
00602 
00603     p = out;
00604     *p = '\0';
00605 
00606     if (aip->ai_grantee != ACL_ID_PUBLIC)
00607     {
00608         htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee));
00609         if (HeapTupleIsValid(htup))
00610         {
00611             putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
00612             ReleaseSysCache(htup);
00613         }
00614         else
00615         {
00616             /* Generate numeric OID if we don't find an entry */
00617             sprintf(p, "%u", aip->ai_grantee);
00618         }
00619     }
00620     while (*p)
00621         ++p;
00622 
00623     *p++ = '=';
00624 
00625     for (i = 0; i < N_ACL_RIGHTS; ++i)
00626     {
00627         if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
00628             *p++ = ACL_ALL_RIGHTS_STR[i];
00629         if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
00630             *p++ = '*';
00631     }
00632 
00633     *p++ = '/';
00634     *p = '\0';
00635 
00636     htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor));
00637     if (HeapTupleIsValid(htup))
00638     {
00639         putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
00640         ReleaseSysCache(htup);
00641     }
00642     else
00643     {
00644         /* Generate numeric OID if we don't find an entry */
00645         sprintf(p, "%u", aip->ai_grantor);
00646     }
00647 
00648     PG_RETURN_CSTRING(out);
00649 }
00650 
00651 /*
00652  * aclitem_match
00653  *      Two AclItems are considered to match iff they have the same
00654  *      grantee and grantor; the privileges are ignored.
00655  */
00656 static bool
00657 aclitem_match(const AclItem *a1, const AclItem *a2)
00658 {
00659     return a1->ai_grantee == a2->ai_grantee &&
00660         a1->ai_grantor == a2->ai_grantor;
00661 }
00662 
00663 /*
00664  * aclitemComparator
00665  *      qsort comparison function for AclItems
00666  */
00667 static int
00668 aclitemComparator(const void *arg1, const void *arg2)
00669 {
00670     const AclItem *a1 = (const AclItem *) arg1;
00671     const AclItem *a2 = (const AclItem *) arg2;
00672 
00673     if (a1->ai_grantee > a2->ai_grantee)
00674         return 1;
00675     if (a1->ai_grantee < a2->ai_grantee)
00676         return -1;
00677     if (a1->ai_grantor > a2->ai_grantor)
00678         return 1;
00679     if (a1->ai_grantor < a2->ai_grantor)
00680         return -1;
00681     if (a1->ai_privs > a2->ai_privs)
00682         return 1;
00683     if (a1->ai_privs < a2->ai_privs)
00684         return -1;
00685     return 0;
00686 }
00687 
00688 /*
00689  * aclitem equality operator
00690  */
00691 Datum
00692 aclitem_eq(PG_FUNCTION_ARGS)
00693 {
00694     AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
00695     AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
00696     bool        result;
00697 
00698     result = a1->ai_privs == a2->ai_privs &&
00699         a1->ai_grantee == a2->ai_grantee &&
00700         a1->ai_grantor == a2->ai_grantor;
00701     PG_RETURN_BOOL(result);
00702 }
00703 
00704 /*
00705  * aclitem hash function
00706  *
00707  * We make aclitems hashable not so much because anyone is likely to hash
00708  * them, as because we want array equality to work on aclitem arrays, and
00709  * with the typcache mechanism we must have a hash or btree opclass.
00710  */
00711 Datum
00712 hash_aclitem(PG_FUNCTION_ARGS)
00713 {
00714     AclItem    *a = PG_GETARG_ACLITEM_P(0);
00715 
00716     /* not very bright, but avoids any issue of padding in struct */
00717     PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
00718 }
00719 
00720 
00721 /*
00722  * acldefault()  --- create an ACL describing default access permissions
00723  *
00724  * Change this routine if you want to alter the default access policy for
00725  * newly-created objects (or any object with a NULL acl entry).
00726  *
00727  * Note that these are the hard-wired "defaults" that are used in the
00728  * absence of any pg_default_acl entry.
00729  */
00730 Acl *
00731 acldefault(GrantObjectType objtype, Oid ownerId)
00732 {
00733     AclMode     world_default;
00734     AclMode     owner_default;
00735     int         nacl;
00736     Acl        *acl;
00737     AclItem    *aip;
00738 
00739     switch (objtype)
00740     {
00741         case ACL_OBJECT_COLUMN:
00742             /* by default, columns have no extra privileges */
00743             world_default = ACL_NO_RIGHTS;
00744             owner_default = ACL_NO_RIGHTS;
00745             break;
00746         case ACL_OBJECT_RELATION:
00747             world_default = ACL_NO_RIGHTS;
00748             owner_default = ACL_ALL_RIGHTS_RELATION;
00749             break;
00750         case ACL_OBJECT_SEQUENCE:
00751             world_default = ACL_NO_RIGHTS;
00752             owner_default = ACL_ALL_RIGHTS_SEQUENCE;
00753             break;
00754         case ACL_OBJECT_DATABASE:
00755             /* for backwards compatibility, grant some rights by default */
00756             world_default = ACL_CREATE_TEMP | ACL_CONNECT;
00757             owner_default = ACL_ALL_RIGHTS_DATABASE;
00758             break;
00759         case ACL_OBJECT_FUNCTION:
00760             /* Grant EXECUTE by default, for now */
00761             world_default = ACL_EXECUTE;
00762             owner_default = ACL_ALL_RIGHTS_FUNCTION;
00763             break;
00764         case ACL_OBJECT_LANGUAGE:
00765             /* Grant USAGE by default, for now */
00766             world_default = ACL_USAGE;
00767             owner_default = ACL_ALL_RIGHTS_LANGUAGE;
00768             break;
00769         case ACL_OBJECT_LARGEOBJECT:
00770             world_default = ACL_NO_RIGHTS;
00771             owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
00772             break;
00773         case ACL_OBJECT_NAMESPACE:
00774             world_default = ACL_NO_RIGHTS;
00775             owner_default = ACL_ALL_RIGHTS_NAMESPACE;
00776             break;
00777         case ACL_OBJECT_TABLESPACE:
00778             world_default = ACL_NO_RIGHTS;
00779             owner_default = ACL_ALL_RIGHTS_TABLESPACE;
00780             break;
00781         case ACL_OBJECT_FDW:
00782             world_default = ACL_NO_RIGHTS;
00783             owner_default = ACL_ALL_RIGHTS_FDW;
00784             break;
00785         case ACL_OBJECT_FOREIGN_SERVER:
00786             world_default = ACL_NO_RIGHTS;
00787             owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
00788             break;
00789         case ACL_OBJECT_DOMAIN:
00790         case ACL_OBJECT_TYPE:
00791             world_default = ACL_USAGE;
00792             owner_default = ACL_ALL_RIGHTS_TYPE;
00793             break;
00794         default:
00795             elog(ERROR, "unrecognized objtype: %d", (int) objtype);
00796             world_default = ACL_NO_RIGHTS;      /* keep compiler quiet */
00797             owner_default = ACL_NO_RIGHTS;
00798             break;
00799     }
00800 
00801     nacl = 0;
00802     if (world_default != ACL_NO_RIGHTS)
00803         nacl++;
00804     if (owner_default != ACL_NO_RIGHTS)
00805         nacl++;
00806 
00807     acl = allocacl(nacl);
00808     aip = ACL_DAT(acl);
00809 
00810     if (world_default != ACL_NO_RIGHTS)
00811     {
00812         aip->ai_grantee = ACL_ID_PUBLIC;
00813         aip->ai_grantor = ownerId;
00814         ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
00815         aip++;
00816     }
00817 
00818     /*
00819      * Note that the owner's entry shows all ordinary privileges but no grant
00820      * options.  This is because his grant options come "from the system" and
00821      * not from his own efforts.  (The SQL spec says that the owner's rights
00822      * come from a "_SYSTEM" authid.)  However, we do consider that the
00823      * owner's ordinary privileges are self-granted; this lets him revoke
00824      * them.  We implement the owner's grant options without any explicit
00825      * "_SYSTEM"-like ACL entry, by internally special-casing the owner
00826      * wherever we are testing grant options.
00827      */
00828     if (owner_default != ACL_NO_RIGHTS)
00829     {
00830         aip->ai_grantee = ownerId;
00831         aip->ai_grantor = ownerId;
00832         ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
00833     }
00834 
00835     return acl;
00836 }
00837 
00838 
00839 /*
00840  * SQL-accessible version of acldefault().  Hackish mapping from "char" type to
00841  * ACL_OBJECT_* values, but it's only used in the information schema, not
00842  * documented for general use.
00843  */
00844 Datum
00845 acldefault_sql(PG_FUNCTION_ARGS)
00846 {
00847     char        objtypec = PG_GETARG_CHAR(0);
00848     Oid         owner = PG_GETARG_OID(1);
00849     GrantObjectType objtype = 0;
00850 
00851     switch (objtypec)
00852     {
00853         case 'c':
00854             objtype = ACL_OBJECT_COLUMN;
00855             break;
00856         case 'r':
00857             objtype = ACL_OBJECT_RELATION;
00858             break;
00859         case 's':
00860             objtype = ACL_OBJECT_SEQUENCE;
00861             break;
00862         case 'd':
00863             objtype = ACL_OBJECT_DATABASE;
00864             break;
00865         case 'f':
00866             objtype = ACL_OBJECT_FUNCTION;
00867             break;
00868         case 'l':
00869             objtype = ACL_OBJECT_LANGUAGE;
00870             break;
00871         case 'L':
00872             objtype = ACL_OBJECT_LARGEOBJECT;
00873             break;
00874         case 'n':
00875             objtype = ACL_OBJECT_NAMESPACE;
00876             break;
00877         case 't':
00878             objtype = ACL_OBJECT_TABLESPACE;
00879             break;
00880         case 'F':
00881             objtype = ACL_OBJECT_FDW;
00882             break;
00883         case 'S':
00884             objtype = ACL_OBJECT_FOREIGN_SERVER;
00885             break;
00886         case 'T':
00887             objtype = ACL_OBJECT_TYPE;
00888             break;
00889         default:
00890             elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
00891     }
00892 
00893     PG_RETURN_ACL_P(acldefault(objtype, owner));
00894 }
00895 
00896 
00897 /*
00898  * Update an ACL array to add or remove specified privileges.
00899  *
00900  *  old_acl: the input ACL array
00901  *  mod_aip: defines the privileges to be added, removed, or substituted
00902  *  modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
00903  *  ownerId: Oid of object owner
00904  *  behavior: RESTRICT or CASCADE behavior for recursive removal
00905  *
00906  * ownerid and behavior are only relevant when the update operation specifies
00907  * deletion of grant options.
00908  *
00909  * The result is a modified copy; the input object is not changed.
00910  *
00911  * NB: caller is responsible for having detoasted the input ACL, if needed.
00912  */
00913 Acl *
00914 aclupdate(const Acl *old_acl, const AclItem *mod_aip,
00915           int modechg, Oid ownerId, DropBehavior behavior)
00916 {
00917     Acl        *new_acl = NULL;
00918     AclItem    *old_aip,
00919                *new_aip = NULL;
00920     AclMode     old_rights,
00921                 old_goptions,
00922                 new_rights,
00923                 new_goptions;
00924     int         dst,
00925                 num;
00926 
00927     /* Caller probably already checked old_acl, but be safe */
00928     check_acl(old_acl);
00929 
00930     /* If granting grant options, check for circularity */
00931     if (modechg != ACL_MODECHG_DEL &&
00932         ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
00933         check_circularity(old_acl, mod_aip, ownerId);
00934 
00935     num = ACL_NUM(old_acl);
00936     old_aip = ACL_DAT(old_acl);
00937 
00938     /*
00939      * Search the ACL for an existing entry for this grantee and grantor. If
00940      * one exists, just modify the entry in-place (well, in the same position,
00941      * since we actually return a copy); otherwise, insert the new entry at
00942      * the end.
00943      */
00944 
00945     for (dst = 0; dst < num; ++dst)
00946     {
00947         if (aclitem_match(mod_aip, old_aip + dst))
00948         {
00949             /* found a match, so modify existing item */
00950             new_acl = allocacl(num);
00951             new_aip = ACL_DAT(new_acl);
00952             memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
00953             break;
00954         }
00955     }
00956 
00957     if (dst == num)
00958     {
00959         /* need to append a new item */
00960         new_acl = allocacl(num + 1);
00961         new_aip = ACL_DAT(new_acl);
00962         memcpy(new_aip, old_aip, num * sizeof(AclItem));
00963 
00964         /* initialize the new entry with no permissions */
00965         new_aip[dst].ai_grantee = mod_aip->ai_grantee;
00966         new_aip[dst].ai_grantor = mod_aip->ai_grantor;
00967         ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
00968                                    ACL_NO_RIGHTS, ACL_NO_RIGHTS);
00969         num++;                  /* set num to the size of new_acl */
00970     }
00971 
00972     old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
00973     old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
00974 
00975     /* apply the specified permissions change */
00976     switch (modechg)
00977     {
00978         case ACL_MODECHG_ADD:
00979             ACLITEM_SET_RIGHTS(new_aip[dst],
00980                                old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
00981             break;
00982         case ACL_MODECHG_DEL:
00983             ACLITEM_SET_RIGHTS(new_aip[dst],
00984                                old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
00985             break;
00986         case ACL_MODECHG_EQL:
00987             ACLITEM_SET_RIGHTS(new_aip[dst],
00988                                ACLITEM_GET_RIGHTS(*mod_aip));
00989             break;
00990     }
00991 
00992     new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
00993     new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
00994 
00995     /*
00996      * If the adjusted entry has no permissions, delete it from the list.
00997      */
00998     if (new_rights == ACL_NO_RIGHTS)
00999     {
01000         memmove(new_aip + dst,
01001                 new_aip + dst + 1,
01002                 (num - dst - 1) * sizeof(AclItem));
01003         /* Adjust array size to be 'num - 1' items */
01004         ARR_DIMS(new_acl)[0] = num - 1;
01005         SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
01006     }
01007 
01008     /*
01009      * Remove abandoned privileges (cascading revoke).  Currently we can only
01010      * handle this when the grantee is not PUBLIC.
01011      */
01012     if ((old_goptions & ~new_goptions) != 0)
01013     {
01014         Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
01015         new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
01016                                    (old_goptions & ~new_goptions),
01017                                    ownerId, behavior);
01018     }
01019 
01020     return new_acl;
01021 }
01022 
01023 /*
01024  * Update an ACL array to reflect a change of owner to the parent object
01025  *
01026  *  old_acl: the input ACL array (must not be NULL)
01027  *  oldOwnerId: Oid of the old object owner
01028  *  newOwnerId: Oid of the new object owner
01029  *
01030  * The result is a modified copy; the input object is not changed.
01031  *
01032  * NB: caller is responsible for having detoasted the input ACL, if needed.
01033  */
01034 Acl *
01035 aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
01036 {
01037     Acl        *new_acl;
01038     AclItem    *new_aip;
01039     AclItem    *old_aip;
01040     AclItem    *dst_aip;
01041     AclItem    *src_aip;
01042     AclItem    *targ_aip;
01043     bool        newpresent = false;
01044     int         dst,
01045                 src,
01046                 targ,
01047                 num;
01048 
01049     check_acl(old_acl);
01050 
01051     /*
01052      * Make a copy of the given ACL, substituting new owner ID for old
01053      * wherever it appears as either grantor or grantee.  Also note if the new
01054      * owner ID is already present.
01055      */
01056     num = ACL_NUM(old_acl);
01057     old_aip = ACL_DAT(old_acl);
01058     new_acl = allocacl(num);
01059     new_aip = ACL_DAT(new_acl);
01060     memcpy(new_aip, old_aip, num * sizeof(AclItem));
01061     for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
01062     {
01063         if (dst_aip->ai_grantor == oldOwnerId)
01064             dst_aip->ai_grantor = newOwnerId;
01065         else if (dst_aip->ai_grantor == newOwnerId)
01066             newpresent = true;
01067         if (dst_aip->ai_grantee == oldOwnerId)
01068             dst_aip->ai_grantee = newOwnerId;
01069         else if (dst_aip->ai_grantee == newOwnerId)
01070             newpresent = true;
01071     }
01072 
01073     /*
01074      * If the old ACL contained any references to the new owner, then we may
01075      * now have generated an ACL containing duplicate entries.  Find them and
01076      * merge them so that there are not duplicates.  (This is relatively
01077      * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to
01078      * be the normal case.)
01079      *
01080      * To simplify deletion of duplicate entries, we temporarily leave them in
01081      * the array but set their privilege masks to zero; when we reach such an
01082      * entry it's just skipped.  (Thus, a side effect of this code will be to
01083      * remove privilege-free entries, should there be any in the input.)  dst
01084      * is the next output slot, targ is the currently considered input slot
01085      * (always >= dst), and src scans entries to the right of targ looking for
01086      * duplicates.  Once an entry has been emitted to dst it is known
01087      * duplicate-free and need not be considered anymore.
01088      */
01089     if (newpresent)
01090     {
01091         dst = 0;
01092         for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
01093         {
01094             /* ignore if deleted in an earlier pass */
01095             if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
01096                 continue;
01097             /* find and merge any duplicates */
01098             for (src = targ + 1, src_aip = targ_aip + 1; src < num;
01099                  src++, src_aip++)
01100             {
01101                 if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
01102                     continue;
01103                 if (aclitem_match(targ_aip, src_aip))
01104                 {
01105                     ACLITEM_SET_RIGHTS(*targ_aip,
01106                                        ACLITEM_GET_RIGHTS(*targ_aip) |
01107                                        ACLITEM_GET_RIGHTS(*src_aip));
01108                     /* mark the duplicate deleted */
01109                     ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
01110                 }
01111             }
01112             /* and emit to output */
01113             new_aip[dst] = *targ_aip;
01114             dst++;
01115         }
01116         /* Adjust array size to be 'dst' items */
01117         ARR_DIMS(new_acl)[0] = dst;
01118         SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
01119     }
01120 
01121     return new_acl;
01122 }
01123 
01124 
01125 /*
01126  * When granting grant options, we must disallow attempts to set up circular
01127  * chains of grant options.  Suppose A (the object owner) grants B some
01128  * privileges with grant option, and B re-grants them to C.  If C could
01129  * grant the privileges to B as well, then A would be unable to effectively
01130  * revoke the privileges from B, since recursive_revoke would consider that
01131  * B still has 'em from C.
01132  *
01133  * We check for this by recursively deleting all grant options belonging to
01134  * the target grantee, and then seeing if the would-be grantor still has the
01135  * grant option or not.
01136  */
01137 static void
01138 check_circularity(const Acl *old_acl, const AclItem *mod_aip,
01139                   Oid ownerId)
01140 {
01141     Acl        *acl;
01142     AclItem    *aip;
01143     int         i,
01144                 num;
01145     AclMode     own_privs;
01146 
01147     check_acl(old_acl);
01148 
01149     /*
01150      * For now, grant options can only be granted to roles, not PUBLIC.
01151      * Otherwise we'd have to work a bit harder here.
01152      */
01153     Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
01154 
01155     /* The owner always has grant options, no need to check */
01156     if (mod_aip->ai_grantor == ownerId)
01157         return;
01158 
01159     /* Make a working copy */
01160     acl = allocacl(ACL_NUM(old_acl));
01161     memcpy(acl, old_acl, ACL_SIZE(old_acl));
01162 
01163     /* Zap all grant options of target grantee, plus what depends on 'em */
01164 cc_restart:
01165     num = ACL_NUM(acl);
01166     aip = ACL_DAT(acl);
01167     for (i = 0; i < num; i++)
01168     {
01169         if (aip[i].ai_grantee == mod_aip->ai_grantee &&
01170             ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
01171         {
01172             Acl        *new_acl;
01173 
01174             /* We'll actually zap ordinary privs too, but no matter */
01175             new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
01176                                 ownerId, DROP_CASCADE);
01177 
01178             pfree(acl);
01179             acl = new_acl;
01180 
01181             goto cc_restart;
01182         }
01183     }
01184 
01185     /* Now we can compute grantor's independently-derived privileges */
01186     own_privs = aclmask(acl,
01187                         mod_aip->ai_grantor,
01188                         ownerId,
01189                         ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
01190                         ACLMASK_ALL);
01191     own_privs = ACL_OPTION_TO_PRIVS(own_privs);
01192 
01193     if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
01194         ereport(ERROR,
01195                 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
01196         errmsg("grant options cannot be granted back to your own grantor")));
01197 
01198     pfree(acl);
01199 }
01200 
01201 
01202 /*
01203  * Ensure that no privilege is "abandoned".  A privilege is abandoned
01204  * if the user that granted the privilege loses the grant option.  (So
01205  * the chain through which it was granted is broken.)  Either the
01206  * abandoned privileges are revoked as well, or an error message is
01207  * printed, depending on the drop behavior option.
01208  *
01209  *  acl: the input ACL list
01210  *  grantee: the user from whom some grant options have been revoked
01211  *  revoke_privs: the grant options being revoked
01212  *  ownerId: Oid of object owner
01213  *  behavior: RESTRICT or CASCADE behavior for recursive removal
01214  *
01215  * The input Acl object is pfree'd if replaced.
01216  */
01217 static Acl *
01218 recursive_revoke(Acl *acl,
01219                  Oid grantee,
01220                  AclMode revoke_privs,
01221                  Oid ownerId,
01222                  DropBehavior behavior)
01223 {
01224     AclMode     still_has;
01225     AclItem    *aip;
01226     int         i,
01227                 num;
01228 
01229     check_acl(acl);
01230 
01231     /* The owner can never truly lose grant options, so short-circuit */
01232     if (grantee == ownerId)
01233         return acl;
01234 
01235     /* The grantee might still have some grant options via another grantor */
01236     still_has = aclmask(acl, grantee, ownerId,
01237                         ACL_GRANT_OPTION_FOR(revoke_privs),
01238                         ACLMASK_ALL);
01239     revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
01240     if (revoke_privs == ACL_NO_RIGHTS)
01241         return acl;
01242 
01243 restart:
01244     num = ACL_NUM(acl);
01245     aip = ACL_DAT(acl);
01246     for (i = 0; i < num; i++)
01247     {
01248         if (aip[i].ai_grantor == grantee
01249             && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
01250         {
01251             AclItem     mod_acl;
01252             Acl        *new_acl;
01253 
01254             if (behavior == DROP_RESTRICT)
01255                 ereport(ERROR,
01256                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
01257                          errmsg("dependent privileges exist"),
01258                          errhint("Use CASCADE to revoke them too.")));
01259 
01260             mod_acl.ai_grantor = grantee;
01261             mod_acl.ai_grantee = aip[i].ai_grantee;
01262             ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
01263                                        revoke_privs,
01264                                        revoke_privs);
01265 
01266             new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
01267                                 ownerId, behavior);
01268 
01269             pfree(acl);
01270             acl = new_acl;
01271 
01272             goto restart;
01273         }
01274     }
01275 
01276     return acl;
01277 }
01278 
01279 
01280 /*
01281  * aclmask --- compute bitmask of all privileges held by roleid.
01282  *
01283  * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
01284  * held by the given roleid according to the given ACL list, ANDed
01285  * with 'mask'.  (The point of passing 'mask' is to let the routine
01286  * exit early if all privileges of interest have been found.)
01287  *
01288  * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
01289  * is known true.  (This lets us exit soonest in cases where the
01290  * caller is only going to test for zero or nonzero result.)
01291  *
01292  * Usage patterns:
01293  *
01294  * To see if any of a set of privileges are held:
01295  *      if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
01296  *
01297  * To see if all of a set of privileges are held:
01298  *      if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
01299  *
01300  * To determine exactly which of a set of privileges are held:
01301  *      heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
01302  */
01303 AclMode
01304 aclmask(const Acl *acl, Oid roleid, Oid ownerId,
01305         AclMode mask, AclMaskHow how)
01306 {
01307     AclMode     result;
01308     AclMode     remaining;
01309     AclItem    *aidat;
01310     int         i,
01311                 num;
01312 
01313     /*
01314      * Null ACL should not happen, since caller should have inserted
01315      * appropriate default
01316      */
01317     if (acl == NULL)
01318         elog(ERROR, "null ACL");
01319 
01320     check_acl(acl);
01321 
01322     /* Quick exit for mask == 0 */
01323     if (mask == 0)
01324         return 0;
01325 
01326     result = 0;
01327 
01328     /* Owner always implicitly has all grant options */
01329     if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
01330         has_privs_of_role(roleid, ownerId))
01331     {
01332         result = mask & ACLITEM_ALL_GOPTION_BITS;
01333         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
01334             return result;
01335     }
01336 
01337     num = ACL_NUM(acl);
01338     aidat = ACL_DAT(acl);
01339 
01340     /*
01341      * Check privileges granted directly to roleid or to public
01342      */
01343     for (i = 0; i < num; i++)
01344     {
01345         AclItem    *aidata = &aidat[i];
01346 
01347         if (aidata->ai_grantee == ACL_ID_PUBLIC ||
01348             aidata->ai_grantee == roleid)
01349         {
01350             result |= aidata->ai_privs & mask;
01351             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
01352                 return result;
01353         }
01354     }
01355 
01356     /*
01357      * Check privileges granted indirectly via role memberships. We do this in
01358      * a separate pass to minimize expensive indirect membership tests.  In
01359      * particular, it's worth testing whether a given ACL entry grants any
01360      * privileges still of interest before we perform the has_privs_of_role
01361      * test.
01362      */
01363     remaining = mask & ~result;
01364     for (i = 0; i < num; i++)
01365     {
01366         AclItem    *aidata = &aidat[i];
01367 
01368         if (aidata->ai_grantee == ACL_ID_PUBLIC ||
01369             aidata->ai_grantee == roleid)
01370             continue;           /* already checked it */
01371 
01372         if ((aidata->ai_privs & remaining) &&
01373             has_privs_of_role(roleid, aidata->ai_grantee))
01374         {
01375             result |= aidata->ai_privs & mask;
01376             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
01377                 return result;
01378             remaining = mask & ~result;
01379         }
01380     }
01381 
01382     return result;
01383 }
01384 
01385 
01386 /*
01387  * aclmask_direct --- compute bitmask of all privileges held by roleid.
01388  *
01389  * This is exactly like aclmask() except that we consider only privileges
01390  * held *directly* by roleid, not those inherited via role membership.
01391  */
01392 static AclMode
01393 aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId,
01394                AclMode mask, AclMaskHow how)
01395 {
01396     AclMode     result;
01397     AclItem    *aidat;
01398     int         i,
01399                 num;
01400 
01401     /*
01402      * Null ACL should not happen, since caller should have inserted
01403      * appropriate default
01404      */
01405     if (acl == NULL)
01406         elog(ERROR, "null ACL");
01407 
01408     check_acl(acl);
01409 
01410     /* Quick exit for mask == 0 */
01411     if (mask == 0)
01412         return 0;
01413 
01414     result = 0;
01415 
01416     /* Owner always implicitly has all grant options */
01417     if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
01418         roleid == ownerId)
01419     {
01420         result = mask & ACLITEM_ALL_GOPTION_BITS;
01421         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
01422             return result;
01423     }
01424 
01425     num = ACL_NUM(acl);
01426     aidat = ACL_DAT(acl);
01427 
01428     /*
01429      * Check privileges granted directly to roleid (and not to public)
01430      */
01431     for (i = 0; i < num; i++)
01432     {
01433         AclItem    *aidata = &aidat[i];
01434 
01435         if (aidata->ai_grantee == roleid)
01436         {
01437             result |= aidata->ai_privs & mask;
01438             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
01439                 return result;
01440         }
01441     }
01442 
01443     return result;
01444 }
01445 
01446 
01447 /*
01448  * aclmembers
01449  *      Find out all the roleids mentioned in an Acl.
01450  *      Note that we do not distinguish grantors from grantees.
01451  *
01452  * *roleids is set to point to a palloc'd array containing distinct OIDs
01453  * in sorted order.  The length of the array is the function result.
01454  */
01455 int
01456 aclmembers(const Acl *acl, Oid **roleids)
01457 {
01458     Oid        *list;
01459     const AclItem *acldat;
01460     int         i,
01461                 j,
01462                 k;
01463 
01464     if (acl == NULL || ACL_NUM(acl) == 0)
01465     {
01466         *roleids = NULL;
01467         return 0;
01468     }
01469 
01470     check_acl(acl);
01471 
01472     /* Allocate the worst-case space requirement */
01473     list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
01474     acldat = ACL_DAT(acl);
01475 
01476     /*
01477      * Walk the ACL collecting mentioned RoleIds.
01478      */
01479     j = 0;
01480     for (i = 0; i < ACL_NUM(acl); i++)
01481     {
01482         const AclItem *ai = &acldat[i];
01483 
01484         if (ai->ai_grantee != ACL_ID_PUBLIC)
01485             list[j++] = ai->ai_grantee;
01486         /* grantor is currently never PUBLIC, but let's check anyway */
01487         if (ai->ai_grantor != ACL_ID_PUBLIC)
01488             list[j++] = ai->ai_grantor;
01489     }
01490 
01491     /* Sort the array */
01492     qsort(list, j, sizeof(Oid), oidComparator);
01493 
01494     /* Remove duplicates from the array */
01495     k = 0;
01496     for (i = 1; i < j; i++)
01497     {
01498         if (list[k] != list[i])
01499             list[++k] = list[i];
01500     }
01501 
01502     /*
01503      * We could repalloc the array down to minimum size, but it's hardly worth
01504      * it since it's only transient memory.
01505      */
01506     *roleids = list;
01507 
01508     return k + 1;
01509 }
01510 
01511 /*
01512  * oidComparator
01513  *      qsort comparison function for Oids
01514  */
01515 static int
01516 oidComparator(const void *arg1, const void *arg2)
01517 {
01518     Oid         oid1 = *(const Oid *) arg1;
01519     Oid         oid2 = *(const Oid *) arg2;
01520 
01521     if (oid1 > oid2)
01522         return 1;
01523     if (oid1 < oid2)
01524         return -1;
01525     return 0;
01526 }
01527 
01528 
01529 /*
01530  * aclinsert (exported function)
01531  */
01532 Datum
01533 aclinsert(PG_FUNCTION_ARGS)
01534 {
01535     ereport(ERROR,
01536             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01537              errmsg("aclinsert is no longer supported")));
01538 
01539     PG_RETURN_NULL();           /* keep compiler quiet */
01540 }
01541 
01542 Datum
01543 aclremove(PG_FUNCTION_ARGS)
01544 {
01545     ereport(ERROR,
01546             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01547              errmsg("aclremove is no longer supported")));
01548 
01549     PG_RETURN_NULL();           /* keep compiler quiet */
01550 }
01551 
01552 Datum
01553 aclcontains(PG_FUNCTION_ARGS)
01554 {
01555     Acl        *acl = PG_GETARG_ACL_P(0);
01556     AclItem    *aip = PG_GETARG_ACLITEM_P(1);
01557     AclItem    *aidat;
01558     int         i,
01559                 num;
01560 
01561     check_acl(acl);
01562     num = ACL_NUM(acl);
01563     aidat = ACL_DAT(acl);
01564     for (i = 0; i < num; ++i)
01565     {
01566         if (aip->ai_grantee == aidat[i].ai_grantee &&
01567             aip->ai_grantor == aidat[i].ai_grantor &&
01568             (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
01569             PG_RETURN_BOOL(true);
01570     }
01571     PG_RETURN_BOOL(false);
01572 }
01573 
01574 Datum
01575 makeaclitem(PG_FUNCTION_ARGS)
01576 {
01577     Oid         grantee = PG_GETARG_OID(0);
01578     Oid         grantor = PG_GETARG_OID(1);
01579     text       *privtext = PG_GETARG_TEXT_P(2);
01580     bool        goption = PG_GETARG_BOOL(3);
01581     AclItem    *result;
01582     AclMode     priv;
01583 
01584     priv = convert_priv_string(privtext);
01585 
01586     result = (AclItem *) palloc(sizeof(AclItem));
01587 
01588     result->ai_grantee = grantee;
01589     result->ai_grantor = grantor;
01590 
01591     ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
01592                                (goption ? priv : ACL_NO_RIGHTS));
01593 
01594     PG_RETURN_ACLITEM_P(result);
01595 }
01596 
01597 static AclMode
01598 convert_priv_string(text *priv_type_text)
01599 {
01600     char       *priv_type = text_to_cstring(priv_type_text);
01601 
01602     if (pg_strcasecmp(priv_type, "SELECT") == 0)
01603         return ACL_SELECT;
01604     if (pg_strcasecmp(priv_type, "INSERT") == 0)
01605         return ACL_INSERT;
01606     if (pg_strcasecmp(priv_type, "UPDATE") == 0)
01607         return ACL_UPDATE;
01608     if (pg_strcasecmp(priv_type, "DELETE") == 0)
01609         return ACL_DELETE;
01610     if (pg_strcasecmp(priv_type, "TRUNCATE") == 0)
01611         return ACL_TRUNCATE;
01612     if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
01613         return ACL_REFERENCES;
01614     if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
01615         return ACL_TRIGGER;
01616     if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
01617         return ACL_EXECUTE;
01618     if (pg_strcasecmp(priv_type, "USAGE") == 0)
01619         return ACL_USAGE;
01620     if (pg_strcasecmp(priv_type, "CREATE") == 0)
01621         return ACL_CREATE;
01622     if (pg_strcasecmp(priv_type, "TEMP") == 0)
01623         return ACL_CREATE_TEMP;
01624     if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
01625         return ACL_CREATE_TEMP;
01626     if (pg_strcasecmp(priv_type, "CONNECT") == 0)
01627         return ACL_CONNECT;
01628     if (pg_strcasecmp(priv_type, "RULE") == 0)
01629         return 0;               /* ignore old RULE privileges */
01630 
01631     ereport(ERROR,
01632             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01633              errmsg("unrecognized privilege type: \"%s\"", priv_type)));
01634     return ACL_NO_RIGHTS;       /* keep compiler quiet */
01635 }
01636 
01637 
01638 /*
01639  * convert_any_priv_string: recognize privilege strings for has_foo_privilege
01640  *
01641  * We accept a comma-separated list of case-insensitive privilege names,
01642  * producing a bitmask of the OR'd privilege bits.  We are liberal about
01643  * whitespace between items, not so much about whitespace within items.
01644  * The allowed privilege names are given as an array of priv_map structs,
01645  * terminated by one with a NULL name pointer.
01646  */
01647 static AclMode
01648 convert_any_priv_string(text *priv_type_text,
01649                         const priv_map *privileges)
01650 {
01651     AclMode     result = 0;
01652     char       *priv_type = text_to_cstring(priv_type_text);
01653     char       *chunk;
01654     char       *next_chunk;
01655 
01656     /* We rely on priv_type being a private, modifiable string */
01657     for (chunk = priv_type; chunk; chunk = next_chunk)
01658     {
01659         int         chunk_len;
01660         const priv_map *this_priv;
01661 
01662         /* Split string at commas */
01663         next_chunk = strchr(chunk, ',');
01664         if (next_chunk)
01665             *next_chunk++ = '\0';
01666 
01667         /* Drop leading/trailing whitespace in this chunk */
01668         while (*chunk && isspace((unsigned char) *chunk))
01669             chunk++;
01670         chunk_len = strlen(chunk);
01671         while (chunk_len > 0 && isspace((unsigned char) chunk[chunk_len - 1]))
01672             chunk_len--;
01673         chunk[chunk_len] = '\0';
01674 
01675         /* Match to the privileges list */
01676         for (this_priv = privileges; this_priv->name; this_priv++)
01677         {
01678             if (pg_strcasecmp(this_priv->name, chunk) == 0)
01679             {
01680                 result |= this_priv->value;
01681                 break;
01682             }
01683         }
01684         if (!this_priv->name)
01685             ereport(ERROR,
01686                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01687                      errmsg("unrecognized privilege type: \"%s\"", chunk)));
01688     }
01689 
01690     pfree(priv_type);
01691     return result;
01692 }
01693 
01694 
01695 static const char *
01696 convert_aclright_to_string(int aclright)
01697 {
01698     switch (aclright)
01699     {
01700         case ACL_INSERT:
01701             return "INSERT";
01702         case ACL_SELECT:
01703             return "SELECT";
01704         case ACL_UPDATE:
01705             return "UPDATE";
01706         case ACL_DELETE:
01707             return "DELETE";
01708         case ACL_TRUNCATE:
01709             return "TRUNCATE";
01710         case ACL_REFERENCES:
01711             return "REFERENCES";
01712         case ACL_TRIGGER:
01713             return "TRIGGER";
01714         case ACL_EXECUTE:
01715             return "EXECUTE";
01716         case ACL_USAGE:
01717             return "USAGE";
01718         case ACL_CREATE:
01719             return "CREATE";
01720         case ACL_CREATE_TEMP:
01721             return "TEMPORARY";
01722         case ACL_CONNECT:
01723             return "CONNECT";
01724         default:
01725             elog(ERROR, "unrecognized aclright: %d", aclright);
01726             return NULL;
01727     }
01728 }
01729 
01730 
01731 /*----------
01732  * Convert an aclitem[] to a table.
01733  *
01734  * Example:
01735  *
01736  * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[])
01737  *
01738  * returns the table
01739  *
01740  * {{ OID(joe), 0::OID,   'SELECT', false },
01741  *  { OID(joe), OID(foo), 'INSERT', true },
01742  *  { OID(joe), OID(foo), 'UPDATE', false }}
01743  *----------
01744  */
01745 Datum
01746 aclexplode(PG_FUNCTION_ARGS)
01747 {
01748     Acl        *acl = PG_GETARG_ACL_P(0);
01749     FuncCallContext *funcctx;
01750     int        *idx;
01751     AclItem    *aidat;
01752 
01753     if (SRF_IS_FIRSTCALL())
01754     {
01755         TupleDesc   tupdesc;
01756         MemoryContext oldcontext;
01757 
01758         check_acl(acl);
01759 
01760         funcctx = SRF_FIRSTCALL_INIT();
01761         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
01762 
01763         /*
01764          * build tupdesc for result tuples (matches out parameters in pg_proc
01765          * entry)
01766          */
01767         tupdesc = CreateTemplateTupleDesc(4, false);
01768         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor",
01769                            OIDOID, -1, 0);
01770         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee",
01771                            OIDOID, -1, 0);
01772         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type",
01773                            TEXTOID, -1, 0);
01774         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable",
01775                            BOOLOID, -1, 0);
01776 
01777         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
01778 
01779         /* allocate memory for user context */
01780         idx = (int *) palloc(sizeof(int[2]));
01781         idx[0] = 0;             /* ACL array item index */
01782         idx[1] = -1;            /* privilege type counter */
01783         funcctx->user_fctx = (void *) idx;
01784 
01785         MemoryContextSwitchTo(oldcontext);
01786     }
01787 
01788     funcctx = SRF_PERCALL_SETUP();
01789     idx = (int *) funcctx->user_fctx;
01790     aidat = ACL_DAT(acl);
01791 
01792     /* need test here in case acl has no items */
01793     while (idx[0] < ACL_NUM(acl))
01794     {
01795         AclItem    *aidata;
01796         AclMode     priv_bit;
01797 
01798         idx[1]++;
01799         if (idx[1] == N_ACL_RIGHTS)
01800         {
01801             idx[1] = 0;
01802             idx[0]++;
01803             if (idx[0] >= ACL_NUM(acl)) /* done */
01804                 break;
01805         }
01806         aidata = &aidat[idx[0]];
01807         priv_bit = 1 << idx[1];
01808 
01809         if (ACLITEM_GET_PRIVS(*aidata) & priv_bit)
01810         {
01811             Datum       result;
01812             Datum       values[4];
01813             bool        nulls[4];
01814             HeapTuple   tuple;
01815 
01816             values[0] = ObjectIdGetDatum(aidata->ai_grantor);
01817             values[1] = ObjectIdGetDatum(aidata->ai_grantee);
01818             values[2] = CStringGetTextDatum(convert_aclright_to_string(priv_bit));
01819             values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0);
01820 
01821             MemSet(nulls, 0, sizeof(nulls));
01822 
01823             tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
01824             result = HeapTupleGetDatum(tuple);
01825 
01826             SRF_RETURN_NEXT(funcctx, result);
01827         }
01828     }
01829 
01830     SRF_RETURN_DONE(funcctx);
01831 }
01832 
01833 
01834 /*
01835  * has_table_privilege variants
01836  *      These are all named "has_table_privilege" at the SQL level.
01837  *      They take various combinations of relation name, relation OID,
01838  *      user name, user OID, or implicit user = current_user.
01839  *
01840  *      The result is a boolean value: true if user has the indicated
01841  *      privilege, false if not.  The variants that take a relation OID
01842  *      return NULL if the OID doesn't exist (rather than failing, as
01843  *      they did before Postgres 8.4).
01844  */
01845 
01846 /*
01847  * has_table_privilege_name_name
01848  *      Check user privileges on a table given
01849  *      name username, text tablename, and text priv name.
01850  */
01851 Datum
01852 has_table_privilege_name_name(PG_FUNCTION_ARGS)
01853 {
01854     Name        rolename = PG_GETARG_NAME(0);
01855     text       *tablename = PG_GETARG_TEXT_P(1);
01856     text       *priv_type_text = PG_GETARG_TEXT_P(2);
01857     Oid         roleid;
01858     Oid         tableoid;
01859     AclMode     mode;
01860     AclResult   aclresult;
01861 
01862     roleid = get_role_oid_or_public(NameStr(*rolename));
01863     tableoid = convert_table_name(tablename);
01864     mode = convert_table_priv_string(priv_type_text);
01865 
01866     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01867 
01868     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01869 }
01870 
01871 /*
01872  * has_table_privilege_name
01873  *      Check user privileges on a table given
01874  *      text tablename and text priv name.
01875  *      current_user is assumed
01876  */
01877 Datum
01878 has_table_privilege_name(PG_FUNCTION_ARGS)
01879 {
01880     text       *tablename = PG_GETARG_TEXT_P(0);
01881     text       *priv_type_text = PG_GETARG_TEXT_P(1);
01882     Oid         roleid;
01883     Oid         tableoid;
01884     AclMode     mode;
01885     AclResult   aclresult;
01886 
01887     roleid = GetUserId();
01888     tableoid = convert_table_name(tablename);
01889     mode = convert_table_priv_string(priv_type_text);
01890 
01891     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01892 
01893     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01894 }
01895 
01896 /*
01897  * has_table_privilege_name_id
01898  *      Check user privileges on a table given
01899  *      name usename, table oid, and text priv name.
01900  */
01901 Datum
01902 has_table_privilege_name_id(PG_FUNCTION_ARGS)
01903 {
01904     Name        username = PG_GETARG_NAME(0);
01905     Oid         tableoid = PG_GETARG_OID(1);
01906     text       *priv_type_text = PG_GETARG_TEXT_P(2);
01907     Oid         roleid;
01908     AclMode     mode;
01909     AclResult   aclresult;
01910 
01911     roleid = get_role_oid_or_public(NameStr(*username));
01912     mode = convert_table_priv_string(priv_type_text);
01913 
01914     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
01915         PG_RETURN_NULL();
01916 
01917     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01918 
01919     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01920 }
01921 
01922 /*
01923  * has_table_privilege_id
01924  *      Check user privileges on a table given
01925  *      table oid, and text priv name.
01926  *      current_user is assumed
01927  */
01928 Datum
01929 has_table_privilege_id(PG_FUNCTION_ARGS)
01930 {
01931     Oid         tableoid = PG_GETARG_OID(0);
01932     text       *priv_type_text = PG_GETARG_TEXT_P(1);
01933     Oid         roleid;
01934     AclMode     mode;
01935     AclResult   aclresult;
01936 
01937     roleid = GetUserId();
01938     mode = convert_table_priv_string(priv_type_text);
01939 
01940     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
01941         PG_RETURN_NULL();
01942 
01943     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01944 
01945     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01946 }
01947 
01948 /*
01949  * has_table_privilege_id_name
01950  *      Check user privileges on a table given
01951  *      roleid, text tablename, and text priv name.
01952  */
01953 Datum
01954 has_table_privilege_id_name(PG_FUNCTION_ARGS)
01955 {
01956     Oid         roleid = PG_GETARG_OID(0);
01957     text       *tablename = PG_GETARG_TEXT_P(1);
01958     text       *priv_type_text = PG_GETARG_TEXT_P(2);
01959     Oid         tableoid;
01960     AclMode     mode;
01961     AclResult   aclresult;
01962 
01963     tableoid = convert_table_name(tablename);
01964     mode = convert_table_priv_string(priv_type_text);
01965 
01966     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01967 
01968     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01969 }
01970 
01971 /*
01972  * has_table_privilege_id_id
01973  *      Check user privileges on a table given
01974  *      roleid, table oid, and text priv name.
01975  */
01976 Datum
01977 has_table_privilege_id_id(PG_FUNCTION_ARGS)
01978 {
01979     Oid         roleid = PG_GETARG_OID(0);
01980     Oid         tableoid = PG_GETARG_OID(1);
01981     text       *priv_type_text = PG_GETARG_TEXT_P(2);
01982     AclMode     mode;
01983     AclResult   aclresult;
01984 
01985     mode = convert_table_priv_string(priv_type_text);
01986 
01987     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
01988         PG_RETURN_NULL();
01989 
01990     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
01991 
01992     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
01993 }
01994 
01995 /*
01996  *      Support routines for has_table_privilege family.
01997  */
01998 
01999 /*
02000  * Given a table name expressed as a string, look it up and return Oid
02001  */
02002 static Oid
02003 convert_table_name(text *tablename)
02004 {
02005     RangeVar   *relrv;
02006 
02007     relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
02008 
02009     /* We might not even have permissions on this relation; don't lock it. */
02010     return RangeVarGetRelid(relrv, NoLock, false);
02011 }
02012 
02013 /*
02014  * convert_table_priv_string
02015  *      Convert text string to AclMode value.
02016  */
02017 static AclMode
02018 convert_table_priv_string(text *priv_type_text)
02019 {
02020     static const priv_map table_priv_map[] = {
02021         {"SELECT", ACL_SELECT},
02022         {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
02023         {"INSERT", ACL_INSERT},
02024         {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
02025         {"UPDATE", ACL_UPDATE},
02026         {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
02027         {"DELETE", ACL_DELETE},
02028         {"DELETE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_DELETE)},
02029         {"TRUNCATE", ACL_TRUNCATE},
02030         {"TRUNCATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRUNCATE)},
02031         {"REFERENCES", ACL_REFERENCES},
02032         {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
02033         {"TRIGGER", ACL_TRIGGER},
02034         {"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
02035         {"RULE", 0},            /* ignore old RULE privileges */
02036         {"RULE WITH GRANT OPTION", 0},
02037         {NULL, 0}
02038     };
02039 
02040     return convert_any_priv_string(priv_type_text, table_priv_map);
02041 }
02042 
02043 /*
02044  * has_sequence_privilege variants
02045  *      These are all named "has_sequence_privilege" at the SQL level.
02046  *      They take various combinations of relation name, relation OID,
02047  *      user name, user OID, or implicit user = current_user.
02048  *
02049  *      The result is a boolean value: true if user has the indicated
02050  *      privilege, false if not.  The variants that take a relation OID
02051  *      return NULL if the OID doesn't exist.
02052  */
02053 
02054 /*
02055  * has_sequence_privilege_name_name
02056  *      Check user privileges on a sequence given
02057  *      name username, text sequencename, and text priv name.
02058  */
02059 Datum
02060 has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
02061 {
02062     Name        rolename = PG_GETARG_NAME(0);
02063     text       *sequencename = PG_GETARG_TEXT_P(1);
02064     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02065     Oid         roleid;
02066     Oid         sequenceoid;
02067     AclMode     mode;
02068     AclResult   aclresult;
02069 
02070     roleid = get_role_oid_or_public(NameStr(*rolename));
02071     mode = convert_sequence_priv_string(priv_type_text);
02072     sequenceoid = convert_table_name(sequencename);
02073     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
02074         ereport(ERROR,
02075                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02076                  errmsg("\"%s\" is not a sequence",
02077                         text_to_cstring(sequencename))));
02078 
02079     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02080 
02081     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02082 }
02083 
02084 /*
02085  * has_sequence_privilege_name
02086  *      Check user privileges on a sequence given
02087  *      text sequencename and text priv name.
02088  *      current_user is assumed
02089  */
02090 Datum
02091 has_sequence_privilege_name(PG_FUNCTION_ARGS)
02092 {
02093     text       *sequencename = PG_GETARG_TEXT_P(0);
02094     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02095     Oid         roleid;
02096     Oid         sequenceoid;
02097     AclMode     mode;
02098     AclResult   aclresult;
02099 
02100     roleid = GetUserId();
02101     mode = convert_sequence_priv_string(priv_type_text);
02102     sequenceoid = convert_table_name(sequencename);
02103     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
02104         ereport(ERROR,
02105                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02106                  errmsg("\"%s\" is not a sequence",
02107                         text_to_cstring(sequencename))));
02108 
02109     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02110 
02111     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02112 }
02113 
02114 /*
02115  * has_sequence_privilege_name_id
02116  *      Check user privileges on a sequence given
02117  *      name usename, sequence oid, and text priv name.
02118  */
02119 Datum
02120 has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
02121 {
02122     Name        username = PG_GETARG_NAME(0);
02123     Oid         sequenceoid = PG_GETARG_OID(1);
02124     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02125     Oid         roleid;
02126     AclMode     mode;
02127     AclResult   aclresult;
02128     char        relkind;
02129 
02130     roleid = get_role_oid_or_public(NameStr(*username));
02131     mode = convert_sequence_priv_string(priv_type_text);
02132     relkind = get_rel_relkind(sequenceoid);
02133     if (relkind == '\0')
02134         PG_RETURN_NULL();
02135     else if (relkind != RELKIND_SEQUENCE)
02136         ereport(ERROR,
02137                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02138                  errmsg("\"%s\" is not a sequence",
02139                         get_rel_name(sequenceoid))));
02140 
02141     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02142 
02143     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02144 }
02145 
02146 /*
02147  * has_sequence_privilege_id
02148  *      Check user privileges on a sequence given
02149  *      sequence oid, and text priv name.
02150  *      current_user is assumed
02151  */
02152 Datum
02153 has_sequence_privilege_id(PG_FUNCTION_ARGS)
02154 {
02155     Oid         sequenceoid = PG_GETARG_OID(0);
02156     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02157     Oid         roleid;
02158     AclMode     mode;
02159     AclResult   aclresult;
02160     char        relkind;
02161 
02162     roleid = GetUserId();
02163     mode = convert_sequence_priv_string(priv_type_text);
02164     relkind = get_rel_relkind(sequenceoid);
02165     if (relkind == '\0')
02166         PG_RETURN_NULL();
02167     else if (relkind != RELKIND_SEQUENCE)
02168         ereport(ERROR,
02169                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02170                  errmsg("\"%s\" is not a sequence",
02171                         get_rel_name(sequenceoid))));
02172 
02173     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02174 
02175     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02176 }
02177 
02178 /*
02179  * has_sequence_privilege_id_name
02180  *      Check user privileges on a sequence given
02181  *      roleid, text sequencename, and text priv name.
02182  */
02183 Datum
02184 has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
02185 {
02186     Oid         roleid = PG_GETARG_OID(0);
02187     text       *sequencename = PG_GETARG_TEXT_P(1);
02188     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02189     Oid         sequenceoid;
02190     AclMode     mode;
02191     AclResult   aclresult;
02192 
02193     mode = convert_sequence_priv_string(priv_type_text);
02194     sequenceoid = convert_table_name(sequencename);
02195     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
02196         ereport(ERROR,
02197                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02198                  errmsg("\"%s\" is not a sequence",
02199                         text_to_cstring(sequencename))));
02200 
02201     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02202 
02203     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02204 }
02205 
02206 /*
02207  * has_sequence_privilege_id_id
02208  *      Check user privileges on a sequence given
02209  *      roleid, sequence oid, and text priv name.
02210  */
02211 Datum
02212 has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
02213 {
02214     Oid         roleid = PG_GETARG_OID(0);
02215     Oid         sequenceoid = PG_GETARG_OID(1);
02216     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02217     AclMode     mode;
02218     AclResult   aclresult;
02219     char        relkind;
02220 
02221     mode = convert_sequence_priv_string(priv_type_text);
02222     relkind = get_rel_relkind(sequenceoid);
02223     if (relkind == '\0')
02224         PG_RETURN_NULL();
02225     else if (relkind != RELKIND_SEQUENCE)
02226         ereport(ERROR,
02227                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
02228                  errmsg("\"%s\" is not a sequence",
02229                         get_rel_name(sequenceoid))));
02230 
02231     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
02232 
02233     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02234 }
02235 
02236 /*
02237  * convert_sequence_priv_string
02238  *      Convert text string to AclMode value.
02239  */
02240 static AclMode
02241 convert_sequence_priv_string(text *priv_type_text)
02242 {
02243     static const priv_map sequence_priv_map[] = {
02244         {"USAGE", ACL_USAGE},
02245         {"SELECT", ACL_SELECT},
02246         {"UPDATE", ACL_UPDATE},
02247         {NULL, 0}
02248     };
02249 
02250     return convert_any_priv_string(priv_type_text, sequence_priv_map);
02251 }
02252 
02253 
02254 /*
02255  * has_any_column_privilege variants
02256  *      These are all named "has_any_column_privilege" at the SQL level.
02257  *      They take various combinations of relation name, relation OID,
02258  *      user name, user OID, or implicit user = current_user.
02259  *
02260  *      The result is a boolean value: true if user has the indicated
02261  *      privilege for any column of the table, false if not.  The variants
02262  *      that take a relation OID return NULL if the OID doesn't exist.
02263  */
02264 
02265 /*
02266  * has_any_column_privilege_name_name
02267  *      Check user privileges on any column of a table given
02268  *      name username, text tablename, and text priv name.
02269  */
02270 Datum
02271 has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
02272 {
02273     Name        rolename = PG_GETARG_NAME(0);
02274     text       *tablename = PG_GETARG_TEXT_P(1);
02275     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02276     Oid         roleid;
02277     Oid         tableoid;
02278     AclMode     mode;
02279     AclResult   aclresult;
02280 
02281     roleid = get_role_oid_or_public(NameStr(*rolename));
02282     tableoid = convert_table_name(tablename);
02283     mode = convert_column_priv_string(priv_type_text);
02284 
02285     /* First check at table level, then examine each column if needed */
02286     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02287     if (aclresult != ACLCHECK_OK)
02288         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02289                                               ACLMASK_ANY);
02290 
02291     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02292 }
02293 
02294 /*
02295  * has_any_column_privilege_name
02296  *      Check user privileges on any column of a table given
02297  *      text tablename and text priv name.
02298  *      current_user is assumed
02299  */
02300 Datum
02301 has_any_column_privilege_name(PG_FUNCTION_ARGS)
02302 {
02303     text       *tablename = PG_GETARG_TEXT_P(0);
02304     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02305     Oid         roleid;
02306     Oid         tableoid;
02307     AclMode     mode;
02308     AclResult   aclresult;
02309 
02310     roleid = GetUserId();
02311     tableoid = convert_table_name(tablename);
02312     mode = convert_column_priv_string(priv_type_text);
02313 
02314     /* First check at table level, then examine each column if needed */
02315     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02316     if (aclresult != ACLCHECK_OK)
02317         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02318                                               ACLMASK_ANY);
02319 
02320     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02321 }
02322 
02323 /*
02324  * has_any_column_privilege_name_id
02325  *      Check user privileges on any column of a table given
02326  *      name usename, table oid, and text priv name.
02327  */
02328 Datum
02329 has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
02330 {
02331     Name        username = PG_GETARG_NAME(0);
02332     Oid         tableoid = PG_GETARG_OID(1);
02333     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02334     Oid         roleid;
02335     AclMode     mode;
02336     AclResult   aclresult;
02337 
02338     roleid = get_role_oid_or_public(NameStr(*username));
02339     mode = convert_column_priv_string(priv_type_text);
02340 
02341     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
02342         PG_RETURN_NULL();
02343 
02344     /* First check at table level, then examine each column if needed */
02345     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02346     if (aclresult != ACLCHECK_OK)
02347         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02348                                               ACLMASK_ANY);
02349 
02350     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02351 }
02352 
02353 /*
02354  * has_any_column_privilege_id
02355  *      Check user privileges on any column of a table given
02356  *      table oid, and text priv name.
02357  *      current_user is assumed
02358  */
02359 Datum
02360 has_any_column_privilege_id(PG_FUNCTION_ARGS)
02361 {
02362     Oid         tableoid = PG_GETARG_OID(0);
02363     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02364     Oid         roleid;
02365     AclMode     mode;
02366     AclResult   aclresult;
02367 
02368     roleid = GetUserId();
02369     mode = convert_column_priv_string(priv_type_text);
02370 
02371     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
02372         PG_RETURN_NULL();
02373 
02374     /* First check at table level, then examine each column if needed */
02375     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02376     if (aclresult != ACLCHECK_OK)
02377         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02378                                               ACLMASK_ANY);
02379 
02380     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02381 }
02382 
02383 /*
02384  * has_any_column_privilege_id_name
02385  *      Check user privileges on any column of a table given
02386  *      roleid, text tablename, and text priv name.
02387  */
02388 Datum
02389 has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
02390 {
02391     Oid         roleid = PG_GETARG_OID(0);
02392     text       *tablename = PG_GETARG_TEXT_P(1);
02393     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02394     Oid         tableoid;
02395     AclMode     mode;
02396     AclResult   aclresult;
02397 
02398     tableoid = convert_table_name(tablename);
02399     mode = convert_column_priv_string(priv_type_text);
02400 
02401     /* First check at table level, then examine each column if needed */
02402     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02403     if (aclresult != ACLCHECK_OK)
02404         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02405                                               ACLMASK_ANY);
02406 
02407     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02408 }
02409 
02410 /*
02411  * has_any_column_privilege_id_id
02412  *      Check user privileges on any column of a table given
02413  *      roleid, table oid, and text priv name.
02414  */
02415 Datum
02416 has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
02417 {
02418     Oid         roleid = PG_GETARG_OID(0);
02419     Oid         tableoid = PG_GETARG_OID(1);
02420     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02421     AclMode     mode;
02422     AclResult   aclresult;
02423 
02424     mode = convert_column_priv_string(priv_type_text);
02425 
02426     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
02427         PG_RETURN_NULL();
02428 
02429     /* First check at table level, then examine each column if needed */
02430     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02431     if (aclresult != ACLCHECK_OK)
02432         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
02433                                               ACLMASK_ANY);
02434 
02435     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02436 }
02437 
02438 
02439 /*
02440  * has_column_privilege variants
02441  *      These are all named "has_column_privilege" at the SQL level.
02442  *      They take various combinations of relation name, relation OID,
02443  *      column name, column attnum, user name, user OID, or
02444  *      implicit user = current_user.
02445  *
02446  *      The result is a boolean value: true if user has the indicated
02447  *      privilege, false if not.  The variants that take a relation OID
02448  *      and an integer attnum return NULL (rather than throwing an error)
02449  *      if the column doesn't exist or is dropped.
02450  */
02451 
02452 /*
02453  * column_privilege_check: check column privileges, but don't throw an error
02454  *      for dropped column or table
02455  *
02456  * Returns 1 if have the privilege, 0 if not, -1 if dropped column/table.
02457  */
02458 static int
02459 column_privilege_check(Oid tableoid, AttrNumber attnum,
02460                        Oid roleid, AclMode mode)
02461 {
02462     AclResult   aclresult;
02463     HeapTuple   attTuple;
02464     Form_pg_attribute attributeForm;
02465 
02466     /*
02467      * First check if we have the privilege at the table level.  We check
02468      * existence of the pg_class row before risking calling pg_class_aclcheck.
02469      * Note: it might seem there's a race condition against concurrent DROP,
02470      * but really it's safe because there will be no syscache flush between
02471      * here and there.  So if we see the row in the syscache, so will
02472      * pg_class_aclcheck.
02473      */
02474     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
02475         return -1;
02476 
02477     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
02478 
02479     if (aclresult == ACLCHECK_OK)
02480         return true;
02481 
02482     /*
02483      * No table privilege, so try per-column privileges.  Again, we have to
02484      * check for dropped attribute first, and we rely on the syscache not to
02485      * notice a concurrent drop before pg_attribute_aclcheck fetches the row.
02486      */
02487     attTuple = SearchSysCache2(ATTNUM,
02488                                ObjectIdGetDatum(tableoid),
02489                                Int16GetDatum(attnum));
02490     if (!HeapTupleIsValid(attTuple))
02491         return -1;
02492     attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
02493     if (attributeForm->attisdropped)
02494     {
02495         ReleaseSysCache(attTuple);
02496         return -1;
02497     }
02498     ReleaseSysCache(attTuple);
02499 
02500     aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode);
02501 
02502     return (aclresult == ACLCHECK_OK);
02503 }
02504 
02505 /*
02506  * has_column_privilege_name_name_name
02507  *      Check user privileges on a column given
02508  *      name username, text tablename, text colname, and text priv name.
02509  */
02510 Datum
02511 has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
02512 {
02513     Name        rolename = PG_GETARG_NAME(0);
02514     text       *tablename = PG_GETARG_TEXT_P(1);
02515     text       *column = PG_GETARG_TEXT_P(2);
02516     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02517     Oid         roleid;
02518     Oid         tableoid;
02519     AttrNumber  colattnum;
02520     AclMode     mode;
02521     int         privresult;
02522 
02523     roleid = get_role_oid_or_public(NameStr(*rolename));
02524     tableoid = convert_table_name(tablename);
02525     colattnum = convert_column_name(tableoid, column);
02526     mode = convert_column_priv_string(priv_type_text);
02527 
02528     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02529     if (privresult < 0)
02530         PG_RETURN_NULL();
02531     PG_RETURN_BOOL(privresult);
02532 }
02533 
02534 /*
02535  * has_column_privilege_name_name_attnum
02536  *      Check user privileges on a column given
02537  *      name username, text tablename, int attnum, and text priv name.
02538  */
02539 Datum
02540 has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
02541 {
02542     Name        rolename = PG_GETARG_NAME(0);
02543     text       *tablename = PG_GETARG_TEXT_P(1);
02544     AttrNumber  colattnum = PG_GETARG_INT16(2);
02545     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02546     Oid         roleid;
02547     Oid         tableoid;
02548     AclMode     mode;
02549     int         privresult;
02550 
02551     roleid = get_role_oid_or_public(NameStr(*rolename));
02552     tableoid = convert_table_name(tablename);
02553     mode = convert_column_priv_string(priv_type_text);
02554 
02555     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02556     if (privresult < 0)
02557         PG_RETURN_NULL();
02558     PG_RETURN_BOOL(privresult);
02559 }
02560 
02561 /*
02562  * has_column_privilege_name_id_name
02563  *      Check user privileges on a column given
02564  *      name username, table oid, text colname, and text priv name.
02565  */
02566 Datum
02567 has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
02568 {
02569     Name        username = PG_GETARG_NAME(0);
02570     Oid         tableoid = PG_GETARG_OID(1);
02571     text       *column = PG_GETARG_TEXT_P(2);
02572     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02573     Oid         roleid;
02574     AttrNumber  colattnum;
02575     AclMode     mode;
02576     int         privresult;
02577 
02578     roleid = get_role_oid_or_public(NameStr(*username));
02579     colattnum = convert_column_name(tableoid, column);
02580     mode = convert_column_priv_string(priv_type_text);
02581 
02582     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02583     if (privresult < 0)
02584         PG_RETURN_NULL();
02585     PG_RETURN_BOOL(privresult);
02586 }
02587 
02588 /*
02589  * has_column_privilege_name_id_attnum
02590  *      Check user privileges on a column given
02591  *      name username, table oid, int attnum, and text priv name.
02592  */
02593 Datum
02594 has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS)
02595 {
02596     Name        username = PG_GETARG_NAME(0);
02597     Oid         tableoid = PG_GETARG_OID(1);
02598     AttrNumber  colattnum = PG_GETARG_INT16(2);
02599     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02600     Oid         roleid;
02601     AclMode     mode;
02602     int         privresult;
02603 
02604     roleid = get_role_oid_or_public(NameStr(*username));
02605     mode = convert_column_priv_string(priv_type_text);
02606 
02607     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02608     if (privresult < 0)
02609         PG_RETURN_NULL();
02610     PG_RETURN_BOOL(privresult);
02611 }
02612 
02613 /*
02614  * has_column_privilege_id_name_name
02615  *      Check user privileges on a column given
02616  *      oid roleid, text tablename, text colname, and text priv name.
02617  */
02618 Datum
02619 has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
02620 {
02621     Oid         roleid = PG_GETARG_OID(0);
02622     text       *tablename = PG_GETARG_TEXT_P(1);
02623     text       *column = PG_GETARG_TEXT_P(2);
02624     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02625     Oid         tableoid;
02626     AttrNumber  colattnum;
02627     AclMode     mode;
02628     int         privresult;
02629 
02630     tableoid = convert_table_name(tablename);
02631     colattnum = convert_column_name(tableoid, column);
02632     mode = convert_column_priv_string(priv_type_text);
02633 
02634     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02635     if (privresult < 0)
02636         PG_RETURN_NULL();
02637     PG_RETURN_BOOL(privresult);
02638 }
02639 
02640 /*
02641  * has_column_privilege_id_name_attnum
02642  *      Check user privileges on a column given
02643  *      oid roleid, text tablename, int attnum, and text priv name.
02644  */
02645 Datum
02646 has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS)
02647 {
02648     Oid         roleid = PG_GETARG_OID(0);
02649     text       *tablename = PG_GETARG_TEXT_P(1);
02650     AttrNumber  colattnum = PG_GETARG_INT16(2);
02651     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02652     Oid         tableoid;
02653     AclMode     mode;
02654     int         privresult;
02655 
02656     tableoid = convert_table_name(tablename);
02657     mode = convert_column_priv_string(priv_type_text);
02658 
02659     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02660     if (privresult < 0)
02661         PG_RETURN_NULL();
02662     PG_RETURN_BOOL(privresult);
02663 }
02664 
02665 /*
02666  * has_column_privilege_id_id_name
02667  *      Check user privileges on a column given
02668  *      oid roleid, table oid, text colname, and text priv name.
02669  */
02670 Datum
02671 has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
02672 {
02673     Oid         roleid = PG_GETARG_OID(0);
02674     Oid         tableoid = PG_GETARG_OID(1);
02675     text       *column = PG_GETARG_TEXT_P(2);
02676     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02677     AttrNumber  colattnum;
02678     AclMode     mode;
02679     int         privresult;
02680 
02681     colattnum = convert_column_name(tableoid, column);
02682     mode = convert_column_priv_string(priv_type_text);
02683 
02684     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02685     if (privresult < 0)
02686         PG_RETURN_NULL();
02687     PG_RETURN_BOOL(privresult);
02688 }
02689 
02690 /*
02691  * has_column_privilege_id_id_attnum
02692  *      Check user privileges on a column given
02693  *      oid roleid, table oid, int attnum, and text priv name.
02694  */
02695 Datum
02696 has_column_privilege_id_id_attnum(PG_FUNCTION_ARGS)
02697 {
02698     Oid         roleid = PG_GETARG_OID(0);
02699     Oid         tableoid = PG_GETARG_OID(1);
02700     AttrNumber  colattnum = PG_GETARG_INT16(2);
02701     text       *priv_type_text = PG_GETARG_TEXT_P(3);
02702     AclMode     mode;
02703     int         privresult;
02704 
02705     mode = convert_column_priv_string(priv_type_text);
02706 
02707     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02708     if (privresult < 0)
02709         PG_RETURN_NULL();
02710     PG_RETURN_BOOL(privresult);
02711 }
02712 
02713 /*
02714  * has_column_privilege_name_name
02715  *      Check user privileges on a column given
02716  *      text tablename, text colname, and text priv name.
02717  *      current_user is assumed
02718  */
02719 Datum
02720 has_column_privilege_name_name(PG_FUNCTION_ARGS)
02721 {
02722     text       *tablename = PG_GETARG_TEXT_P(0);
02723     text       *column = PG_GETARG_TEXT_P(1);
02724     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02725     Oid         roleid;
02726     Oid         tableoid;
02727     AttrNumber  colattnum;
02728     AclMode     mode;
02729     int         privresult;
02730 
02731     roleid = GetUserId();
02732     tableoid = convert_table_name(tablename);
02733     colattnum = convert_column_name(tableoid, column);
02734     mode = convert_column_priv_string(priv_type_text);
02735 
02736     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02737     if (privresult < 0)
02738         PG_RETURN_NULL();
02739     PG_RETURN_BOOL(privresult);
02740 }
02741 
02742 /*
02743  * has_column_privilege_name_attnum
02744  *      Check user privileges on a column given
02745  *      text tablename, int attnum, and text priv name.
02746  *      current_user is assumed
02747  */
02748 Datum
02749 has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
02750 {
02751     text       *tablename = PG_GETARG_TEXT_P(0);
02752     AttrNumber  colattnum = PG_GETARG_INT16(1);
02753     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02754     Oid         roleid;
02755     Oid         tableoid;
02756     AclMode     mode;
02757     int         privresult;
02758 
02759     roleid = GetUserId();
02760     tableoid = convert_table_name(tablename);
02761     mode = convert_column_priv_string(priv_type_text);
02762 
02763     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02764     if (privresult < 0)
02765         PG_RETURN_NULL();
02766     PG_RETURN_BOOL(privresult);
02767 }
02768 
02769 /*
02770  * has_column_privilege_id_name
02771  *      Check user privileges on a column given
02772  *      table oid, text colname, and text priv name.
02773  *      current_user is assumed
02774  */
02775 Datum
02776 has_column_privilege_id_name(PG_FUNCTION_ARGS)
02777 {
02778     Oid         tableoid = PG_GETARG_OID(0);
02779     text       *column = PG_GETARG_TEXT_P(1);
02780     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02781     Oid         roleid;
02782     AttrNumber  colattnum;
02783     AclMode     mode;
02784     int         privresult;
02785 
02786     roleid = GetUserId();
02787     colattnum = convert_column_name(tableoid, column);
02788     mode = convert_column_priv_string(priv_type_text);
02789 
02790     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02791     if (privresult < 0)
02792         PG_RETURN_NULL();
02793     PG_RETURN_BOOL(privresult);
02794 }
02795 
02796 /*
02797  * has_column_privilege_id_attnum
02798  *      Check user privileges on a column given
02799  *      table oid, int attnum, and text priv name.
02800  *      current_user is assumed
02801  */
02802 Datum
02803 has_column_privilege_id_attnum(PG_FUNCTION_ARGS)
02804 {
02805     Oid         tableoid = PG_GETARG_OID(0);
02806     AttrNumber  colattnum = PG_GETARG_INT16(1);
02807     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02808     Oid         roleid;
02809     AclMode     mode;
02810     int         privresult;
02811 
02812     roleid = GetUserId();
02813     mode = convert_column_priv_string(priv_type_text);
02814 
02815     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
02816     if (privresult < 0)
02817         PG_RETURN_NULL();
02818     PG_RETURN_BOOL(privresult);
02819 }
02820 
02821 /*
02822  *      Support routines for has_column_privilege family.
02823  */
02824 
02825 /*
02826  * Given a table OID and a column name expressed as a string, look it up
02827  * and return the column number
02828  */
02829 static AttrNumber
02830 convert_column_name(Oid tableoid, text *column)
02831 {
02832     AttrNumber  attnum;
02833     char       *colname;
02834 
02835     colname = text_to_cstring(column);
02836     attnum = get_attnum(tableoid, colname);
02837     if (attnum == InvalidAttrNumber)
02838         ereport(ERROR,
02839                 (errcode(ERRCODE_UNDEFINED_COLUMN),
02840                  errmsg("column \"%s\" of relation \"%s\" does not exist",
02841                         colname, get_rel_name(tableoid))));
02842     pfree(colname);
02843     return attnum;
02844 }
02845 
02846 /*
02847  * convert_column_priv_string
02848  *      Convert text string to AclMode value.
02849  */
02850 static AclMode
02851 convert_column_priv_string(text *priv_type_text)
02852 {
02853     static const priv_map column_priv_map[] = {
02854         {"SELECT", ACL_SELECT},
02855         {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
02856         {"INSERT", ACL_INSERT},
02857         {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
02858         {"UPDATE", ACL_UPDATE},
02859         {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
02860         {"REFERENCES", ACL_REFERENCES},
02861         {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
02862         {NULL, 0}
02863     };
02864 
02865     return convert_any_priv_string(priv_type_text, column_priv_map);
02866 }
02867 
02868 
02869 /*
02870  * has_database_privilege variants
02871  *      These are all named "has_database_privilege" at the SQL level.
02872  *      They take various combinations of database name, database OID,
02873  *      user name, user OID, or implicit user = current_user.
02874  *
02875  *      The result is a boolean value: true if user has the indicated
02876  *      privilege, false if not, or NULL if object doesn't exist.
02877  */
02878 
02879 /*
02880  * has_database_privilege_name_name
02881  *      Check user privileges on a database given
02882  *      name username, text databasename, and text priv name.
02883  */
02884 Datum
02885 has_database_privilege_name_name(PG_FUNCTION_ARGS)
02886 {
02887     Name        username = PG_GETARG_NAME(0);
02888     text       *databasename = PG_GETARG_TEXT_P(1);
02889     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02890     Oid         roleid;
02891     Oid         databaseoid;
02892     AclMode     mode;
02893     AclResult   aclresult;
02894 
02895     roleid = get_role_oid_or_public(NameStr(*username));
02896     databaseoid = convert_database_name(databasename);
02897     mode = convert_database_priv_string(priv_type_text);
02898 
02899     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
02900 
02901     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02902 }
02903 
02904 /*
02905  * has_database_privilege_name
02906  *      Check user privileges on a database given
02907  *      text databasename and text priv name.
02908  *      current_user is assumed
02909  */
02910 Datum
02911 has_database_privilege_name(PG_FUNCTION_ARGS)
02912 {
02913     text       *databasename = PG_GETARG_TEXT_P(0);
02914     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02915     Oid         roleid;
02916     Oid         databaseoid;
02917     AclMode     mode;
02918     AclResult   aclresult;
02919 
02920     roleid = GetUserId();
02921     databaseoid = convert_database_name(databasename);
02922     mode = convert_database_priv_string(priv_type_text);
02923 
02924     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
02925 
02926     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02927 }
02928 
02929 /*
02930  * has_database_privilege_name_id
02931  *      Check user privileges on a database given
02932  *      name usename, database oid, and text priv name.
02933  */
02934 Datum
02935 has_database_privilege_name_id(PG_FUNCTION_ARGS)
02936 {
02937     Name        username = PG_GETARG_NAME(0);
02938     Oid         databaseoid = PG_GETARG_OID(1);
02939     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02940     Oid         roleid;
02941     AclMode     mode;
02942     AclResult   aclresult;
02943 
02944     roleid = get_role_oid_or_public(NameStr(*username));
02945     mode = convert_database_priv_string(priv_type_text);
02946 
02947     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
02948         PG_RETURN_NULL();
02949 
02950     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
02951 
02952     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02953 }
02954 
02955 /*
02956  * has_database_privilege_id
02957  *      Check user privileges on a database given
02958  *      database oid, and text priv name.
02959  *      current_user is assumed
02960  */
02961 Datum
02962 has_database_privilege_id(PG_FUNCTION_ARGS)
02963 {
02964     Oid         databaseoid = PG_GETARG_OID(0);
02965     text       *priv_type_text = PG_GETARG_TEXT_P(1);
02966     Oid         roleid;
02967     AclMode     mode;
02968     AclResult   aclresult;
02969 
02970     roleid = GetUserId();
02971     mode = convert_database_priv_string(priv_type_text);
02972 
02973     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
02974         PG_RETURN_NULL();
02975 
02976     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
02977 
02978     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
02979 }
02980 
02981 /*
02982  * has_database_privilege_id_name
02983  *      Check user privileges on a database given
02984  *      roleid, text databasename, and text priv name.
02985  */
02986 Datum
02987 has_database_privilege_id_name(PG_FUNCTION_ARGS)
02988 {
02989     Oid         roleid = PG_GETARG_OID(0);
02990     text       *databasename = PG_GETARG_TEXT_P(1);
02991     text       *priv_type_text = PG_GETARG_TEXT_P(2);
02992     Oid         databaseoid;
02993     AclMode     mode;
02994     AclResult   aclresult;
02995 
02996     databaseoid = convert_database_name(databasename);
02997     mode = convert_database_priv_string(priv_type_text);
02998 
02999     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
03000 
03001     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03002 }
03003 
03004 /*
03005  * has_database_privilege_id_id
03006  *      Check user privileges on a database given
03007  *      roleid, database oid, and text priv name.
03008  */
03009 Datum
03010 has_database_privilege_id_id(PG_FUNCTION_ARGS)
03011 {
03012     Oid         roleid = PG_GETARG_OID(0);
03013     Oid         databaseoid = PG_GETARG_OID(1);
03014     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03015     AclMode     mode;
03016     AclResult   aclresult;
03017 
03018     mode = convert_database_priv_string(priv_type_text);
03019 
03020     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
03021         PG_RETURN_NULL();
03022 
03023     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
03024 
03025     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03026 }
03027 
03028 /*
03029  *      Support routines for has_database_privilege family.
03030  */
03031 
03032 /*
03033  * Given a database name expressed as a string, look it up and return Oid
03034  */
03035 static Oid
03036 convert_database_name(text *databasename)
03037 {
03038     char       *dbname = text_to_cstring(databasename);
03039 
03040     return get_database_oid(dbname, false);
03041 }
03042 
03043 /*
03044  * convert_database_priv_string
03045  *      Convert text string to AclMode value.
03046  */
03047 static AclMode
03048 convert_database_priv_string(text *priv_type_text)
03049 {
03050     static const priv_map database_priv_map[] = {
03051         {"CREATE", ACL_CREATE},
03052         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
03053         {"TEMPORARY", ACL_CREATE_TEMP},
03054         {"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
03055         {"TEMP", ACL_CREATE_TEMP},
03056         {"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
03057         {"CONNECT", ACL_CONNECT},
03058         {"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
03059         {NULL, 0}
03060     };
03061 
03062     return convert_any_priv_string(priv_type_text, database_priv_map);
03063 
03064 }
03065 
03066 
03067 /*
03068  * has_foreign_data_wrapper_privilege variants
03069  *      These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
03070  *      They take various combinations of foreign-data wrapper name,
03071  *      fdw OID, user name, user OID, or implicit user = current_user.
03072  *
03073  *      The result is a boolean value: true if user has the indicated
03074  *      privilege, false if not.
03075  */
03076 
03077 /*
03078  * has_foreign_data_wrapper_privilege_name_name
03079  *      Check user privileges on a foreign-data wrapper given
03080  *      name username, text fdwname, and text priv name.
03081  */
03082 Datum
03083 has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
03084 {
03085     Name        username = PG_GETARG_NAME(0);
03086     text       *fdwname = PG_GETARG_TEXT_P(1);
03087     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03088     Oid         roleid;
03089     Oid         fdwid;
03090     AclMode     mode;
03091     AclResult   aclresult;
03092 
03093     roleid = get_role_oid_or_public(NameStr(*username));
03094     fdwid = convert_foreign_data_wrapper_name(fdwname);
03095     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03096 
03097     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03098 
03099     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03100 }
03101 
03102 /*
03103  * has_foreign_data_wrapper_privilege_name
03104  *      Check user privileges on a foreign-data wrapper given
03105  *      text fdwname and text priv name.
03106  *      current_user is assumed
03107  */
03108 Datum
03109 has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
03110 {
03111     text       *fdwname = PG_GETARG_TEXT_P(0);
03112     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03113     Oid         roleid;
03114     Oid         fdwid;
03115     AclMode     mode;
03116     AclResult   aclresult;
03117 
03118     roleid = GetUserId();
03119     fdwid = convert_foreign_data_wrapper_name(fdwname);
03120     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03121 
03122     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03123 
03124     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03125 }
03126 
03127 /*
03128  * has_foreign_data_wrapper_privilege_name_id
03129  *      Check user privileges on a foreign-data wrapper given
03130  *      name usename, foreign-data wrapper oid, and text priv name.
03131  */
03132 Datum
03133 has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
03134 {
03135     Name        username = PG_GETARG_NAME(0);
03136     Oid         fdwid = PG_GETARG_OID(1);
03137     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03138     Oid         roleid;
03139     AclMode     mode;
03140     AclResult   aclresult;
03141 
03142     roleid = get_role_oid_or_public(NameStr(*username));
03143     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03144 
03145     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03146 
03147     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03148 }
03149 
03150 /*
03151  * has_foreign_data_wrapper_privilege_id
03152  *      Check user privileges on a foreign-data wrapper given
03153  *      foreign-data wrapper oid, and text priv name.
03154  *      current_user is assumed
03155  */
03156 Datum
03157 has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
03158 {
03159     Oid         fdwid = PG_GETARG_OID(0);
03160     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03161     Oid         roleid;
03162     AclMode     mode;
03163     AclResult   aclresult;
03164 
03165     roleid = GetUserId();
03166     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03167 
03168     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03169 
03170     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03171 }
03172 
03173 /*
03174  * has_foreign_data_wrapper_privilege_id_name
03175  *      Check user privileges on a foreign-data wrapper given
03176  *      roleid, text fdwname, and text priv name.
03177  */
03178 Datum
03179 has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
03180 {
03181     Oid         roleid = PG_GETARG_OID(0);
03182     text       *fdwname = PG_GETARG_TEXT_P(1);
03183     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03184     Oid         fdwid;
03185     AclMode     mode;
03186     AclResult   aclresult;
03187 
03188     fdwid = convert_foreign_data_wrapper_name(fdwname);
03189     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03190 
03191     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03192 
03193     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03194 }
03195 
03196 /*
03197  * has_foreign_data_wrapper_privilege_id_id
03198  *      Check user privileges on a foreign-data wrapper given
03199  *      roleid, fdw oid, and text priv name.
03200  */
03201 Datum
03202 has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
03203 {
03204     Oid         roleid = PG_GETARG_OID(0);
03205     Oid         fdwid = PG_GETARG_OID(1);
03206     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03207     AclMode     mode;
03208     AclResult   aclresult;
03209 
03210     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
03211 
03212     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
03213 
03214     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03215 }
03216 
03217 /*
03218  *      Support routines for has_foreign_data_wrapper_privilege family.
03219  */
03220 
03221 /*
03222  * Given a FDW name expressed as a string, look it up and return Oid
03223  */
03224 static Oid
03225 convert_foreign_data_wrapper_name(text *fdwname)
03226 {
03227     char       *fdwstr = text_to_cstring(fdwname);
03228 
03229     return get_foreign_data_wrapper_oid(fdwstr, false);
03230 }
03231 
03232 /*
03233  * convert_foreign_data_wrapper_priv_string
03234  *      Convert text string to AclMode value.
03235  */
03236 static AclMode
03237 convert_foreign_data_wrapper_priv_string(text *priv_type_text)
03238 {
03239     static const priv_map foreign_data_wrapper_priv_map[] = {
03240         {"USAGE", ACL_USAGE},
03241         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
03242         {NULL, 0}
03243     };
03244 
03245     return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map);
03246 }
03247 
03248 
03249 /*
03250  * has_function_privilege variants
03251  *      These are all named "has_function_privilege" at the SQL level.
03252  *      They take various combinations of function name, function OID,
03253  *      user name, user OID, or implicit user = current_user.
03254  *
03255  *      The result is a boolean value: true if user has the indicated
03256  *      privilege, false if not, or NULL if object doesn't exist.
03257  */
03258 
03259 /*
03260  * has_function_privilege_name_name
03261  *      Check user privileges on a function given
03262  *      name username, text functionname, and text priv name.
03263  */
03264 Datum
03265 has_function_privilege_name_name(PG_FUNCTION_ARGS)
03266 {
03267     Name        username = PG_GETARG_NAME(0);
03268     text       *functionname = PG_GETARG_TEXT_P(1);
03269     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03270     Oid         roleid;
03271     Oid         functionoid;
03272     AclMode     mode;
03273     AclResult   aclresult;
03274 
03275     roleid = get_role_oid_or_public(NameStr(*username));
03276     functionoid = convert_function_name(functionname);
03277     mode = convert_function_priv_string(priv_type_text);
03278 
03279     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03280 
03281     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03282 }
03283 
03284 /*
03285  * has_function_privilege_name
03286  *      Check user privileges on a function given
03287  *      text functionname and text priv name.
03288  *      current_user is assumed
03289  */
03290 Datum
03291 has_function_privilege_name(PG_FUNCTION_ARGS)
03292 {
03293     text       *functionname = PG_GETARG_TEXT_P(0);
03294     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03295     Oid         roleid;
03296     Oid         functionoid;
03297     AclMode     mode;
03298     AclResult   aclresult;
03299 
03300     roleid = GetUserId();
03301     functionoid = convert_function_name(functionname);
03302     mode = convert_function_priv_string(priv_type_text);
03303 
03304     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03305 
03306     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03307 }
03308 
03309 /*
03310  * has_function_privilege_name_id
03311  *      Check user privileges on a function given
03312  *      name usename, function oid, and text priv name.
03313  */
03314 Datum
03315 has_function_privilege_name_id(PG_FUNCTION_ARGS)
03316 {
03317     Name        username = PG_GETARG_NAME(0);
03318     Oid         functionoid = PG_GETARG_OID(1);
03319     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03320     Oid         roleid;
03321     AclMode     mode;
03322     AclResult   aclresult;
03323 
03324     roleid = get_role_oid_or_public(NameStr(*username));
03325     mode = convert_function_priv_string(priv_type_text);
03326 
03327     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
03328         PG_RETURN_NULL();
03329 
03330     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03331 
03332     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03333 }
03334 
03335 /*
03336  * has_function_privilege_id
03337  *      Check user privileges on a function given
03338  *      function oid, and text priv name.
03339  *      current_user is assumed
03340  */
03341 Datum
03342 has_function_privilege_id(PG_FUNCTION_ARGS)
03343 {
03344     Oid         functionoid = PG_GETARG_OID(0);
03345     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03346     Oid         roleid;
03347     AclMode     mode;
03348     AclResult   aclresult;
03349 
03350     roleid = GetUserId();
03351     mode = convert_function_priv_string(priv_type_text);
03352 
03353     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
03354         PG_RETURN_NULL();
03355 
03356     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03357 
03358     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03359 }
03360 
03361 /*
03362  * has_function_privilege_id_name
03363  *      Check user privileges on a function given
03364  *      roleid, text functionname, and text priv name.
03365  */
03366 Datum
03367 has_function_privilege_id_name(PG_FUNCTION_ARGS)
03368 {
03369     Oid         roleid = PG_GETARG_OID(0);
03370     text       *functionname = PG_GETARG_TEXT_P(1);
03371     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03372     Oid         functionoid;
03373     AclMode     mode;
03374     AclResult   aclresult;
03375 
03376     functionoid = convert_function_name(functionname);
03377     mode = convert_function_priv_string(priv_type_text);
03378 
03379     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03380 
03381     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03382 }
03383 
03384 /*
03385  * has_function_privilege_id_id
03386  *      Check user privileges on a function given
03387  *      roleid, function oid, and text priv name.
03388  */
03389 Datum
03390 has_function_privilege_id_id(PG_FUNCTION_ARGS)
03391 {
03392     Oid         roleid = PG_GETARG_OID(0);
03393     Oid         functionoid = PG_GETARG_OID(1);
03394     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03395     AclMode     mode;
03396     AclResult   aclresult;
03397 
03398     mode = convert_function_priv_string(priv_type_text);
03399 
03400     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
03401         PG_RETURN_NULL();
03402 
03403     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
03404 
03405     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03406 }
03407 
03408 /*
03409  *      Support routines for has_function_privilege family.
03410  */
03411 
03412 /*
03413  * Given a function name expressed as a string, look it up and return Oid
03414  */
03415 static Oid
03416 convert_function_name(text *functionname)
03417 {
03418     char       *funcname = text_to_cstring(functionname);
03419     Oid         oid;
03420 
03421     oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
03422                                                CStringGetDatum(funcname)));
03423 
03424     if (!OidIsValid(oid))
03425         ereport(ERROR,
03426                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
03427                  errmsg("function \"%s\" does not exist", funcname)));
03428 
03429     return oid;
03430 }
03431 
03432 /*
03433  * convert_function_priv_string
03434  *      Convert text string to AclMode value.
03435  */
03436 static AclMode
03437 convert_function_priv_string(text *priv_type_text)
03438 {
03439     static const priv_map function_priv_map[] = {
03440         {"EXECUTE", ACL_EXECUTE},
03441         {"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
03442         {NULL, 0}
03443     };
03444 
03445     return convert_any_priv_string(priv_type_text, function_priv_map);
03446 }
03447 
03448 
03449 /*
03450  * has_language_privilege variants
03451  *      These are all named "has_language_privilege" at the SQL level.
03452  *      They take various combinations of language name, language OID,
03453  *      user name, user OID, or implicit user = current_user.
03454  *
03455  *      The result is a boolean value: true if user has the indicated
03456  *      privilege, false if not, or NULL if object doesn't exist.
03457  */
03458 
03459 /*
03460  * has_language_privilege_name_name
03461  *      Check user privileges on a language given
03462  *      name username, text languagename, and text priv name.
03463  */
03464 Datum
03465 has_language_privilege_name_name(PG_FUNCTION_ARGS)
03466 {
03467     Name        username = PG_GETARG_NAME(0);
03468     text       *languagename = PG_GETARG_TEXT_P(1);
03469     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03470     Oid         roleid;
03471     Oid         languageoid;
03472     AclMode     mode;
03473     AclResult   aclresult;
03474 
03475     roleid = get_role_oid_or_public(NameStr(*username));
03476     languageoid = convert_language_name(languagename);
03477     mode = convert_language_priv_string(priv_type_text);
03478 
03479     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03480 
03481     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03482 }
03483 
03484 /*
03485  * has_language_privilege_name
03486  *      Check user privileges on a language given
03487  *      text languagename and text priv name.
03488  *      current_user is assumed
03489  */
03490 Datum
03491 has_language_privilege_name(PG_FUNCTION_ARGS)
03492 {
03493     text       *languagename = PG_GETARG_TEXT_P(0);
03494     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03495     Oid         roleid;
03496     Oid         languageoid;
03497     AclMode     mode;
03498     AclResult   aclresult;
03499 
03500     roleid = GetUserId();
03501     languageoid = convert_language_name(languagename);
03502     mode = convert_language_priv_string(priv_type_text);
03503 
03504     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03505 
03506     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03507 }
03508 
03509 /*
03510  * has_language_privilege_name_id
03511  *      Check user privileges on a language given
03512  *      name usename, language oid, and text priv name.
03513  */
03514 Datum
03515 has_language_privilege_name_id(PG_FUNCTION_ARGS)
03516 {
03517     Name        username = PG_GETARG_NAME(0);
03518     Oid         languageoid = PG_GETARG_OID(1);
03519     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03520     Oid         roleid;
03521     AclMode     mode;
03522     AclResult   aclresult;
03523 
03524     roleid = get_role_oid_or_public(NameStr(*username));
03525     mode = convert_language_priv_string(priv_type_text);
03526 
03527     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
03528         PG_RETURN_NULL();
03529 
03530     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03531 
03532     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03533 }
03534 
03535 /*
03536  * has_language_privilege_id
03537  *      Check user privileges on a language given
03538  *      language oid, and text priv name.
03539  *      current_user is assumed
03540  */
03541 Datum
03542 has_language_privilege_id(PG_FUNCTION_ARGS)
03543 {
03544     Oid         languageoid = PG_GETARG_OID(0);
03545     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03546     Oid         roleid;
03547     AclMode     mode;
03548     AclResult   aclresult;
03549 
03550     roleid = GetUserId();
03551     mode = convert_language_priv_string(priv_type_text);
03552 
03553     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
03554         PG_RETURN_NULL();
03555 
03556     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03557 
03558     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03559 }
03560 
03561 /*
03562  * has_language_privilege_id_name
03563  *      Check user privileges on a language given
03564  *      roleid, text languagename, and text priv name.
03565  */
03566 Datum
03567 has_language_privilege_id_name(PG_FUNCTION_ARGS)
03568 {
03569     Oid         roleid = PG_GETARG_OID(0);
03570     text       *languagename = PG_GETARG_TEXT_P(1);
03571     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03572     Oid         languageoid;
03573     AclMode     mode;
03574     AclResult   aclresult;
03575 
03576     languageoid = convert_language_name(languagename);
03577     mode = convert_language_priv_string(priv_type_text);
03578 
03579     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03580 
03581     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03582 }
03583 
03584 /*
03585  * has_language_privilege_id_id
03586  *      Check user privileges on a language given
03587  *      roleid, language oid, and text priv name.
03588  */
03589 Datum
03590 has_language_privilege_id_id(PG_FUNCTION_ARGS)
03591 {
03592     Oid         roleid = PG_GETARG_OID(0);
03593     Oid         languageoid = PG_GETARG_OID(1);
03594     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03595     AclMode     mode;
03596     AclResult   aclresult;
03597 
03598     mode = convert_language_priv_string(priv_type_text);
03599 
03600     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
03601         PG_RETURN_NULL();
03602 
03603     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
03604 
03605     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03606 }
03607 
03608 /*
03609  *      Support routines for has_language_privilege family.
03610  */
03611 
03612 /*
03613  * Given a language name expressed as a string, look it up and return Oid
03614  */
03615 static Oid
03616 convert_language_name(text *languagename)
03617 {
03618     char       *langname = text_to_cstring(languagename);
03619 
03620     return get_language_oid(langname, false);
03621 }
03622 
03623 /*
03624  * convert_language_priv_string
03625  *      Convert text string to AclMode value.
03626  */
03627 static AclMode
03628 convert_language_priv_string(text *priv_type_text)
03629 {
03630     static const priv_map language_priv_map[] = {
03631         {"USAGE", ACL_USAGE},
03632         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
03633         {NULL, 0}
03634     };
03635 
03636     return convert_any_priv_string(priv_type_text, language_priv_map);
03637 }
03638 
03639 
03640 /*
03641  * has_schema_privilege variants
03642  *      These are all named "has_schema_privilege" at the SQL level.
03643  *      They take various combinations of schema name, schema OID,
03644  *      user name, user OID, or implicit user = current_user.
03645  *
03646  *      The result is a boolean value: true if user has the indicated
03647  *      privilege, false if not, or NULL if object doesn't exist.
03648  */
03649 
03650 /*
03651  * has_schema_privilege_name_name
03652  *      Check user privileges on a schema given
03653  *      name username, text schemaname, and text priv name.
03654  */
03655 Datum
03656 has_schema_privilege_name_name(PG_FUNCTION_ARGS)
03657 {
03658     Name        username = PG_GETARG_NAME(0);
03659     text       *schemaname = PG_GETARG_TEXT_P(1);
03660     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03661     Oid         roleid;
03662     Oid         schemaoid;
03663     AclMode     mode;
03664     AclResult   aclresult;
03665 
03666     roleid = get_role_oid_or_public(NameStr(*username));
03667     schemaoid = convert_schema_name(schemaname);
03668     mode = convert_schema_priv_string(priv_type_text);
03669 
03670     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03671 
03672     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03673 }
03674 
03675 /*
03676  * has_schema_privilege_name
03677  *      Check user privileges on a schema given
03678  *      text schemaname and text priv name.
03679  *      current_user is assumed
03680  */
03681 Datum
03682 has_schema_privilege_name(PG_FUNCTION_ARGS)
03683 {
03684     text       *schemaname = PG_GETARG_TEXT_P(0);
03685     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03686     Oid         roleid;
03687     Oid         schemaoid;
03688     AclMode     mode;
03689     AclResult   aclresult;
03690 
03691     roleid = GetUserId();
03692     schemaoid = convert_schema_name(schemaname);
03693     mode = convert_schema_priv_string(priv_type_text);
03694 
03695     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03696 
03697     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03698 }
03699 
03700 /*
03701  * has_schema_privilege_name_id
03702  *      Check user privileges on a schema given
03703  *      name usename, schema oid, and text priv name.
03704  */
03705 Datum
03706 has_schema_privilege_name_id(PG_FUNCTION_ARGS)
03707 {
03708     Name        username = PG_GETARG_NAME(0);
03709     Oid         schemaoid = PG_GETARG_OID(1);
03710     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03711     Oid         roleid;
03712     AclMode     mode;
03713     AclResult   aclresult;
03714 
03715     roleid = get_role_oid_or_public(NameStr(*username));
03716     mode = convert_schema_priv_string(priv_type_text);
03717 
03718     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
03719         PG_RETURN_NULL();
03720 
03721     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03722 
03723     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03724 }
03725 
03726 /*
03727  * has_schema_privilege_id
03728  *      Check user privileges on a schema given
03729  *      schema oid, and text priv name.
03730  *      current_user is assumed
03731  */
03732 Datum
03733 has_schema_privilege_id(PG_FUNCTION_ARGS)
03734 {
03735     Oid         schemaoid = PG_GETARG_OID(0);
03736     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03737     Oid         roleid;
03738     AclMode     mode;
03739     AclResult   aclresult;
03740 
03741     roleid = GetUserId();
03742     mode = convert_schema_priv_string(priv_type_text);
03743 
03744     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
03745         PG_RETURN_NULL();
03746 
03747     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03748 
03749     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03750 }
03751 
03752 /*
03753  * has_schema_privilege_id_name
03754  *      Check user privileges on a schema given
03755  *      roleid, text schemaname, and text priv name.
03756  */
03757 Datum
03758 has_schema_privilege_id_name(PG_FUNCTION_ARGS)
03759 {
03760     Oid         roleid = PG_GETARG_OID(0);
03761     text       *schemaname = PG_GETARG_TEXT_P(1);
03762     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03763     Oid         schemaoid;
03764     AclMode     mode;
03765     AclResult   aclresult;
03766 
03767     schemaoid = convert_schema_name(schemaname);
03768     mode = convert_schema_priv_string(priv_type_text);
03769 
03770     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03771 
03772     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03773 }
03774 
03775 /*
03776  * has_schema_privilege_id_id
03777  *      Check user privileges on a schema given
03778  *      roleid, schema oid, and text priv name.
03779  */
03780 Datum
03781 has_schema_privilege_id_id(PG_FUNCTION_ARGS)
03782 {
03783     Oid         roleid = PG_GETARG_OID(0);
03784     Oid         schemaoid = PG_GETARG_OID(1);
03785     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03786     AclMode     mode;
03787     AclResult   aclresult;
03788 
03789     mode = convert_schema_priv_string(priv_type_text);
03790 
03791     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
03792         PG_RETURN_NULL();
03793 
03794     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
03795 
03796     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03797 }
03798 
03799 /*
03800  *      Support routines for has_schema_privilege family.
03801  */
03802 
03803 /*
03804  * Given a schema name expressed as a string, look it up and return Oid
03805  */
03806 static Oid
03807 convert_schema_name(text *schemaname)
03808 {
03809     char       *nspname = text_to_cstring(schemaname);
03810 
03811     return get_namespace_oid(nspname, false);
03812 }
03813 
03814 /*
03815  * convert_schema_priv_string
03816  *      Convert text string to AclMode value.
03817  */
03818 static AclMode
03819 convert_schema_priv_string(text *priv_type_text)
03820 {
03821     static const priv_map schema_priv_map[] = {
03822         {"CREATE", ACL_CREATE},
03823         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
03824         {"USAGE", ACL_USAGE},
03825         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
03826         {NULL, 0}
03827     };
03828 
03829     return convert_any_priv_string(priv_type_text, schema_priv_map);
03830 }
03831 
03832 
03833 /*
03834  * has_server_privilege variants
03835  *      These are all named "has_server_privilege" at the SQL level.
03836  *      They take various combinations of foreign server name,
03837  *      server OID, user name, user OID, or implicit user = current_user.
03838  *
03839  *      The result is a boolean value: true if user has the indicated
03840  *      privilege, false if not.
03841  */
03842 
03843 /*
03844  * has_server_privilege_name_name
03845  *      Check user privileges on a foreign server given
03846  *      name username, text servername, and text priv name.
03847  */
03848 Datum
03849 has_server_privilege_name_name(PG_FUNCTION_ARGS)
03850 {
03851     Name        username = PG_GETARG_NAME(0);
03852     text       *servername = PG_GETARG_TEXT_P(1);
03853     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03854     Oid         roleid;
03855     Oid         serverid;
03856     AclMode     mode;
03857     AclResult   aclresult;
03858 
03859     roleid = get_role_oid_or_public(NameStr(*username));
03860     serverid = convert_server_name(servername);
03861     mode = convert_server_priv_string(priv_type_text);
03862 
03863     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03864 
03865     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03866 }
03867 
03868 /*
03869  * has_server_privilege_name
03870  *      Check user privileges on a foreign server given
03871  *      text servername and text priv name.
03872  *      current_user is assumed
03873  */
03874 Datum
03875 has_server_privilege_name(PG_FUNCTION_ARGS)
03876 {
03877     text       *servername = PG_GETARG_TEXT_P(0);
03878     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03879     Oid         roleid;
03880     Oid         serverid;
03881     AclMode     mode;
03882     AclResult   aclresult;
03883 
03884     roleid = GetUserId();
03885     serverid = convert_server_name(servername);
03886     mode = convert_server_priv_string(priv_type_text);
03887 
03888     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03889 
03890     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03891 }
03892 
03893 /*
03894  * has_server_privilege_name_id
03895  *      Check user privileges on a foreign server given
03896  *      name usename, foreign server oid, and text priv name.
03897  */
03898 Datum
03899 has_server_privilege_name_id(PG_FUNCTION_ARGS)
03900 {
03901     Name        username = PG_GETARG_NAME(0);
03902     Oid         serverid = PG_GETARG_OID(1);
03903     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03904     Oid         roleid;
03905     AclMode     mode;
03906     AclResult   aclresult;
03907 
03908     roleid = get_role_oid_or_public(NameStr(*username));
03909     mode = convert_server_priv_string(priv_type_text);
03910 
03911     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03912 
03913     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03914 }
03915 
03916 /*
03917  * has_server_privilege_id
03918  *      Check user privileges on a foreign server given
03919  *      server oid, and text priv name.
03920  *      current_user is assumed
03921  */
03922 Datum
03923 has_server_privilege_id(PG_FUNCTION_ARGS)
03924 {
03925     Oid         serverid = PG_GETARG_OID(0);
03926     text       *priv_type_text = PG_GETARG_TEXT_P(1);
03927     Oid         roleid;
03928     AclMode     mode;
03929     AclResult   aclresult;
03930 
03931     roleid = GetUserId();
03932     mode = convert_server_priv_string(priv_type_text);
03933 
03934     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03935 
03936     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03937 }
03938 
03939 /*
03940  * has_server_privilege_id_name
03941  *      Check user privileges on a foreign server given
03942  *      roleid, text servername, and text priv name.
03943  */
03944 Datum
03945 has_server_privilege_id_name(PG_FUNCTION_ARGS)
03946 {
03947     Oid         roleid = PG_GETARG_OID(0);
03948     text       *servername = PG_GETARG_TEXT_P(1);
03949     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03950     Oid         serverid;
03951     AclMode     mode;
03952     AclResult   aclresult;
03953 
03954     serverid = convert_server_name(servername);
03955     mode = convert_server_priv_string(priv_type_text);
03956 
03957     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03958 
03959     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03960 }
03961 
03962 /*
03963  * has_server_privilege_id_id
03964  *      Check user privileges on a foreign server given
03965  *      roleid, server oid, and text priv name.
03966  */
03967 Datum
03968 has_server_privilege_id_id(PG_FUNCTION_ARGS)
03969 {
03970     Oid         roleid = PG_GETARG_OID(0);
03971     Oid         serverid = PG_GETARG_OID(1);
03972     text       *priv_type_text = PG_GETARG_TEXT_P(2);
03973     AclMode     mode;
03974     AclResult   aclresult;
03975 
03976     mode = convert_server_priv_string(priv_type_text);
03977 
03978     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
03979 
03980     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
03981 }
03982 
03983 /*
03984  *      Support routines for has_server_privilege family.
03985  */
03986 
03987 /*
03988  * Given a server name expressed as a string, look it up and return Oid
03989  */
03990 static Oid
03991 convert_server_name(text *servername)
03992 {
03993     char       *serverstr = text_to_cstring(servername);
03994 
03995     return get_foreign_server_oid(serverstr, false);
03996 }
03997 
03998 /*
03999  * convert_server_priv_string
04000  *      Convert text string to AclMode value.
04001  */
04002 static AclMode
04003 convert_server_priv_string(text *priv_type_text)
04004 {
04005     static const priv_map server_priv_map[] = {
04006         {"USAGE", ACL_USAGE},
04007         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
04008         {NULL, 0}
04009     };
04010 
04011     return convert_any_priv_string(priv_type_text, server_priv_map);
04012 }
04013 
04014 
04015 /*
04016  * has_tablespace_privilege variants
04017  *      These are all named "has_tablespace_privilege" at the SQL level.
04018  *      They take various combinations of tablespace name, tablespace OID,
04019  *      user name, user OID, or implicit user = current_user.
04020  *
04021  *      The result is a boolean value: true if user has the indicated
04022  *      privilege, false if not.
04023  */
04024 
04025 /*
04026  * has_tablespace_privilege_name_name
04027  *      Check user privileges on a tablespace given
04028  *      name username, text tablespacename, and text priv name.
04029  */
04030 Datum
04031 has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
04032 {
04033     Name        username = PG_GETARG_NAME(0);
04034     text       *tablespacename = PG_GETARG_TEXT_P(1);
04035     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04036     Oid         roleid;
04037     Oid         tablespaceoid;
04038     AclMode     mode;
04039     AclResult   aclresult;
04040 
04041     roleid = get_role_oid_or_public(NameStr(*username));
04042     tablespaceoid = convert_tablespace_name(tablespacename);
04043     mode = convert_tablespace_priv_string(priv_type_text);
04044 
04045     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04046 
04047     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04048 }
04049 
04050 /*
04051  * has_tablespace_privilege_name
04052  *      Check user privileges on a tablespace given
04053  *      text tablespacename and text priv name.
04054  *      current_user is assumed
04055  */
04056 Datum
04057 has_tablespace_privilege_name(PG_FUNCTION_ARGS)
04058 {
04059     text       *tablespacename = PG_GETARG_TEXT_P(0);
04060     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04061     Oid         roleid;
04062     Oid         tablespaceoid;
04063     AclMode     mode;
04064     AclResult   aclresult;
04065 
04066     roleid = GetUserId();
04067     tablespaceoid = convert_tablespace_name(tablespacename);
04068     mode = convert_tablespace_priv_string(priv_type_text);
04069 
04070     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04071 
04072     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04073 }
04074 
04075 /*
04076  * has_tablespace_privilege_name_id
04077  *      Check user privileges on a tablespace given
04078  *      name usename, tablespace oid, and text priv name.
04079  */
04080 Datum
04081 has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
04082 {
04083     Name        username = PG_GETARG_NAME(0);
04084     Oid         tablespaceoid = PG_GETARG_OID(1);
04085     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04086     Oid         roleid;
04087     AclMode     mode;
04088     AclResult   aclresult;
04089 
04090     roleid = get_role_oid_or_public(NameStr(*username));
04091     mode = convert_tablespace_priv_string(priv_type_text);
04092 
04093     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04094 
04095     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04096 }
04097 
04098 /*
04099  * has_tablespace_privilege_id
04100  *      Check user privileges on a tablespace given
04101  *      tablespace oid, and text priv name.
04102  *      current_user is assumed
04103  */
04104 Datum
04105 has_tablespace_privilege_id(PG_FUNCTION_ARGS)
04106 {
04107     Oid         tablespaceoid = PG_GETARG_OID(0);
04108     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04109     Oid         roleid;
04110     AclMode     mode;
04111     AclResult   aclresult;
04112 
04113     roleid = GetUserId();
04114     mode = convert_tablespace_priv_string(priv_type_text);
04115 
04116     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04117 
04118     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04119 }
04120 
04121 /*
04122  * has_tablespace_privilege_id_name
04123  *      Check user privileges on a tablespace given
04124  *      roleid, text tablespacename, and text priv name.
04125  */
04126 Datum
04127 has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
04128 {
04129     Oid         roleid = PG_GETARG_OID(0);
04130     text       *tablespacename = PG_GETARG_TEXT_P(1);
04131     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04132     Oid         tablespaceoid;
04133     AclMode     mode;
04134     AclResult   aclresult;
04135 
04136     tablespaceoid = convert_tablespace_name(tablespacename);
04137     mode = convert_tablespace_priv_string(priv_type_text);
04138 
04139     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04140 
04141     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04142 }
04143 
04144 /*
04145  * has_tablespace_privilege_id_id
04146  *      Check user privileges on a tablespace given
04147  *      roleid, tablespace oid, and text priv name.
04148  */
04149 Datum
04150 has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
04151 {
04152     Oid         roleid = PG_GETARG_OID(0);
04153     Oid         tablespaceoid = PG_GETARG_OID(1);
04154     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04155     AclMode     mode;
04156     AclResult   aclresult;
04157 
04158     mode = convert_tablespace_priv_string(priv_type_text);
04159 
04160     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
04161 
04162     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04163 }
04164 
04165 /*
04166  *      Support routines for has_tablespace_privilege family.
04167  */
04168 
04169 /*
04170  * Given a tablespace name expressed as a string, look it up and return Oid
04171  */
04172 static Oid
04173 convert_tablespace_name(text *tablespacename)
04174 {
04175     char       *spcname = text_to_cstring(tablespacename);
04176 
04177     return get_tablespace_oid(spcname, false);
04178 }
04179 
04180 /*
04181  * convert_tablespace_priv_string
04182  *      Convert text string to AclMode value.
04183  */
04184 static AclMode
04185 convert_tablespace_priv_string(text *priv_type_text)
04186 {
04187     static const priv_map tablespace_priv_map[] = {
04188         {"CREATE", ACL_CREATE},
04189         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
04190         {NULL, 0}
04191     };
04192 
04193     return convert_any_priv_string(priv_type_text, tablespace_priv_map);
04194 }
04195 
04196 /*
04197  * has_type_privilege variants
04198  *      These are all named "has_type_privilege" at the SQL level.
04199  *      They take various combinations of type name, type OID,
04200  *      user name, user OID, or implicit user = current_user.
04201  *
04202  *      The result is a boolean value: true if user has the indicated
04203  *      privilege, false if not, or NULL if object doesn't exist.
04204  */
04205 
04206 /*
04207  * has_type_privilege_name_name
04208  *      Check user privileges on a type given
04209  *      name username, text typename, and text priv name.
04210  */
04211 Datum
04212 has_type_privilege_name_name(PG_FUNCTION_ARGS)
04213 {
04214     Name        username = PG_GETARG_NAME(0);
04215     text       *typename = PG_GETARG_TEXT_P(1);
04216     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04217     Oid         roleid;
04218     Oid         typeoid;
04219     AclMode     mode;
04220     AclResult   aclresult;
04221 
04222     roleid = get_role_oid_or_public(NameStr(*username));
04223     typeoid = convert_type_name(typename);
04224     mode = convert_type_priv_string(priv_type_text);
04225 
04226     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04227 
04228     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04229 }
04230 
04231 /*
04232  * has_type_privilege_name
04233  *      Check user privileges on a type given
04234  *      text typename and text priv name.
04235  *      current_user is assumed
04236  */
04237 Datum
04238 has_type_privilege_name(PG_FUNCTION_ARGS)
04239 {
04240     text       *typename = PG_GETARG_TEXT_P(0);
04241     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04242     Oid         roleid;
04243     Oid         typeoid;
04244     AclMode     mode;
04245     AclResult   aclresult;
04246 
04247     roleid = GetUserId();
04248     typeoid = convert_type_name(typename);
04249     mode = convert_type_priv_string(priv_type_text);
04250 
04251     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04252 
04253     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04254 }
04255 
04256 /*
04257  * has_type_privilege_name_id
04258  *      Check user privileges on a type given
04259  *      name usename, type oid, and text priv name.
04260  */
04261 Datum
04262 has_type_privilege_name_id(PG_FUNCTION_ARGS)
04263 {
04264     Name        username = PG_GETARG_NAME(0);
04265     Oid         typeoid = PG_GETARG_OID(1);
04266     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04267     Oid         roleid;
04268     AclMode     mode;
04269     AclResult   aclresult;
04270 
04271     roleid = get_role_oid_or_public(NameStr(*username));
04272     mode = convert_type_priv_string(priv_type_text);
04273 
04274     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
04275         PG_RETURN_NULL();
04276 
04277     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04278 
04279     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04280 }
04281 
04282 /*
04283  * has_type_privilege_id
04284  *      Check user privileges on a type given
04285  *      type oid, and text priv name.
04286  *      current_user is assumed
04287  */
04288 Datum
04289 has_type_privilege_id(PG_FUNCTION_ARGS)
04290 {
04291     Oid         typeoid = PG_GETARG_OID(0);
04292     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04293     Oid         roleid;
04294     AclMode     mode;
04295     AclResult   aclresult;
04296 
04297     roleid = GetUserId();
04298     mode = convert_type_priv_string(priv_type_text);
04299 
04300     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
04301         PG_RETURN_NULL();
04302 
04303     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04304 
04305     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04306 }
04307 
04308 /*
04309  * has_type_privilege_id_name
04310  *      Check user privileges on a type given
04311  *      roleid, text typename, and text priv name.
04312  */
04313 Datum
04314 has_type_privilege_id_name(PG_FUNCTION_ARGS)
04315 {
04316     Oid         roleid = PG_GETARG_OID(0);
04317     text       *typename = PG_GETARG_TEXT_P(1);
04318     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04319     Oid         typeoid;
04320     AclMode     mode;
04321     AclResult   aclresult;
04322 
04323     typeoid = convert_type_name(typename);
04324     mode = convert_type_priv_string(priv_type_text);
04325 
04326     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04327 
04328     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04329 }
04330 
04331 /*
04332  * has_type_privilege_id_id
04333  *      Check user privileges on a type given
04334  *      roleid, type oid, and text priv name.
04335  */
04336 Datum
04337 has_type_privilege_id_id(PG_FUNCTION_ARGS)
04338 {
04339     Oid         roleid = PG_GETARG_OID(0);
04340     Oid         typeoid = PG_GETARG_OID(1);
04341     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04342     AclMode     mode;
04343     AclResult   aclresult;
04344 
04345     mode = convert_type_priv_string(priv_type_text);
04346 
04347     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
04348         PG_RETURN_NULL();
04349 
04350     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
04351 
04352     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04353 }
04354 
04355 /*
04356  *      Support routines for has_type_privilege family.
04357  */
04358 
04359 /*
04360  * Given a type name expressed as a string, look it up and return Oid
04361  */
04362 static Oid
04363 convert_type_name(text *typename)
04364 {
04365     char       *typname = text_to_cstring(typename);
04366     Oid         oid;
04367 
04368     oid = DatumGetObjectId(DirectFunctionCall1(regtypein,
04369                                                CStringGetDatum(typname)));
04370 
04371     if (!OidIsValid(oid))
04372         ereport(ERROR,
04373                 (errcode(ERRCODE_UNDEFINED_OBJECT),
04374                  errmsg("type \"%s\" does not exist", typname)));
04375 
04376     return oid;
04377 }
04378 
04379 /*
04380  * convert_type_priv_string
04381  *      Convert text string to AclMode value.
04382  */
04383 static AclMode
04384 convert_type_priv_string(text *priv_type_text)
04385 {
04386     static const priv_map type_priv_map[] = {
04387         {"USAGE", ACL_USAGE},
04388         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
04389         {NULL, 0}
04390     };
04391 
04392     return convert_any_priv_string(priv_type_text, type_priv_map);
04393 }
04394 
04395 
04396 /*
04397  * pg_has_role variants
04398  *      These are all named "pg_has_role" at the SQL level.
04399  *      They take various combinations of role name, role OID,
04400  *      user name, user OID, or implicit user = current_user.
04401  *
04402  *      The result is a boolean value: true if user has the indicated
04403  *      privilege, false if not.
04404  */
04405 
04406 /*
04407  * pg_has_role_name_name
04408  *      Check user privileges on a role given
04409  *      name username, name rolename, and text priv name.
04410  */
04411 Datum
04412 pg_has_role_name_name(PG_FUNCTION_ARGS)
04413 {
04414     Name        username = PG_GETARG_NAME(0);
04415     Name        rolename = PG_GETARG_NAME(1);
04416     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04417     Oid         roleid;
04418     Oid         roleoid;
04419     AclMode     mode;
04420     AclResult   aclresult;
04421 
04422     roleid = get_role_oid(NameStr(*username), false);
04423     roleoid = get_role_oid(NameStr(*rolename), false);
04424     mode = convert_role_priv_string(priv_type_text);
04425 
04426     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04427 
04428     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04429 }
04430 
04431 /*
04432  * pg_has_role_name
04433  *      Check user privileges on a role given
04434  *      name rolename and text priv name.
04435  *      current_user is assumed
04436  */
04437 Datum
04438 pg_has_role_name(PG_FUNCTION_ARGS)
04439 {
04440     Name        rolename = PG_GETARG_NAME(0);
04441     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04442     Oid         roleid;
04443     Oid         roleoid;
04444     AclMode     mode;
04445     AclResult   aclresult;
04446 
04447     roleid = GetUserId();
04448     roleoid = get_role_oid(NameStr(*rolename), false);
04449     mode = convert_role_priv_string(priv_type_text);
04450 
04451     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04452 
04453     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04454 }
04455 
04456 /*
04457  * pg_has_role_name_id
04458  *      Check user privileges on a role given
04459  *      name usename, role oid, and text priv name.
04460  */
04461 Datum
04462 pg_has_role_name_id(PG_FUNCTION_ARGS)
04463 {
04464     Name        username = PG_GETARG_NAME(0);
04465     Oid         roleoid = PG_GETARG_OID(1);
04466     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04467     Oid         roleid;
04468     AclMode     mode;
04469     AclResult   aclresult;
04470 
04471     roleid = get_role_oid(NameStr(*username), false);
04472     mode = convert_role_priv_string(priv_type_text);
04473 
04474     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04475 
04476     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04477 }
04478 
04479 /*
04480  * pg_has_role_id
04481  *      Check user privileges on a role given
04482  *      role oid, and text priv name.
04483  *      current_user is assumed
04484  */
04485 Datum
04486 pg_has_role_id(PG_FUNCTION_ARGS)
04487 {
04488     Oid         roleoid = PG_GETARG_OID(0);
04489     text       *priv_type_text = PG_GETARG_TEXT_P(1);
04490     Oid         roleid;
04491     AclMode     mode;
04492     AclResult   aclresult;
04493 
04494     roleid = GetUserId();
04495     mode = convert_role_priv_string(priv_type_text);
04496 
04497     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04498 
04499     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04500 }
04501 
04502 /*
04503  * pg_has_role_id_name
04504  *      Check user privileges on a role given
04505  *      roleid, name rolename, and text priv name.
04506  */
04507 Datum
04508 pg_has_role_id_name(PG_FUNCTION_ARGS)
04509 {
04510     Oid         roleid = PG_GETARG_OID(0);
04511     Name        rolename = PG_GETARG_NAME(1);
04512     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04513     Oid         roleoid;
04514     AclMode     mode;
04515     AclResult   aclresult;
04516 
04517     roleoid = get_role_oid(NameStr(*rolename), false);
04518     mode = convert_role_priv_string(priv_type_text);
04519 
04520     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04521 
04522     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04523 }
04524 
04525 /*
04526  * pg_has_role_id_id
04527  *      Check user privileges on a role given
04528  *      roleid, role oid, and text priv name.
04529  */
04530 Datum
04531 pg_has_role_id_id(PG_FUNCTION_ARGS)
04532 {
04533     Oid         roleid = PG_GETARG_OID(0);
04534     Oid         roleoid = PG_GETARG_OID(1);
04535     text       *priv_type_text = PG_GETARG_TEXT_P(2);
04536     AclMode     mode;
04537     AclResult   aclresult;
04538 
04539     mode = convert_role_priv_string(priv_type_text);
04540 
04541     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
04542 
04543     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
04544 }
04545 
04546 /*
04547  *      Support routines for pg_has_role family.
04548  */
04549 
04550 /*
04551  * convert_role_priv_string
04552  *      Convert text string to AclMode value.
04553  *
04554  * We use USAGE to denote whether the privileges of the role are accessible
04555  * (has_privs), MEMBER to denote is_member, and MEMBER WITH GRANT OPTION
04556  * (or ADMIN OPTION) to denote is_admin.  There is no ACL bit corresponding
04557  * to MEMBER so we cheat and use ACL_CREATE for that.  This convention
04558  * is shared only with pg_role_aclcheck, below.
04559  */
04560 static AclMode
04561 convert_role_priv_string(text *priv_type_text)
04562 {
04563     static const priv_map role_priv_map[] = {
04564         {"USAGE", ACL_USAGE},
04565         {"MEMBER", ACL_CREATE},
04566         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
04567         {"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
04568         {"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
04569         {"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
04570         {NULL, 0}
04571     };
04572 
04573     return convert_any_priv_string(priv_type_text, role_priv_map);
04574 }
04575 
04576 /*
04577  * pg_role_aclcheck
04578  *      Quick-and-dirty support for pg_has_role
04579  */
04580 static AclResult
04581 pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
04582 {
04583     if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
04584     {
04585         if (is_admin_of_role(roleid, role_oid))
04586             return ACLCHECK_OK;
04587     }
04588     if (mode & ACL_CREATE)
04589     {
04590         if (is_member_of_role(roleid, role_oid))
04591             return ACLCHECK_OK;
04592     }
04593     if (mode & ACL_USAGE)
04594     {
04595         if (has_privs_of_role(roleid, role_oid))
04596             return ACLCHECK_OK;
04597     }
04598     return ACLCHECK_NO_PRIV;
04599 }
04600 
04601 
04602 /*
04603  * initialization function (called by InitPostgres)
04604  */
04605 void
04606 initialize_acl(void)
04607 {
04608     if (!IsBootstrapProcessingMode())
04609     {
04610         /*
04611          * In normal mode, set a callback on any syscache invalidation of
04612          * pg_auth_members rows
04613          */
04614         CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
04615                                       RoleMembershipCacheCallback,
04616                                       (Datum) 0);
04617     }
04618 }
04619 
04620 /*
04621  * RoleMembershipCacheCallback
04622  *      Syscache inval callback function
04623  */
04624 static void
04625 RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
04626 {
04627     /* Force membership caches to be recomputed on next use */
04628     cached_privs_role = InvalidOid;
04629     cached_member_role = InvalidOid;
04630 }
04631 
04632 
04633 /* Check if specified role has rolinherit set */
04634 static bool
04635 has_rolinherit(Oid roleid)
04636 {
04637     bool        result = false;
04638     HeapTuple   utup;
04639 
04640     utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
04641     if (HeapTupleIsValid(utup))
04642     {
04643         result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
04644         ReleaseSysCache(utup);
04645     }
04646     return result;
04647 }
04648 
04649 
04650 /*
04651  * Get a list of roles that the specified roleid has the privileges of
04652  *
04653  * This is defined not to recurse through roles that don't have rolinherit
04654  * set; for such roles, membership implies the ability to do SET ROLE, but
04655  * the privileges are not available until you've done so.
04656  *
04657  * Since indirect membership testing is relatively expensive, we cache
04658  * a list of memberships.  Hence, the result is only guaranteed good until
04659  * the next call of roles_has_privs_of()!
04660  *
04661  * For the benefit of select_best_grantor, the result is defined to be
04662  * in breadth-first order, ie, closer relationships earlier.
04663  */
04664 static List *
04665 roles_has_privs_of(Oid roleid)
04666 {
04667     List       *roles_list;
04668     ListCell   *l;
04669     List       *new_cached_privs_roles;
04670     MemoryContext oldctx;
04671 
04672     /* If cache is already valid, just return the list */
04673     if (OidIsValid(cached_privs_role) && cached_privs_role == roleid)
04674         return cached_privs_roles;
04675 
04676     /*
04677      * Find all the roles that roleid is a member of, including multi-level
04678      * recursion.  The role itself will always be the first element of the
04679      * resulting list.
04680      *
04681      * Each element of the list is scanned to see if it adds any indirect
04682      * memberships.  We can use a single list as both the record of
04683      * already-found memberships and the agenda of roles yet to be scanned.
04684      * This is a bit tricky but works because the foreach() macro doesn't
04685      * fetch the next list element until the bottom of the loop.
04686      */
04687     roles_list = list_make1_oid(roleid);
04688 
04689     foreach(l, roles_list)
04690     {
04691         Oid         memberid = lfirst_oid(l);
04692         CatCList   *memlist;
04693         int         i;
04694 
04695         /* Ignore non-inheriting roles */
04696         if (!has_rolinherit(memberid))
04697             continue;
04698 
04699         /* Find roles that memberid is directly a member of */
04700         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
04701                                       ObjectIdGetDatum(memberid));
04702         for (i = 0; i < memlist->n_members; i++)
04703         {
04704             HeapTuple   tup = &memlist->members[i]->tuple;
04705             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
04706 
04707             /*
04708              * Even though there shouldn't be any loops in the membership
04709              * graph, we must test for having already seen this role. It is
04710              * legal for instance to have both A->B and A->C->B.
04711              */
04712             roles_list = list_append_unique_oid(roles_list, otherid);
04713         }
04714         ReleaseSysCacheList(memlist);
04715     }
04716 
04717     /*
04718      * Copy the completed list into TopMemoryContext so it will persist.
04719      */
04720     oldctx = MemoryContextSwitchTo(TopMemoryContext);
04721     new_cached_privs_roles = list_copy(roles_list);
04722     MemoryContextSwitchTo(oldctx);
04723     list_free(roles_list);
04724 
04725     /*
04726      * Now safe to assign to state variable
04727      */
04728     cached_privs_role = InvalidOid;     /* just paranoia */
04729     list_free(cached_privs_roles);
04730     cached_privs_roles = new_cached_privs_roles;
04731     cached_privs_role = roleid;
04732 
04733     /* And now we can return the answer */
04734     return cached_privs_roles;
04735 }
04736 
04737 
04738 /*
04739  * Get a list of roles that the specified roleid is a member of
04740  *
04741  * This is defined to recurse through roles regardless of rolinherit.
04742  *
04743  * Since indirect membership testing is relatively expensive, we cache
04744  * a list of memberships.  Hence, the result is only guaranteed good until
04745  * the next call of roles_is_member_of()!
04746  */
04747 static List *
04748 roles_is_member_of(Oid roleid)
04749 {
04750     List       *roles_list;
04751     ListCell   *l;
04752     List       *new_cached_membership_roles;
04753     MemoryContext oldctx;
04754 
04755     /* If cache is already valid, just return the list */
04756     if (OidIsValid(cached_member_role) && cached_member_role == roleid)
04757         return cached_membership_roles;
04758 
04759     /*
04760      * Find all the roles that roleid is a member of, including multi-level
04761      * recursion.  The role itself will always be the first element of the
04762      * resulting list.
04763      *
04764      * Each element of the list is scanned to see if it adds any indirect
04765      * memberships.  We can use a single list as both the record of
04766      * already-found memberships and the agenda of roles yet to be scanned.
04767      * This is a bit tricky but works because the foreach() macro doesn't
04768      * fetch the next list element until the bottom of the loop.
04769      */
04770     roles_list = list_make1_oid(roleid);
04771 
04772     foreach(l, roles_list)
04773     {
04774         Oid         memberid = lfirst_oid(l);
04775         CatCList   *memlist;
04776         int         i;
04777 
04778         /* Find roles that memberid is directly a member of */
04779         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
04780                                       ObjectIdGetDatum(memberid));
04781         for (i = 0; i < memlist->n_members; i++)
04782         {
04783             HeapTuple   tup = &memlist->members[i]->tuple;
04784             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
04785 
04786             /*
04787              * Even though there shouldn't be any loops in the membership
04788              * graph, we must test for having already seen this role. It is
04789              * legal for instance to have both A->B and A->C->B.
04790              */
04791             roles_list = list_append_unique_oid(roles_list, otherid);
04792         }
04793         ReleaseSysCacheList(memlist);
04794     }
04795 
04796     /*
04797      * Copy the completed list into TopMemoryContext so it will persist.
04798      */
04799     oldctx = MemoryContextSwitchTo(TopMemoryContext);
04800     new_cached_membership_roles = list_copy(roles_list);
04801     MemoryContextSwitchTo(oldctx);
04802     list_free(roles_list);
04803 
04804     /*
04805      * Now safe to assign to state variable
04806      */
04807     cached_member_role = InvalidOid;    /* just paranoia */
04808     list_free(cached_membership_roles);
04809     cached_membership_roles = new_cached_membership_roles;
04810     cached_member_role = roleid;
04811 
04812     /* And now we can return the answer */
04813     return cached_membership_roles;
04814 }
04815 
04816 
04817 /*
04818  * Does member have the privileges of role (directly or indirectly)?
04819  *
04820  * This is defined not to recurse through roles that don't have rolinherit
04821  * set; for such roles, membership implies the ability to do SET ROLE, but
04822  * the privileges are not available until you've done so.
04823  */
04824 bool
04825 has_privs_of_role(Oid member, Oid role)
04826 {
04827     /* Fast path for simple case */
04828     if (member == role)
04829         return true;
04830 
04831     /* Superusers have every privilege, so are part of every role */
04832     if (superuser_arg(member))
04833         return true;
04834 
04835     /*
04836      * Find all the roles that member has the privileges of, including
04837      * multi-level recursion, then see if target role is any one of them.
04838      */
04839     return list_member_oid(roles_has_privs_of(member), role);
04840 }
04841 
04842 
04843 /*
04844  * Is member a member of role (directly or indirectly)?
04845  *
04846  * This is defined to recurse through roles regardless of rolinherit.
04847  */
04848 bool
04849 is_member_of_role(Oid member, Oid role)
04850 {
04851     /* Fast path for simple case */
04852     if (member == role)
04853         return true;
04854 
04855     /* Superusers have every privilege, so are part of every role */
04856     if (superuser_arg(member))
04857         return true;
04858 
04859     /*
04860      * Find all the roles that member is a member of, including multi-level
04861      * recursion, then see if target role is any one of them.
04862      */
04863     return list_member_oid(roles_is_member_of(member), role);
04864 }
04865 
04866 /*
04867  * check_is_member_of_role
04868  *      is_member_of_role with a standard permission-violation error if not
04869  */
04870 void
04871 check_is_member_of_role(Oid member, Oid role)
04872 {
04873     if (!is_member_of_role(member, role))
04874         ereport(ERROR,
04875                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
04876                  errmsg("must be member of role \"%s\"",
04877                         GetUserNameFromId(role))));
04878 }
04879 
04880 /*
04881  * Is member a member of role, not considering superuserness?
04882  *
04883  * This is identical to is_member_of_role except we ignore superuser
04884  * status.
04885  */
04886 bool
04887 is_member_of_role_nosuper(Oid member, Oid role)
04888 {
04889     /* Fast path for simple case */
04890     if (member == role)
04891         return true;
04892 
04893     /*
04894      * Find all the roles that member is a member of, including multi-level
04895      * recursion, then see if target role is any one of them.
04896      */
04897     return list_member_oid(roles_is_member_of(member), role);
04898 }
04899 
04900 
04901 /*
04902  * Is member an admin of role (directly or indirectly)?  That is, is it
04903  * a member WITH ADMIN OPTION?
04904  *
04905  * We could cache the result as for is_member_of_role, but currently this
04906  * is not used in any performance-critical paths, so we don't.
04907  */
04908 bool
04909 is_admin_of_role(Oid member, Oid role)
04910 {
04911     bool        result = false;
04912     List       *roles_list;
04913     ListCell   *l;
04914 
04915     /* Fast path for simple case */
04916     if (member == role)
04917         return true;
04918 
04919     /* Superusers have every privilege, so are part of every role */
04920     if (superuser_arg(member))
04921         return true;
04922 
04923     /*
04924      * Find all the roles that member is a member of, including multi-level
04925      * recursion.  We build a list in the same way that is_member_of_role does
04926      * to track visited and unvisited roles.
04927      */
04928     roles_list = list_make1_oid(member);
04929 
04930     foreach(l, roles_list)
04931     {
04932         Oid         memberid = lfirst_oid(l);
04933         CatCList   *memlist;
04934         int         i;
04935 
04936         /* Find roles that memberid is directly a member of */
04937         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
04938                                       ObjectIdGetDatum(memberid));
04939         for (i = 0; i < memlist->n_members; i++)
04940         {
04941             HeapTuple   tup = &memlist->members[i]->tuple;
04942             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
04943 
04944             if (otherid == role &&
04945                 ((Form_pg_auth_members) GETSTRUCT(tup))->admin_option)
04946             {
04947                 /* Found what we came for, so can stop searching */
04948                 result = true;
04949                 break;
04950             }
04951 
04952             roles_list = list_append_unique_oid(roles_list, otherid);
04953         }
04954         ReleaseSysCacheList(memlist);
04955         if (result)
04956             break;
04957     }
04958 
04959     list_free(roles_list);
04960 
04961     return result;
04962 }
04963 
04964 
04965 /* does what it says ... */
04966 static int
04967 count_one_bits(AclMode mask)
04968 {
04969     int         nbits = 0;
04970 
04971     /* this code relies on AclMode being an unsigned type */
04972     while (mask)
04973     {
04974         if (mask & 1)
04975             nbits++;
04976         mask >>= 1;
04977     }
04978     return nbits;
04979 }
04980 
04981 
04982 /*
04983  * Select the effective grantor ID for a GRANT or REVOKE operation.
04984  *
04985  * The grantor must always be either the object owner or some role that has
04986  * been explicitly granted grant options.  This ensures that all granted
04987  * privileges appear to flow from the object owner, and there are never
04988  * multiple "original sources" of a privilege.  Therefore, if the would-be
04989  * grantor is a member of a role that has the needed grant options, we have
04990  * to do the grant as that role instead.
04991  *
04992  * It is possible that the would-be grantor is a member of several roles
04993  * that have different subsets of the desired grant options, but no one
04994  * role has 'em all.  In this case we pick a role with the largest number
04995  * of desired options.  Ties are broken in favor of closer ancestors.
04996  *
04997  * roleId: the role attempting to do the GRANT/REVOKE
04998  * privileges: the privileges to be granted/revoked
04999  * acl: the ACL of the object in question
05000  * ownerId: the role owning the object in question
05001  * *grantorId: receives the OID of the role to do the grant as
05002  * *grantOptions: receives the grant options actually held by grantorId
05003  *
05004  * If no grant options exist, we set grantorId to roleId, grantOptions to 0.
05005  */
05006 void
05007 select_best_grantor(Oid roleId, AclMode privileges,
05008                     const Acl *acl, Oid ownerId,
05009                     Oid *grantorId, AclMode *grantOptions)
05010 {
05011     AclMode     needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
05012     List       *roles_list;
05013     int         nrights;
05014     ListCell   *l;
05015 
05016     /*
05017      * The object owner is always treated as having all grant options, so if
05018      * roleId is the owner it's easy.  Also, if roleId is a superuser it's
05019      * easy: superusers are implicitly members of every role, so they act as
05020      * the object owner.
05021      */
05022     if (roleId == ownerId || superuser_arg(roleId))
05023     {
05024         *grantorId = ownerId;
05025         *grantOptions = needed_goptions;
05026         return;
05027     }
05028 
05029     /*
05030      * Otherwise we have to do a careful search to see if roleId has the
05031      * privileges of any suitable role.  Note: we can hang onto the result of
05032      * roles_has_privs_of() throughout this loop, because aclmask_direct()
05033      * doesn't query any role memberships.
05034      */
05035     roles_list = roles_has_privs_of(roleId);
05036 
05037     /* initialize candidate result as default */
05038     *grantorId = roleId;
05039     *grantOptions = ACL_NO_RIGHTS;
05040     nrights = 0;
05041 
05042     foreach(l, roles_list)
05043     {
05044         Oid         otherrole = lfirst_oid(l);
05045         AclMode     otherprivs;
05046 
05047         otherprivs = aclmask_direct(acl, otherrole, ownerId,
05048                                     needed_goptions, ACLMASK_ALL);
05049         if (otherprivs == needed_goptions)
05050         {
05051             /* Found a suitable grantor */
05052             *grantorId = otherrole;
05053             *grantOptions = otherprivs;
05054             return;
05055         }
05056 
05057         /*
05058          * If it has just some of the needed privileges, remember best
05059          * candidate.
05060          */
05061         if (otherprivs != ACL_NO_RIGHTS)
05062         {
05063             int         nnewrights = count_one_bits(otherprivs);
05064 
05065             if (nnewrights > nrights)
05066             {
05067                 *grantorId = otherrole;
05068                 *grantOptions = otherprivs;
05069                 nrights = nnewrights;
05070             }
05071         }
05072     }
05073 }
05074 
05075 /*
05076  * get_role_oid - Given a role name, look up the role's OID.
05077  *
05078  * If missing_ok is false, throw an error if tablespace name not found.  If
05079  * true, just return InvalidOid.
05080  */
05081 Oid
05082 get_role_oid(const char *rolname, bool missing_ok)
05083 {
05084     Oid         oid;
05085 
05086     oid = GetSysCacheOid1(AUTHNAME, CStringGetDatum(rolname));
05087     if (!OidIsValid(oid) && !missing_ok)
05088         ereport(ERROR,
05089                 (errcode(ERRCODE_UNDEFINED_OBJECT),
05090                  errmsg("role \"%s\" does not exist", rolname)));
05091     return oid;
05092 }
05093 
05094 /*
05095  * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
05096  *      role name is "public".
05097  */
05098 static Oid
05099 get_role_oid_or_public(const char *rolname)
05100 {
05101     if (strcmp(rolname, "public") == 0)
05102         return ACL_ID_PUBLIC;
05103 
05104     return get_role_oid(rolname, false);
05105 }