LCOV - code coverage report
Current view: top level - src/backend/utils/adt - acl.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 924 1708 54.1 %
Date: 2017-09-29 13:40:31 Functions: 92 161 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * acl.c
       4             :  *    Basic access control list data structures manipulation routines.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/acl.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include <ctype.h>
      18             : 
      19             : #include "access/hash.h"
      20             : #include "access/htup_details.h"
      21             : #include "catalog/catalog.h"
      22             : #include "catalog/namespace.h"
      23             : #include "catalog/pg_authid.h"
      24             : #include "catalog/pg_auth_members.h"
      25             : #include "catalog/pg_type.h"
      26             : #include "catalog/pg_class.h"
      27             : #include "commands/dbcommands.h"
      28             : #include "commands/proclang.h"
      29             : #include "commands/tablespace.h"
      30             : #include "foreign/foreign.h"
      31             : #include "funcapi.h"
      32             : #include "miscadmin.h"
      33             : #include "utils/acl.h"
      34             : #include "utils/builtins.h"
      35             : #include "utils/catcache.h"
      36             : #include "utils/inval.h"
      37             : #include "utils/lsyscache.h"
      38             : #include "utils/memutils.h"
      39             : #include "utils/syscache.h"
      40             : #include "utils/varlena.h"
      41             : 
      42             : 
      43             : typedef struct
      44             : {
      45             :     const char *name;
      46             :     AclMode     value;
      47             : } priv_map;
      48             : 
      49             : /*
      50             :  * We frequently need to test whether a given role is a member of some other
      51             :  * role.  In most of these tests the "given role" is the same, namely the
      52             :  * active current user.  So we can optimize it by keeping a cached list of
      53             :  * all the roles the "given role" is a member of, directly or indirectly.
      54             :  * The cache is flushed whenever we detect a change in pg_auth_members.
      55             :  *
      56             :  * There are actually two caches, one computed under "has_privs" rules
      57             :  * (do not recurse where rolinherit isn't true) and one computed under
      58             :  * "is_member" rules (recurse regardless of rolinherit).
      59             :  *
      60             :  * Possibly this mechanism should be generalized to allow caching membership
      61             :  * info for multiple roles?
      62             :  *
      63             :  * The has_privs cache is:
      64             :  * cached_privs_role is the role OID the cache is for.
      65             :  * cached_privs_roles is an OID list of roles that cached_privs_role
      66             :  *      has the privileges of (always including itself).
      67             :  * The cache is valid if cached_privs_role is not InvalidOid.
      68             :  *
      69             :  * The is_member cache is similarly:
      70             :  * cached_member_role is the role OID the cache is for.
      71             :  * cached_membership_roles is an OID list of roles that cached_member_role
      72             :  *      is a member of (always including itself).
      73             :  * The cache is valid if cached_member_role is not InvalidOid.
      74             :  */
      75             : static Oid  cached_privs_role = InvalidOid;
      76             : static List *cached_privs_roles = NIL;
      77             : static Oid  cached_member_role = InvalidOid;
      78             : static List *cached_membership_roles = NIL;
      79             : 
      80             : 
      81             : static const char *getid(const char *s, char *n);
      82             : static void putid(char *p, const char *s);
      83             : static Acl *allocacl(int n);
      84             : static void check_acl(const Acl *acl);
      85             : static const char *aclparse(const char *s, AclItem *aip);
      86             : static bool aclitem_match(const AclItem *a1, const AclItem *a2);
      87             : static int  aclitemComparator(const void *arg1, const void *arg2);
      88             : static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
      89             :                   Oid ownerId);
      90             : static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
      91             :                  Oid ownerId, DropBehavior behavior);
      92             : 
      93             : static AclMode convert_priv_string(text *priv_type_text);
      94             : static AclMode convert_any_priv_string(text *priv_type_text,
      95             :                         const priv_map *privileges);
      96             : 
      97             : static Oid  convert_table_name(text *tablename);
      98             : static AclMode convert_table_priv_string(text *priv_type_text);
      99             : static AclMode convert_sequence_priv_string(text *priv_type_text);
     100             : static AttrNumber convert_column_name(Oid tableoid, text *column);
     101             : static AclMode convert_column_priv_string(text *priv_type_text);
     102             : static Oid  convert_database_name(text *databasename);
     103             : static AclMode convert_database_priv_string(text *priv_type_text);
     104             : static Oid  convert_foreign_data_wrapper_name(text *fdwname);
     105             : static AclMode convert_foreign_data_wrapper_priv_string(text *priv_type_text);
     106             : static Oid  convert_function_name(text *functionname);
     107             : static AclMode convert_function_priv_string(text *priv_type_text);
     108             : static Oid  convert_language_name(text *languagename);
     109             : static AclMode convert_language_priv_string(text *priv_type_text);
     110             : static Oid  convert_schema_name(text *schemaname);
     111             : static AclMode convert_schema_priv_string(text *priv_type_text);
     112             : static Oid  convert_server_name(text *servername);
     113             : static AclMode convert_server_priv_string(text *priv_type_text);
     114             : static Oid  convert_tablespace_name(text *tablespacename);
     115             : static AclMode convert_tablespace_priv_string(text *priv_type_text);
     116             : static Oid  convert_type_name(text *typename);
     117             : static AclMode convert_type_priv_string(text *priv_type_text);
     118             : static AclMode convert_role_priv_string(text *priv_type_text);
     119             : static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
     120             : 
     121             : static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
     122             : 
     123             : 
     124             : /*
     125             :  * getid
     126             :  *      Consumes the first alphanumeric string (identifier) found in string
     127             :  *      's', ignoring any leading white space.  If it finds a double quote
     128             :  *      it returns the word inside the quotes.
     129             :  *
     130             :  * RETURNS:
     131             :  *      the string position in 's' that points to the next non-space character
     132             :  *      in 's', after any quotes.  Also:
     133             :  *      - loads the identifier into 'n'.  (If no identifier is found, 'n'
     134             :  *        contains an empty string.)  'n' must be NAMEDATALEN bytes.
     135             :  */
     136             : static const char *
     137           2 : getid(const char *s, char *n)
     138             : {
     139           2 :     int         len = 0;
     140           2 :     bool        in_quotes = false;
     141             : 
     142           2 :     Assert(s && n);
     143             : 
     144           4 :     while (isspace((unsigned char) *s))
     145           0 :         s++;
     146             :     /* This code had better match what putid() does, below */
     147          13 :     for (;
     148          21 :          *s != '\0' &&
     149          13 :          (isalnum((unsigned char) *s) ||
     150           6 :           *s == '_' ||
     151           4 :           *s == '"' ||
     152             :           in_quotes);
     153           9 :          s++)
     154             :     {
     155           9 :         if (*s == '"')
     156             :         {
     157             :             /* safe to look at next char (could be '\0' though) */
     158           2 :             if (*(s + 1) != '"')
     159             :             {
     160           2 :                 in_quotes = !in_quotes;
     161           2 :                 continue;
     162             :             }
     163             :             /* it's an escaped double quote; skip the escaping char */
     164           0 :             s++;
     165             :         }
     166             : 
     167             :         /* Add the character to the string */
     168           7 :         if (len >= NAMEDATALEN - 1)
     169           0 :             ereport(ERROR,
     170             :                     (errcode(ERRCODE_NAME_TOO_LONG),
     171             :                      errmsg("identifier too long"),
     172             :                      errdetail("Identifier must be less than %d characters.",
     173             :                                NAMEDATALEN)));
     174             : 
     175           7 :         n[len++] = *s;
     176             :     }
     177           2 :     n[len] = '\0';
     178           4 :     while (isspace((unsigned char) *s))
     179           0 :         s++;
     180           2 :     return s;
     181             : }
     182             : 
     183             : /*
     184             :  * Write a role name at *p, adding double quotes if needed.
     185             :  * There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
     186             :  * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c
     187             :  */
     188             : static void
     189         176 : putid(char *p, const char *s)
     190             : {
     191             :     const char *src;
     192         176 :     bool        safe = true;
     193             : 
     194        2909 :     for (src = s; *src; src++)
     195             :     {
     196             :         /* This test had better match what getid() does, above */
     197        2733 :         if (!isalnum((unsigned char) *src) && *src != '_')
     198             :         {
     199           0 :             safe = false;
     200           0 :             break;
     201             :         }
     202             :     }
     203         176 :     if (!safe)
     204           0 :         *p++ = '"';
     205        2909 :     for (src = s; *src; src++)
     206             :     {
     207             :         /* A double quote character in a username is encoded as "" */
     208        2733 :         if (*src == '"')
     209           0 :             *p++ = '"';
     210        2733 :         *p++ = *src;
     211             :     }
     212         176 :     if (!safe)
     213           0 :         *p++ = '"';
     214         176 :     *p = '\0';
     215         176 : }
     216             : 
     217             : /*
     218             :  * aclparse
     219             :  *      Consumes and parses an ACL specification of the form:
     220             :  *              [group|user] [A-Za-z0-9]*=[rwaR]*
     221             :  *      from string 's', ignoring any leading white space or white space
     222             :  *      between the optional id type keyword (group|user) and the actual
     223             :  *      ACL specification.
     224             :  *
     225             :  *      The group|user decoration is unnecessary in the roles world,
     226             :  *      but we still accept it for backward compatibility.
     227             :  *
     228             :  *      This routine is called by the parser as well as aclitemin(), hence
     229             :  *      the added generality.
     230             :  *
     231             :  * RETURNS:
     232             :  *      the string position in 's' immediately following the ACL
     233             :  *      specification.  Also:
     234             :  *      - loads the structure pointed to by 'aip' with the appropriate
     235             :  *        UID/GID, id type identifier and mode type values.
     236             :  */
     237             : static const char *
     238           1 : aclparse(const char *s, AclItem *aip)
     239             : {
     240             :     AclMode     privs,
     241             :                 goption,
     242             :                 read;
     243             :     char        name[NAMEDATALEN];
     244             :     char        name2[NAMEDATALEN];
     245             : 
     246           1 :     Assert(s && aip);
     247             : 
     248             : #ifdef ACLDEBUG
     249             :     elog(LOG, "aclparse: input = \"%s\"", s);
     250             : #endif
     251           1 :     s = getid(s, name);
     252           1 :     if (*s != '=')
     253             :     {
     254             :         /* we just read a keyword, not a name */
     255           0 :         if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
     256           0 :             ereport(ERROR,
     257             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     258             :                      errmsg("unrecognized key word: \"%s\"", name),
     259             :                      errhint("ACL key word must be \"group\" or \"user\".")));
     260           0 :         s = getid(s, name);     /* move s to the name beyond the keyword */
     261           0 :         if (name[0] == '\0')
     262           0 :             ereport(ERROR,
     263             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     264             :                      errmsg("missing name"),
     265             :                      errhint("A name must follow the \"group\" or \"user\" key word.")));
     266             :     }
     267             : 
     268           1 :     if (*s != '=')
     269           0 :         ereport(ERROR,
     270             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     271             :                  errmsg("missing \"=\" sign")));
     272             : 
     273           1 :     privs = goption = ACL_NO_RIGHTS;
     274             : 
     275           2 :     for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
     276             :     {
     277           1 :         switch (*s)
     278             :         {
     279             :             case '*':
     280           0 :                 goption |= read;
     281           0 :                 break;
     282             :             case ACL_INSERT_CHR:
     283           0 :                 read = ACL_INSERT;
     284           0 :                 break;
     285             :             case ACL_SELECT_CHR:
     286           1 :                 read = ACL_SELECT;
     287           1 :                 break;
     288             :             case ACL_UPDATE_CHR:
     289           0 :                 read = ACL_UPDATE;
     290           0 :                 break;
     291             :             case ACL_DELETE_CHR:
     292           0 :                 read = ACL_DELETE;
     293           0 :                 break;
     294             :             case ACL_TRUNCATE_CHR:
     295           0 :                 read = ACL_TRUNCATE;
     296           0 :                 break;
     297             :             case ACL_REFERENCES_CHR:
     298           0 :                 read = ACL_REFERENCES;
     299           0 :                 break;
     300             :             case ACL_TRIGGER_CHR:
     301           0 :                 read = ACL_TRIGGER;
     302           0 :                 break;
     303             :             case ACL_EXECUTE_CHR:
     304           0 :                 read = ACL_EXECUTE;
     305           0 :                 break;
     306             :             case ACL_USAGE_CHR:
     307           0 :                 read = ACL_USAGE;
     308           0 :                 break;
     309             :             case ACL_CREATE_CHR:
     310           0 :                 read = ACL_CREATE;
     311           0 :                 break;
     312             :             case ACL_CREATE_TEMP_CHR:
     313           0 :                 read = ACL_CREATE_TEMP;
     314           0 :                 break;
     315             :             case ACL_CONNECT_CHR:
     316           0 :                 read = ACL_CONNECT;
     317           0 :                 break;
     318             :             case 'R':           /* ignore old RULE privileges */
     319           0 :                 read = 0;
     320           0 :                 break;
     321             :             default:
     322           0 :                 ereport(ERROR,
     323             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     324             :                          errmsg("invalid mode character: must be one of \"%s\"",
     325             :                                 ACL_ALL_RIGHTS_STR)));
     326             :         }
     327             : 
     328           1 :         privs |= read;
     329             :     }
     330             : 
     331           1 :     if (name[0] == '\0')
     332           1 :         aip->ai_grantee = ACL_ID_PUBLIC;
     333             :     else
     334           0 :         aip->ai_grantee = get_role_oid(name, false);
     335             : 
     336             :     /*
     337             :      * XXX Allow a degree of backward compatibility by defaulting the grantor
     338             :      * to the superuser.
     339             :      */
     340           1 :     if (*s == '/')
     341             :     {
     342           1 :         s = getid(s + 1, name2);
     343           1 :         if (name2[0] == '\0')
     344           0 :             ereport(ERROR,
     345             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     346             :                      errmsg("a name must follow the \"/\" sign")));
     347           1 :         aip->ai_grantor = get_role_oid(name2, false);
     348             :     }
     349             :     else
     350             :     {
     351           0 :         aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
     352           0 :         ereport(WARNING,
     353             :                 (errcode(ERRCODE_INVALID_GRANTOR),
     354             :                  errmsg("defaulting grantor to user ID %u",
     355             :                         BOOTSTRAP_SUPERUSERID)));
     356             :     }
     357             : 
     358           1 :     ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
     359             : 
     360             : #ifdef ACLDEBUG
     361             :     elog(LOG, "aclparse: correctly read [%u %x %x]",
     362             :          aip->ai_grantee, privs, goption);
     363             : #endif
     364             : 
     365           1 :     return s;
     366             : }
     367             : 
     368             : /*
     369             :  * allocacl
     370             :  *      Allocates storage for a new Acl with 'n' entries.
     371             :  *
     372             :  * RETURNS:
     373             :  *      the new Acl
     374             :  */
     375             : static Acl *
     376        5352 : allocacl(int n)
     377             : {
     378             :     Acl        *new_acl;
     379             :     Size        size;
     380             : 
     381        5352 :     if (n < 0)
     382           0 :         elog(ERROR, "invalid size: %d", n);
     383        5352 :     size = ACL_N_SIZE(n);
     384        5352 :     new_acl = (Acl *) palloc0(size);
     385        5352 :     SET_VARSIZE(new_acl, size);
     386        5352 :     new_acl->ndim = 1;
     387        5352 :     new_acl->dataoffset = 0; /* we never put in any nulls */
     388        5352 :     new_acl->elemtype = ACLITEMOID;
     389        5352 :     ARR_LBOUND(new_acl)[0] = 1;
     390        5352 :     ARR_DIMS(new_acl)[0] = n;
     391        5352 :     return new_acl;
     392             : }
     393             : 
     394             : /*
     395             :  * Create a zero-entry ACL
     396             :  */
     397             : Acl *
     398           8 : make_empty_acl(void)
     399             : {
     400           8 :     return allocacl(0);
     401             : }
     402             : 
     403             : /*
     404             :  * Copy an ACL
     405             :  */
     406             : Acl *
     407         269 : aclcopy(const Acl *orig_acl)
     408             : {
     409             :     Acl        *result_acl;
     410             : 
     411         269 :     result_acl = allocacl(ACL_NUM(orig_acl));
     412             : 
     413        1076 :     memcpy(ACL_DAT(result_acl),
     414         807 :            ACL_DAT(orig_acl),
     415         269 :            ACL_NUM(orig_acl) * sizeof(AclItem));
     416             : 
     417         269 :     return result_acl;
     418             : }
     419             : 
     420             : /*
     421             :  * Concatenate two ACLs
     422             :  *
     423             :  * This is a bit cheesy, since we may produce an ACL with redundant entries.
     424             :  * Be careful what the result is used for!
     425             :  */
     426             : Acl *
     427         444 : aclconcat(const Acl *left_acl, const Acl *right_acl)
     428             : {
     429             :     Acl        *result_acl;
     430             : 
     431         444 :     result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
     432             : 
     433        1776 :     memcpy(ACL_DAT(result_acl),
     434        1332 :            ACL_DAT(left_acl),
     435         444 :            ACL_NUM(left_acl) * sizeof(AclItem));
     436             : 
     437        1776 :     memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
     438        1332 :            ACL_DAT(right_acl),
     439         444 :            ACL_NUM(right_acl) * sizeof(AclItem));
     440             : 
     441         444 :     return result_acl;
     442             : }
     443             : 
     444             : /*
     445             :  * Merge two ACLs
     446             :  *
     447             :  * This produces a properly merged ACL with no redundant entries.
     448             :  * Returns NULL on NULL input.
     449             :  */
     450             : Acl *
     451          21 : aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
     452             : {
     453             :     Acl        *result_acl;
     454             :     AclItem    *aip;
     455             :     int         i,
     456             :                 num;
     457             : 
     458             :     /* Check for cases where one or both are empty/null */
     459          21 :     if (left_acl == NULL || ACL_NUM(left_acl) == 0)
     460             :     {
     461           0 :         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
     462           0 :             return NULL;
     463             :         else
     464           0 :             return aclcopy(right_acl);
     465             :     }
     466             :     else
     467             :     {
     468          21 :         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
     469          14 :             return aclcopy(left_acl);
     470             :     }
     471             : 
     472             :     /* Merge them the hard way, one item at a time */
     473           7 :     result_acl = aclcopy(left_acl);
     474             : 
     475           7 :     aip = ACL_DAT(right_acl);
     476           7 :     num = ACL_NUM(right_acl);
     477             : 
     478          17 :     for (i = 0; i < num; i++, aip++)
     479             :     {
     480             :         Acl        *tmp_acl;
     481             : 
     482          10 :         tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
     483             :                             ownerId, DROP_RESTRICT);
     484          10 :         pfree(result_acl);
     485          10 :         result_acl = tmp_acl;
     486             :     }
     487             : 
     488           7 :     return result_acl;
     489             : }
     490             : 
     491             : /*
     492             :  * Sort the items in an ACL (into an arbitrary but consistent order)
     493             :  */
     494             : void
     495          76 : aclitemsort(Acl *acl)
     496             : {
     497          76 :     if (acl != NULL && ACL_NUM(acl) > 1)
     498          18 :         qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
     499          76 : }
     500             : 
     501             : /*
     502             :  * Check if two ACLs are exactly equal
     503             :  *
     504             :  * This will not detect equality if the two arrays contain the same items
     505             :  * in different orders.  To handle that case, sort both inputs first,
     506             :  * using aclitemsort().
     507             :  */
     508             : bool
     509          38 : aclequal(const Acl *left_acl, const Acl *right_acl)
     510             : {
     511             :     /* Check for cases where one or both are empty/null */
     512          38 :     if (left_acl == NULL || ACL_NUM(left_acl) == 0)
     513             :     {
     514           0 :         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
     515           0 :             return true;
     516             :         else
     517           0 :             return false;
     518             :     }
     519             :     else
     520             :     {
     521          38 :         if (right_acl == NULL || ACL_NUM(right_acl) == 0)
     522           7 :             return false;
     523             :     }
     524             : 
     525          31 :     if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
     526          13 :         return false;
     527             : 
     528          54 :     if (memcmp(ACL_DAT(left_acl),
     529          36 :                ACL_DAT(right_acl),
     530          18 :                ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
     531           5 :         return true;
     532             : 
     533          13 :     return false;
     534             : }
     535             : 
     536             : /*
     537             :  * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
     538             :  */
     539             : static void
     540        7371 : check_acl(const Acl *acl)
     541             : {
     542        7371 :     if (ARR_ELEMTYPE(acl) != ACLITEMOID)
     543           0 :         ereport(ERROR,
     544             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     545             :                  errmsg("ACL array contains wrong data type")));
     546        7371 :     if (ARR_NDIM(acl) != 1)
     547           0 :         ereport(ERROR,
     548             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     549             :                  errmsg("ACL arrays must be one-dimensional")));
     550        7371 :     if (ARR_HASNULL(acl))
     551           0 :         ereport(ERROR,
     552             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     553             :                  errmsg("ACL arrays must not contain null values")));
     554        7371 : }
     555             : 
     556             : /*
     557             :  * aclitemin
     558             :  *      Allocates storage for, and fills in, a new AclItem given a string
     559             :  *      's' that contains an ACL specification.  See aclparse for details.
     560             :  *
     561             :  * RETURNS:
     562             :  *      the new AclItem
     563             :  */
     564             : Datum
     565           1 : aclitemin(PG_FUNCTION_ARGS)
     566             : {
     567           1 :     const char *s = PG_GETARG_CSTRING(0);
     568             :     AclItem    *aip;
     569             : 
     570           1 :     aip = (AclItem *) palloc(sizeof(AclItem));
     571           1 :     s = aclparse(s, aip);
     572           2 :     while (isspace((unsigned char) *s))
     573           0 :         ++s;
     574           1 :     if (*s)
     575           0 :         ereport(ERROR,
     576             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     577             :                  errmsg("extra garbage at the end of the ACL specification")));
     578             : 
     579           1 :     PG_RETURN_ACLITEM_P(aip);
     580             : }
     581             : 
     582             : /*
     583             :  * aclitemout
     584             :  *      Allocates storage for, and fills in, a new null-delimited string
     585             :  *      containing a formatted ACL specification.  See aclparse for details.
     586             :  *
     587             :  * RETURNS:
     588             :  *      the new string
     589             :  */
     590             : Datum
     591          93 : aclitemout(PG_FUNCTION_ARGS)
     592             : {
     593          93 :     AclItem    *aip = PG_GETARG_ACLITEM_P(0);
     594             :     char       *p;
     595             :     char       *out;
     596             :     HeapTuple   htup;
     597             :     unsigned    i;
     598             : 
     599          93 :     out = palloc(strlen("=/") +
     600             :                  2 * N_ACL_RIGHTS +
     601             :                  2 * (2 * NAMEDATALEN + 2) +
     602             :                  1);
     603             : 
     604          93 :     p = out;
     605          93 :     *p = '\0';
     606             : 
     607          93 :     if (aip->ai_grantee != ACL_ID_PUBLIC)
     608             :     {
     609          83 :         htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee));
     610          83 :         if (HeapTupleIsValid(htup))
     611             :         {
     612          83 :             putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
     613          83 :             ReleaseSysCache(htup);
     614             :         }
     615             :         else
     616             :         {
     617             :             /* Generate numeric OID if we don't find an entry */
     618           0 :             sprintf(p, "%u", aip->ai_grantee);
     619             :         }
     620             :     }
     621        1458 :     while (*p)
     622        1272 :         ++p;
     623             : 
     624          93 :     *p++ = '=';
     625             : 
     626        1209 :     for (i = 0; i < N_ACL_RIGHTS; ++i)
     627             :     {
     628        1116 :         if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
     629         174 :             *p++ = ACL_ALL_RIGHTS_STR[i];
     630        1116 :         if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
     631          20 :             *p++ = '*';
     632             :     }
     633             : 
     634          93 :     *p++ = '/';
     635          93 :     *p = '\0';
     636             : 
     637          93 :     htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor));
     638          93 :     if (HeapTupleIsValid(htup))
     639             :     {
     640          93 :         putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
     641          93 :         ReleaseSysCache(htup);
     642             :     }
     643             :     else
     644             :     {
     645             :         /* Generate numeric OID if we don't find an entry */
     646           0 :         sprintf(p, "%u", aip->ai_grantor);
     647             :     }
     648             : 
     649          93 :     PG_RETURN_CSTRING(out);
     650             : }
     651             : 
     652             : /*
     653             :  * aclitem_match
     654             :  *      Two AclItems are considered to match iff they have the same
     655             :  *      grantee and grantor; the privileges are ignored.
     656             :  */
     657             : static bool
     658         544 : aclitem_match(const AclItem *a1, const AclItem *a2)
     659             : {
     660         691 :     return a1->ai_grantee == a2->ai_grantee &&
     661         147 :         a1->ai_grantor == a2->ai_grantor;
     662             : }
     663             : 
     664             : /*
     665             :  * aclitemComparator
     666             :  *      qsort comparison function for AclItems
     667             :  */
     668             : static int
     669          19 : aclitemComparator(const void *arg1, const void *arg2)
     670             : {
     671          19 :     const AclItem *a1 = (const AclItem *) arg1;
     672          19 :     const AclItem *a2 = (const AclItem *) arg2;
     673             : 
     674          19 :     if (a1->ai_grantee > a2->ai_grantee)
     675           5 :         return 1;
     676          14 :     if (a1->ai_grantee < a2->ai_grantee)
     677          14 :         return -1;
     678           0 :     if (a1->ai_grantor > a2->ai_grantor)
     679           0 :         return 1;
     680           0 :     if (a1->ai_grantor < a2->ai_grantor)
     681           0 :         return -1;
     682           0 :     if (a1->ai_privs > a2->ai_privs)
     683           0 :         return 1;
     684           0 :     if (a1->ai_privs < a2->ai_privs)
     685           0 :         return -1;
     686           0 :     return 0;
     687             : }
     688             : 
     689             : /*
     690             :  * aclitem equality operator
     691             :  */
     692             : Datum
     693        1207 : aclitem_eq(PG_FUNCTION_ARGS)
     694             : {
     695        1207 :     AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
     696        1207 :     AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
     697             :     bool        result;
     698             : 
     699        3579 :     result = a1->ai_privs == a2->ai_privs &&
     700        2372 :         a1->ai_grantee == a2->ai_grantee &&
     701        1165 :         a1->ai_grantor == a2->ai_grantor;
     702        1207 :     PG_RETURN_BOOL(result);
     703             : }
     704             : 
     705             : /*
     706             :  * aclitem hash function
     707             :  *
     708             :  * We make aclitems hashable not so much because anyone is likely to hash
     709             :  * them, as because we want array equality to work on aclitem arrays, and
     710             :  * with the typcache mechanism we must have a hash or btree opclass.
     711             :  */
     712             : Datum
     713         405 : hash_aclitem(PG_FUNCTION_ARGS)
     714             : {
     715         405 :     AclItem    *a = PG_GETARG_ACLITEM_P(0);
     716             : 
     717             :     /* not very bright, but avoids any issue of padding in struct */
     718         405 :     PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
     719             : }
     720             : 
     721             : /*
     722             :  * 64-bit hash function for aclitem.
     723             :  *
     724             :  * Similar to hash_aclitem, but accepts a seed and returns a uint64 value.
     725             :  */
     726             : Datum
     727           2 : hash_aclitem_extended(PG_FUNCTION_ARGS)
     728             : {
     729           2 :     AclItem    *a = PG_GETARG_ACLITEM_P(0);
     730           2 :     uint64      seed = PG_GETARG_INT64(1);
     731           2 :     uint32      sum = (uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor);
     732             : 
     733           2 :     return (seed == 0) ? UInt64GetDatum(sum) : hash_uint32_extended(sum, seed);
     734             : }
     735             : 
     736             : /*
     737             :  * acldefault()  --- create an ACL describing default access permissions
     738             :  *
     739             :  * Change this routine if you want to alter the default access policy for
     740             :  * newly-created objects (or any object with a NULL acl entry).
     741             :  *
     742             :  * Note that these are the hard-wired "defaults" that are used in the
     743             :  * absence of any pg_default_acl entry.
     744             :  */
     745             : Acl *
     746        3796 : acldefault(GrantObjectType objtype, Oid ownerId)
     747             : {
     748             :     AclMode     world_default;
     749             :     AclMode     owner_default;
     750             :     int         nacl;
     751             :     Acl        *acl;
     752             :     AclItem    *aip;
     753             : 
     754        3796 :     switch (objtype)
     755             :     {
     756             :         case ACL_OBJECT_COLUMN:
     757             :             /* by default, columns have no extra privileges */
     758         427 :             world_default = ACL_NO_RIGHTS;
     759         427 :             owner_default = ACL_NO_RIGHTS;
     760         427 :             break;
     761             :         case ACL_OBJECT_RELATION:
     762         573 :             world_default = ACL_NO_RIGHTS;
     763         573 :             owner_default = ACL_ALL_RIGHTS_RELATION;
     764         573 :             break;
     765             :         case ACL_OBJECT_SEQUENCE:
     766          17 :             world_default = ACL_NO_RIGHTS;
     767          17 :             owner_default = ACL_ALL_RIGHTS_SEQUENCE;
     768          17 :             break;
     769             :         case ACL_OBJECT_DATABASE:
     770             :             /* for backwards compatibility, grant some rights by default */
     771          14 :             world_default = ACL_CREATE_TEMP | ACL_CONNECT;
     772          14 :             owner_default = ACL_ALL_RIGHTS_DATABASE;
     773          14 :             break;
     774             :         case ACL_OBJECT_FUNCTION:
     775             :             /* Grant EXECUTE by default, for now */
     776        2292 :             world_default = ACL_EXECUTE;
     777        2292 :             owner_default = ACL_ALL_RIGHTS_FUNCTION;
     778        2292 :             break;
     779             :         case ACL_OBJECT_LANGUAGE:
     780             :             /* Grant USAGE by default, for now */
     781          10 :             world_default = ACL_USAGE;
     782          10 :             owner_default = ACL_ALL_RIGHTS_LANGUAGE;
     783          10 :             break;
     784             :         case ACL_OBJECT_LARGEOBJECT:
     785          12 :             world_default = ACL_NO_RIGHTS;
     786          12 :             owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
     787          12 :             break;
     788             :         case ACL_OBJECT_NAMESPACE:
     789          23 :             world_default = ACL_NO_RIGHTS;
     790          23 :             owner_default = ACL_ALL_RIGHTS_NAMESPACE;
     791          23 :             break;
     792             :         case ACL_OBJECT_TABLESPACE:
     793           1 :             world_default = ACL_NO_RIGHTS;
     794           1 :             owner_default = ACL_ALL_RIGHTS_TABLESPACE;
     795           1 :             break;
     796             :         case ACL_OBJECT_FDW:
     797           3 :             world_default = ACL_NO_RIGHTS;
     798           3 :             owner_default = ACL_ALL_RIGHTS_FDW;
     799           3 :             break;
     800             :         case ACL_OBJECT_FOREIGN_SERVER:
     801          21 :             world_default = ACL_NO_RIGHTS;
     802          21 :             owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
     803          21 :             break;
     804             :         case ACL_OBJECT_DOMAIN:
     805             :         case ACL_OBJECT_TYPE:
     806         403 :             world_default = ACL_USAGE;
     807         403 :             owner_default = ACL_ALL_RIGHTS_TYPE;
     808         403 :             break;
     809             :         default:
     810           0 :             elog(ERROR, "unrecognized objtype: %d", (int) objtype);
     811             :             world_default = ACL_NO_RIGHTS;  /* keep compiler quiet */
     812             :             owner_default = ACL_NO_RIGHTS;
     813             :             break;
     814             :     }
     815             : 
     816        3796 :     nacl = 0;
     817        3796 :     if (world_default != ACL_NO_RIGHTS)
     818        2719 :         nacl++;
     819        3796 :     if (owner_default != ACL_NO_RIGHTS)
     820        3369 :         nacl++;
     821             : 
     822        3796 :     acl = allocacl(nacl);
     823        3796 :     aip = ACL_DAT(acl);
     824             : 
     825        3796 :     if (world_default != ACL_NO_RIGHTS)
     826             :     {
     827        2719 :         aip->ai_grantee = ACL_ID_PUBLIC;
     828        2719 :         aip->ai_grantor = ownerId;
     829        2719 :         ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
     830        2719 :         aip++;
     831             :     }
     832             : 
     833             :     /*
     834             :      * Note that the owner's entry shows all ordinary privileges but no grant
     835             :      * options.  This is because his grant options come "from the system" and
     836             :      * not from his own efforts.  (The SQL spec says that the owner's rights
     837             :      * come from a "_SYSTEM" authid.)  However, we do consider that the
     838             :      * owner's ordinary privileges are self-granted; this lets him revoke
     839             :      * them.  We implement the owner's grant options without any explicit
     840             :      * "_SYSTEM"-like ACL entry, by internally special-casing the owner
     841             :      * wherever we are testing grant options.
     842             :      */
     843        3796 :     if (owner_default != ACL_NO_RIGHTS)
     844             :     {
     845        3369 :         aip->ai_grantee = ownerId;
     846        3369 :         aip->ai_grantor = ownerId;
     847        3369 :         ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
     848             :     }
     849             : 
     850        3796 :     return acl;
     851             : }
     852             : 
     853             : 
     854             : /*
     855             :  * SQL-accessible version of acldefault().  Hackish mapping from "char" type to
     856             :  * ACL_OBJECT_* values, but it's only used in the information schema, not
     857             :  * documented for general use.
     858             :  */
     859             : Datum
     860         111 : acldefault_sql(PG_FUNCTION_ARGS)
     861             : {
     862         111 :     char        objtypec = PG_GETARG_CHAR(0);
     863         111 :     Oid         owner = PG_GETARG_OID(1);
     864         111 :     GrantObjectType objtype = 0;
     865             : 
     866         111 :     switch (objtypec)
     867             :     {
     868             :         case 'c':
     869           0 :             objtype = ACL_OBJECT_COLUMN;
     870           0 :             break;
     871             :         case 'r':
     872         111 :             objtype = ACL_OBJECT_RELATION;
     873         111 :             break;
     874             :         case 's':
     875           0 :             objtype = ACL_OBJECT_SEQUENCE;
     876           0 :             break;
     877             :         case 'd':
     878           0 :             objtype = ACL_OBJECT_DATABASE;
     879           0 :             break;
     880             :         case 'f':
     881           0 :             objtype = ACL_OBJECT_FUNCTION;
     882           0 :             break;
     883             :         case 'l':
     884           0 :             objtype = ACL_OBJECT_LANGUAGE;
     885           0 :             break;
     886             :         case 'L':
     887           0 :             objtype = ACL_OBJECT_LARGEOBJECT;
     888           0 :             break;
     889             :         case 'n':
     890           0 :             objtype = ACL_OBJECT_NAMESPACE;
     891           0 :             break;
     892             :         case 't':
     893           0 :             objtype = ACL_OBJECT_TABLESPACE;
     894           0 :             break;
     895             :         case 'F':
     896           0 :             objtype = ACL_OBJECT_FDW;
     897           0 :             break;
     898             :         case 'S':
     899           0 :             objtype = ACL_OBJECT_FOREIGN_SERVER;
     900           0 :             break;
     901             :         case 'T':
     902           0 :             objtype = ACL_OBJECT_TYPE;
     903           0 :             break;
     904             :         default:
     905           0 :             elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
     906             :     }
     907             : 
     908         111 :     PG_RETURN_ACL_P(acldefault(objtype, owner));
     909             : }
     910             : 
     911             : 
     912             : /*
     913             :  * Update an ACL array to add or remove specified privileges.
     914             :  *
     915             :  *  old_acl: the input ACL array
     916             :  *  mod_aip: defines the privileges to be added, removed, or substituted
     917             :  *  modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
     918             :  *  ownerId: Oid of object owner
     919             :  *  behavior: RESTRICT or CASCADE behavior for recursive removal
     920             :  *
     921             :  * ownerid and behavior are only relevant when the update operation specifies
     922             :  * deletion of grant options.
     923             :  *
     924             :  * The result is a modified copy; the input object is not changed.
     925             :  *
     926             :  * NB: caller is responsible for having detoasted the input ACL, if needed.
     927             :  */
     928             : Acl *
     929         828 : aclupdate(const Acl *old_acl, const AclItem *mod_aip,
     930             :           int modechg, Oid ownerId, DropBehavior behavior)
     931             : {
     932         828 :     Acl        *new_acl = NULL;
     933             :     AclItem    *old_aip,
     934         828 :                *new_aip = NULL;
     935             :     AclMode     old_rights,
     936             :                 old_goptions,
     937             :                 new_rights,
     938             :                 new_goptions;
     939             :     int         dst,
     940             :                 num;
     941             : 
     942             :     /* Caller probably already checked old_acl, but be safe */
     943         828 :     check_acl(old_acl);
     944             : 
     945             :     /* If granting grant options, check for circularity */
     946        1124 :     if (modechg != ACL_MODECHG_DEL &&
     947         296 :         ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
     948          10 :         check_circularity(old_acl, mod_aip, ownerId);
     949             : 
     950         828 :     num = ACL_NUM(old_acl);
     951         828 :     old_aip = ACL_DAT(old_acl);
     952             : 
     953             :     /*
     954             :      * Search the ACL for an existing entry for this grantee and grantor. If
     955             :      * one exists, just modify the entry in-place (well, in the same position,
     956             :      * since we actually return a copy); otherwise, insert the new entry at
     957             :      * the end.
     958             :      */
     959             : 
     960        1229 :     for (dst = 0; dst < num; ++dst)
     961             :     {
     962         543 :         if (aclitem_match(mod_aip, old_aip + dst))
     963             :         {
     964             :             /* found a match, so modify existing item */
     965         142 :             new_acl = allocacl(num);
     966         142 :             new_aip = ACL_DAT(new_acl);
     967         142 :             memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
     968         142 :             break;
     969             :         }
     970             :     }
     971             : 
     972         828 :     if (dst == num)
     973             :     {
     974             :         /* need to append a new item */
     975         686 :         new_acl = allocacl(num + 1);
     976         686 :         new_aip = ACL_DAT(new_acl);
     977         686 :         memcpy(new_aip, old_aip, num * sizeof(AclItem));
     978             : 
     979             :         /* initialize the new entry with no permissions */
     980         686 :         new_aip[dst].ai_grantee = mod_aip->ai_grantee;
     981         686 :         new_aip[dst].ai_grantor = mod_aip->ai_grantor;
     982         686 :         ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
     983             :                                    ACL_NO_RIGHTS, ACL_NO_RIGHTS);
     984         686 :         num++;                  /* set num to the size of new_acl */
     985             :     }
     986             : 
     987         828 :     old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
     988         828 :     old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
     989             : 
     990             :     /* apply the specified permissions change */
     991         828 :     switch (modechg)
     992             :     {
     993             :         case ACL_MODECHG_ADD:
     994         296 :             ACLITEM_SET_RIGHTS(new_aip[dst],
     995             :                                old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
     996         296 :             break;
     997             :         case ACL_MODECHG_DEL:
     998         532 :             ACLITEM_SET_RIGHTS(new_aip[dst],
     999             :                                old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
    1000         532 :             break;
    1001             :         case ACL_MODECHG_EQL:
    1002           0 :             ACLITEM_SET_RIGHTS(new_aip[dst],
    1003             :                                ACLITEM_GET_RIGHTS(*mod_aip));
    1004           0 :             break;
    1005             :     }
    1006             : 
    1007         828 :     new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
    1008         828 :     new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
    1009             : 
    1010             :     /*
    1011             :      * If the adjusted entry has no permissions, delete it from the list.
    1012             :      */
    1013         828 :     if (new_rights == ACL_NO_RIGHTS)
    1014             :     {
    1015        1572 :         memmove(new_aip + dst,
    1016        1048 :                 new_aip + dst + 1,
    1017         524 :                 (num - dst - 1) * sizeof(AclItem));
    1018             :         /* Adjust array size to be 'num - 1' items */
    1019         524 :         ARR_DIMS(new_acl)[0] = num - 1;
    1020         524 :         SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
    1021             :     }
    1022             : 
    1023             :     /*
    1024             :      * Remove abandoned privileges (cascading revoke).  Currently we can only
    1025             :      * handle this when the grantee is not PUBLIC.
    1026             :      */
    1027         828 :     if ((old_goptions & ~new_goptions) != 0)
    1028             :     {
    1029          10 :         Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
    1030          10 :         new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
    1031          10 :                                    (old_goptions & ~new_goptions),
    1032             :                                    ownerId, behavior);
    1033             :     }
    1034             : 
    1035         826 :     return new_acl;
    1036             : }
    1037             : 
    1038             : /*
    1039             :  * Update an ACL array to reflect a change of owner to the parent object
    1040             :  *
    1041             :  *  old_acl: the input ACL array (must not be NULL)
    1042             :  *  oldOwnerId: Oid of the old object owner
    1043             :  *  newOwnerId: Oid of the new object owner
    1044             :  *
    1045             :  * The result is a modified copy; the input object is not changed.
    1046             :  *
    1047             :  * NB: caller is responsible for having detoasted the input ACL, if needed.
    1048             :  */
    1049             : Acl *
    1050           5 : aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
    1051             : {
    1052             :     Acl        *new_acl;
    1053             :     AclItem    *new_aip;
    1054             :     AclItem    *old_aip;
    1055             :     AclItem    *dst_aip;
    1056             :     AclItem    *src_aip;
    1057             :     AclItem    *targ_aip;
    1058           5 :     bool        newpresent = false;
    1059             :     int         dst,
    1060             :                 src,
    1061             :                 targ,
    1062             :                 num;
    1063             : 
    1064           5 :     check_acl(old_acl);
    1065             : 
    1066             :     /*
    1067             :      * Make a copy of the given ACL, substituting new owner ID for old
    1068             :      * wherever it appears as either grantor or grantee.  Also note if the new
    1069             :      * owner ID is already present.
    1070             :      */
    1071           5 :     num = ACL_NUM(old_acl);
    1072           5 :     old_aip = ACL_DAT(old_acl);
    1073           5 :     new_acl = allocacl(num);
    1074           5 :     new_aip = ACL_DAT(new_acl);
    1075           5 :     memcpy(new_aip, old_aip, num * sizeof(AclItem));
    1076          13 :     for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
    1077             :     {
    1078           8 :         if (dst_aip->ai_grantor == oldOwnerId)
    1079           8 :             dst_aip->ai_grantor = newOwnerId;
    1080           0 :         else if (dst_aip->ai_grantor == newOwnerId)
    1081           0 :             newpresent = true;
    1082           8 :         if (dst_aip->ai_grantee == oldOwnerId)
    1083           5 :             dst_aip->ai_grantee = newOwnerId;
    1084           3 :         else if (dst_aip->ai_grantee == newOwnerId)
    1085           1 :             newpresent = true;
    1086             :     }
    1087             : 
    1088             :     /*
    1089             :      * If the old ACL contained any references to the new owner, then we may
    1090             :      * now have generated an ACL containing duplicate entries.  Find them and
    1091             :      * merge them so that there are not duplicates.  (This is relatively
    1092             :      * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to
    1093             :      * be the normal case.)
    1094             :      *
    1095             :      * To simplify deletion of duplicate entries, we temporarily leave them in
    1096             :      * the array but set their privilege masks to zero; when we reach such an
    1097             :      * entry it's just skipped.  (Thus, a side effect of this code will be to
    1098             :      * remove privilege-free entries, should there be any in the input.)  dst
    1099             :      * is the next output slot, targ is the currently considered input slot
    1100             :      * (always >= dst), and src scans entries to the right of targ looking for
    1101             :      * duplicates.  Once an entry has been emitted to dst it is known
    1102             :      * duplicate-free and need not be considered anymore.
    1103             :      */
    1104           5 :     if (newpresent)
    1105             :     {
    1106           1 :         dst = 0;
    1107           3 :         for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
    1108             :         {
    1109             :             /* ignore if deleted in an earlier pass */
    1110           2 :             if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
    1111           1 :                 continue;
    1112             :             /* find and merge any duplicates */
    1113           3 :             for (src = targ + 1, src_aip = targ_aip + 1; src < num;
    1114           1 :                  src++, src_aip++)
    1115             :             {
    1116           1 :                 if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
    1117           0 :                     continue;
    1118           1 :                 if (aclitem_match(targ_aip, src_aip))
    1119             :                 {
    1120           1 :                     ACLITEM_SET_RIGHTS(*targ_aip,
    1121             :                                        ACLITEM_GET_RIGHTS(*targ_aip) |
    1122             :                                        ACLITEM_GET_RIGHTS(*src_aip));
    1123             :                     /* mark the duplicate deleted */
    1124           1 :                     ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
    1125             :                 }
    1126             :             }
    1127             :             /* and emit to output */
    1128           1 :             new_aip[dst] = *targ_aip;
    1129           1 :             dst++;
    1130             :         }
    1131             :         /* Adjust array size to be 'dst' items */
    1132           1 :         ARR_DIMS(new_acl)[0] = dst;
    1133           1 :         SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
    1134             :     }
    1135             : 
    1136           5 :     return new_acl;
    1137             : }
    1138             : 
    1139             : 
    1140             : /*
    1141             :  * When granting grant options, we must disallow attempts to set up circular
    1142             :  * chains of grant options.  Suppose A (the object owner) grants B some
    1143             :  * privileges with grant option, and B re-grants them to C.  If C could
    1144             :  * grant the privileges to B as well, then A would be unable to effectively
    1145             :  * revoke the privileges from B, since recursive_revoke would consider that
    1146             :  * B still has 'em from C.
    1147             :  *
    1148             :  * We check for this by recursively deleting all grant options belonging to
    1149             :  * the target grantee, and then seeing if the would-be grantor still has the
    1150             :  * grant option or not.
    1151             :  */
    1152             : static void
    1153          10 : check_circularity(const Acl *old_acl, const AclItem *mod_aip,
    1154             :                   Oid ownerId)
    1155             : {
    1156             :     Acl        *acl;
    1157             :     AclItem    *aip;
    1158             :     int         i,
    1159             :                 num;
    1160             :     AclMode     own_privs;
    1161             : 
    1162          10 :     check_acl(old_acl);
    1163             : 
    1164             :     /*
    1165             :      * For now, grant options can only be granted to roles, not PUBLIC.
    1166             :      * Otherwise we'd have to work a bit harder here.
    1167             :      */
    1168          10 :     Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
    1169             : 
    1170             :     /* The owner always has grant options, no need to check */
    1171          10 :     if (mod_aip->ai_grantor == ownerId)
    1172          18 :         return;
    1173             : 
    1174             :     /* Make a working copy */
    1175           2 :     acl = allocacl(ACL_NUM(old_acl));
    1176           2 :     memcpy(acl, old_acl, ACL_SIZE(old_acl));
    1177             : 
    1178             :     /* Zap all grant options of target grantee, plus what depends on 'em */
    1179             : cc_restart:
    1180           3 :     num = ACL_NUM(acl);
    1181           3 :     aip = ACL_DAT(acl);
    1182          12 :     for (i = 0; i < num; i++)
    1183             :     {
    1184          11 :         if (aip[i].ai_grantee == mod_aip->ai_grantee &&
    1185           1 :             ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
    1186             :         {
    1187             :             Acl        *new_acl;
    1188             : 
    1189             :             /* We'll actually zap ordinary privs too, but no matter */
    1190           1 :             new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
    1191             :                                 ownerId, DROP_CASCADE);
    1192             : 
    1193           1 :             pfree(acl);
    1194           1 :             acl = new_acl;
    1195             : 
    1196           1 :             goto cc_restart;
    1197             :         }
    1198             :     }
    1199             : 
    1200             :     /* Now we can compute grantor's independently-derived privileges */
    1201           2 :     own_privs = aclmask(acl,
    1202             :                         mod_aip->ai_grantor,
    1203             :                         ownerId,
    1204           2 :                         ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
    1205             :                         ACLMASK_ALL);
    1206           2 :     own_privs = ACL_OPTION_TO_PRIVS(own_privs);
    1207             : 
    1208           2 :     if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
    1209           0 :         ereport(ERROR,
    1210             :                 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    1211             :                  errmsg("grant options cannot be granted back to your own grantor")));
    1212             : 
    1213           2 :     pfree(acl);
    1214             : }
    1215             : 
    1216             : 
    1217             : /*
    1218             :  * Ensure that no privilege is "abandoned".  A privilege is abandoned
    1219             :  * if the user that granted the privilege loses the grant option.  (So
    1220             :  * the chain through which it was granted is broken.)  Either the
    1221             :  * abandoned privileges are revoked as well, or an error message is
    1222             :  * printed, depending on the drop behavior option.
    1223             :  *
    1224             :  *  acl: the input ACL list
    1225             :  *  grantee: the user from whom some grant options have been revoked
    1226             :  *  revoke_privs: the grant options being revoked
    1227             :  *  ownerId: Oid of object owner
    1228             :  *  behavior: RESTRICT or CASCADE behavior for recursive removal
    1229             :  *
    1230             :  * The input Acl object is pfree'd if replaced.
    1231             :  */
    1232             : static Acl *
    1233          10 : recursive_revoke(Acl *acl,
    1234             :                  Oid grantee,
    1235             :                  AclMode revoke_privs,
    1236             :                  Oid ownerId,
    1237             :                  DropBehavior behavior)
    1238             : {
    1239             :     AclMode     still_has;
    1240             :     AclItem    *aip;
    1241             :     int         i,
    1242             :                 num;
    1243             : 
    1244          10 :     check_acl(acl);
    1245             : 
    1246             :     /* The owner can never truly lose grant options, so short-circuit */
    1247          10 :     if (grantee == ownerId)
    1248           0 :         return acl;
    1249             : 
    1250             :     /* The grantee might still have some grant options via another grantor */
    1251          10 :     still_has = aclmask(acl, grantee, ownerId,
    1252             :                         ACL_GRANT_OPTION_FOR(revoke_privs),
    1253             :                         ACLMASK_ALL);
    1254          10 :     revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
    1255          10 :     if (revoke_privs == ACL_NO_RIGHTS)
    1256           1 :         return acl;
    1257             : 
    1258             : restart:
    1259          13 :     num = ACL_NUM(acl);
    1260          13 :     aip = ACL_DAT(acl);
    1261          44 :     for (i = 0; i < num; i++)
    1262             :     {
    1263          37 :         if (aip[i].ai_grantor == grantee
    1264           6 :             && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
    1265             :         {
    1266             :             AclItem     mod_acl;
    1267             :             Acl        *new_acl;
    1268             : 
    1269           6 :             if (behavior == DROP_RESTRICT)
    1270           2 :                 ereport(ERROR,
    1271             :                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    1272             :                          errmsg("dependent privileges exist"),
    1273             :                          errhint("Use CASCADE to revoke them too.")));
    1274             : 
    1275           4 :             mod_acl.ai_grantor = grantee;
    1276           4 :             mod_acl.ai_grantee = aip[i].ai_grantee;
    1277           4 :             ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
    1278             :                                        revoke_privs,
    1279             :                                        revoke_privs);
    1280             : 
    1281           4 :             new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
    1282             :                                 ownerId, behavior);
    1283             : 
    1284           4 :             pfree(acl);
    1285           4 :             acl = new_acl;
    1286             : 
    1287           4 :             goto restart;
    1288             :         }
    1289             :     }
    1290             : 
    1291           7 :     return acl;
    1292             : }
    1293             : 
    1294             : 
    1295             : /*
    1296             :  * aclmask --- compute bitmask of all privileges held by roleid.
    1297             :  *
    1298             :  * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
    1299             :  * held by the given roleid according to the given ACL list, ANDed
    1300             :  * with 'mask'.  (The point of passing 'mask' is to let the routine
    1301             :  * exit early if all privileges of interest have been found.)
    1302             :  *
    1303             :  * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
    1304             :  * is known true.  (This lets us exit soonest in cases where the
    1305             :  * caller is only going to test for zero or nonzero result.)
    1306             :  *
    1307             :  * Usage patterns:
    1308             :  *
    1309             :  * To see if any of a set of privileges are held:
    1310             :  *      if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
    1311             :  *
    1312             :  * To see if all of a set of privileges are held:
    1313             :  *      if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
    1314             :  *
    1315             :  * To determine exactly which of a set of privileges are held:
    1316             :  *      heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
    1317             :  */
    1318             : AclMode
    1319        5931 : aclmask(const Acl *acl, Oid roleid, Oid ownerId,
    1320             :         AclMode mask, AclMaskHow how)
    1321             : {
    1322             :     AclMode     result;
    1323             :     AclMode     remaining;
    1324             :     AclItem    *aidat;
    1325             :     int         i,
    1326             :                 num;
    1327             : 
    1328             :     /*
    1329             :      * Null ACL should not happen, since caller should have inserted
    1330             :      * appropriate default
    1331             :      */
    1332        5931 :     if (acl == NULL)
    1333           0 :         elog(ERROR, "null ACL");
    1334             : 
    1335        5931 :     check_acl(acl);
    1336             : 
    1337             :     /* Quick exit for mask == 0 */
    1338        5931 :     if (mask == 0)
    1339           8 :         return 0;
    1340             : 
    1341        5923 :     result = 0;
    1342             : 
    1343             :     /* Owner always implicitly has all grant options */
    1344        5947 :     if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
    1345          24 :         has_privs_of_role(roleid, ownerId))
    1346             :     {
    1347           1 :         result = mask & ACLITEM_ALL_GOPTION_BITS;
    1348           1 :         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
    1349           1 :             return result;
    1350             :     }
    1351             : 
    1352        5922 :     num = ACL_NUM(acl);
    1353        5922 :     aidat = ACL_DAT(acl);
    1354             : 
    1355             :     /*
    1356             :      * Check privileges granted directly to roleid or to public
    1357             :      */
    1358        8936 :     for (i = 0; i < num; i++)
    1359             :     {
    1360        8566 :         AclItem    *aidata = &aidat[i];
    1361             : 
    1362       12223 :         if (aidata->ai_grantee == ACL_ID_PUBLIC ||
    1363        3657 :             aidata->ai_grantee == roleid)
    1364             :         {
    1365        5672 :             result |= aidata->ai_privs & mask;
    1366        5672 :             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
    1367        5552 :                 return result;
    1368             :         }
    1369             :     }
    1370             : 
    1371             :     /*
    1372             :      * Check privileges granted indirectly via role memberships. We do this in
    1373             :      * a separate pass to minimize expensive indirect membership tests.  In
    1374             :      * particular, it's worth testing whether a given ACL entry grants any
    1375             :      * privileges still of interest before we perform the has_privs_of_role
    1376             :      * test.
    1377             :      */
    1378         370 :     remaining = mask & ~result;
    1379         978 :     for (i = 0; i < num; i++)
    1380             :     {
    1381         614 :         AclItem    *aidata = &aidat[i];
    1382             : 
    1383        1222 :         if (aidata->ai_grantee == ACL_ID_PUBLIC ||
    1384         608 :             aidata->ai_grantee == roleid)
    1385         117 :             continue;           /* already checked it */
    1386             : 
    1387         918 :         if ((aidata->ai_privs & remaining) &&
    1388         421 :             has_privs_of_role(roleid, aidata->ai_grantee))
    1389             :         {
    1390           6 :             result |= aidata->ai_privs & mask;
    1391           6 :             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
    1392           6 :                 return result;
    1393           0 :             remaining = mask & ~result;
    1394             :         }
    1395             :     }
    1396             : 
    1397         364 :     return result;
    1398             : }
    1399             : 
    1400             : 
    1401             : /*
    1402             :  * aclmask_direct --- compute bitmask of all privileges held by roleid.
    1403             :  *
    1404             :  * This is exactly like aclmask() except that we consider only privileges
    1405             :  * held *directly* by roleid, not those inherited via role membership.
    1406             :  */
    1407             : static AclMode
    1408          36 : aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId,
    1409             :                AclMode mask, AclMaskHow how)
    1410             : {
    1411             :     AclMode     result;
    1412             :     AclItem    *aidat;
    1413             :     int         i,
    1414             :                 num;
    1415             : 
    1416             :     /*
    1417             :      * Null ACL should not happen, since caller should have inserted
    1418             :      * appropriate default
    1419             :      */
    1420          36 :     if (acl == NULL)
    1421           0 :         elog(ERROR, "null ACL");
    1422             : 
    1423          36 :     check_acl(acl);
    1424             : 
    1425             :     /* Quick exit for mask == 0 */
    1426          36 :     if (mask == 0)
    1427           0 :         return 0;
    1428             : 
    1429          36 :     result = 0;
    1430             : 
    1431             :     /* Owner always implicitly has all grant options */
    1432          36 :     if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
    1433             :         roleid == ownerId)
    1434             :     {
    1435           0 :         result = mask & ACLITEM_ALL_GOPTION_BITS;
    1436           0 :         if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
    1437           0 :             return result;
    1438             :     }
    1439             : 
    1440          36 :     num = ACL_NUM(acl);
    1441          36 :     aidat = ACL_DAT(acl);
    1442             : 
    1443             :     /*
    1444             :      * Check privileges granted directly to roleid (and not to public)
    1445             :      */
    1446         104 :     for (i = 0; i < num; i++)
    1447             :     {
    1448          91 :         AclItem    *aidata = &aidat[i];
    1449             : 
    1450          91 :         if (aidata->ai_grantee == roleid)
    1451             :         {
    1452          29 :             result |= aidata->ai_privs & mask;
    1453          29 :             if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
    1454          23 :                 return result;
    1455             :         }
    1456             :     }
    1457             : 
    1458          13 :     return result;
    1459             : }
    1460             : 
    1461             : 
    1462             : /*
    1463             :  * aclmembers
    1464             :  *      Find out all the roleids mentioned in an Acl.
    1465             :  *      Note that we do not distinguish grantors from grantees.
    1466             :  *
    1467             :  * *roleids is set to point to a palloc'd array containing distinct OIDs
    1468             :  * in sorted order.  The length of the array is the function result.
    1469             :  */
    1470             : int
    1471         961 : aclmembers(const Acl *acl, Oid **roleids)
    1472             : {
    1473             :     Oid        *list;
    1474             :     const AclItem *acldat;
    1475             :     int         i,
    1476             :                 j,
    1477             :                 k;
    1478             : 
    1479         961 :     if (acl == NULL || ACL_NUM(acl) == 0)
    1480             :     {
    1481         418 :         *roleids = NULL;
    1482         418 :         return 0;
    1483             :     }
    1484             : 
    1485         543 :     check_acl(acl);
    1486             : 
    1487             :     /* Allocate the worst-case space requirement */
    1488         543 :     list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
    1489         543 :     acldat = ACL_DAT(acl);
    1490             : 
    1491             :     /*
    1492             :      * Walk the ACL collecting mentioned RoleIds.
    1493             :      */
    1494         543 :     j = 0;
    1495        1535 :     for (i = 0; i < ACL_NUM(acl); i++)
    1496             :     {
    1497         992 :         const AclItem *ai = &acldat[i];
    1498             : 
    1499         992 :         if (ai->ai_grantee != ACL_ID_PUBLIC)
    1500         848 :             list[j++] = ai->ai_grantee;
    1501             :         /* grantor is currently never PUBLIC, but let's check anyway */
    1502         992 :         if (ai->ai_grantor != ACL_ID_PUBLIC)
    1503         992 :             list[j++] = ai->ai_grantor;
    1504             :     }
    1505             : 
    1506             :     /* Sort the array */
    1507         543 :     qsort(list, j, sizeof(Oid), oid_cmp);
    1508             : 
    1509             :     /* Remove duplicates from the array */
    1510         543 :     k = 0;
    1511        1840 :     for (i = 1; i < j; i++)
    1512             :     {
    1513        1297 :         if (list[k] != list[i])
    1514         367 :             list[++k] = list[i];
    1515             :     }
    1516             : 
    1517             :     /*
    1518             :      * We could repalloc the array down to minimum size, but it's hardly worth
    1519             :      * it since it's only transient memory.
    1520             :      */
    1521         543 :     *roleids = list;
    1522             : 
    1523         543 :     return k + 1;
    1524             : }
    1525             : 
    1526             : 
    1527             : /*
    1528             :  * aclinsert (exported function)
    1529             :  */
    1530             : Datum
    1531           0 : aclinsert(PG_FUNCTION_ARGS)
    1532             : {
    1533           0 :     ereport(ERROR,
    1534             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1535             :              errmsg("aclinsert is no longer supported")));
    1536             : 
    1537             :     PG_RETURN_NULL();           /* keep compiler quiet */
    1538             : }
    1539             : 
    1540             : Datum
    1541           0 : aclremove(PG_FUNCTION_ARGS)
    1542             : {
    1543           0 :     ereport(ERROR,
    1544             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1545             :              errmsg("aclremove is no longer supported")));
    1546             : 
    1547             :     PG_RETURN_NULL();           /* keep compiler quiet */
    1548             : }
    1549             : 
    1550             : Datum
    1551           0 : aclcontains(PG_FUNCTION_ARGS)
    1552             : {
    1553           0 :     Acl        *acl = PG_GETARG_ACL_P(0);
    1554           0 :     AclItem    *aip = PG_GETARG_ACLITEM_P(1);
    1555             :     AclItem    *aidat;
    1556             :     int         i,
    1557             :                 num;
    1558             : 
    1559           0 :     check_acl(acl);
    1560           0 :     num = ACL_NUM(acl);
    1561           0 :     aidat = ACL_DAT(acl);
    1562           0 :     for (i = 0; i < num; ++i)
    1563             :     {
    1564           0 :         if (aip->ai_grantee == aidat[i].ai_grantee &&
    1565           0 :             aip->ai_grantor == aidat[i].ai_grantor &&
    1566           0 :             (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
    1567           0 :             PG_RETURN_BOOL(true);
    1568             :     }
    1569           0 :     PG_RETURN_BOOL(false);
    1570             : }
    1571             : 
    1572             : Datum
    1573           0 : makeaclitem(PG_FUNCTION_ARGS)
    1574             : {
    1575           0 :     Oid         grantee = PG_GETARG_OID(0);
    1576           0 :     Oid         grantor = PG_GETARG_OID(1);
    1577           0 :     text       *privtext = PG_GETARG_TEXT_PP(2);
    1578           0 :     bool        goption = PG_GETARG_BOOL(3);
    1579             :     AclItem    *result;
    1580             :     AclMode     priv;
    1581             : 
    1582           0 :     priv = convert_priv_string(privtext);
    1583             : 
    1584           0 :     result = (AclItem *) palloc(sizeof(AclItem));
    1585             : 
    1586           0 :     result->ai_grantee = grantee;
    1587           0 :     result->ai_grantor = grantor;
    1588             : 
    1589           0 :     ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
    1590             :                                (goption ? priv : ACL_NO_RIGHTS));
    1591             : 
    1592           0 :     PG_RETURN_ACLITEM_P(result);
    1593             : }
    1594             : 
    1595             : static AclMode
    1596           0 : convert_priv_string(text *priv_type_text)
    1597             : {
    1598           0 :     char       *priv_type = text_to_cstring(priv_type_text);
    1599             : 
    1600           0 :     if (pg_strcasecmp(priv_type, "SELECT") == 0)
    1601           0 :         return ACL_SELECT;
    1602           0 :     if (pg_strcasecmp(priv_type, "INSERT") == 0)
    1603           0 :         return ACL_INSERT;
    1604           0 :     if (pg_strcasecmp(priv_type, "UPDATE") == 0)
    1605           0 :         return ACL_UPDATE;
    1606           0 :     if (pg_strcasecmp(priv_type, "DELETE") == 0)
    1607           0 :         return ACL_DELETE;
    1608           0 :     if (pg_strcasecmp(priv_type, "TRUNCATE") == 0)
    1609           0 :         return ACL_TRUNCATE;
    1610           0 :     if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
    1611           0 :         return ACL_REFERENCES;
    1612           0 :     if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
    1613           0 :         return ACL_TRIGGER;
    1614           0 :     if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
    1615           0 :         return ACL_EXECUTE;
    1616           0 :     if (pg_strcasecmp(priv_type, "USAGE") == 0)
    1617           0 :         return ACL_USAGE;
    1618           0 :     if (pg_strcasecmp(priv_type, "CREATE") == 0)
    1619           0 :         return ACL_CREATE;
    1620           0 :     if (pg_strcasecmp(priv_type, "TEMP") == 0)
    1621           0 :         return ACL_CREATE_TEMP;
    1622           0 :     if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
    1623           0 :         return ACL_CREATE_TEMP;
    1624           0 :     if (pg_strcasecmp(priv_type, "CONNECT") == 0)
    1625           0 :         return ACL_CONNECT;
    1626           0 :     if (pg_strcasecmp(priv_type, "RULE") == 0)
    1627           0 :         return 0;               /* ignore old RULE privileges */
    1628             : 
    1629           0 :     ereport(ERROR,
    1630             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1631             :              errmsg("unrecognized privilege type: \"%s\"", priv_type)));
    1632             :     return ACL_NO_RIGHTS;       /* keep compiler quiet */
    1633             : }
    1634             : 
    1635             : 
    1636             : /*
    1637             :  * convert_any_priv_string: recognize privilege strings for has_foo_privilege
    1638             :  *
    1639             :  * We accept a comma-separated list of case-insensitive privilege names,
    1640             :  * producing a bitmask of the OR'd privilege bits.  We are liberal about
    1641             :  * whitespace between items, not so much about whitespace within items.
    1642             :  * The allowed privilege names are given as an array of priv_map structs,
    1643             :  * terminated by one with a NULL name pointer.
    1644             :  */
    1645             : static AclMode
    1646        6931 : convert_any_priv_string(text *priv_type_text,
    1647             :                         const priv_map *privileges)
    1648             : {
    1649        6931 :     AclMode     result = 0;
    1650        6931 :     char       *priv_type = text_to_cstring(priv_type_text);
    1651             :     char       *chunk;
    1652             :     char       *next_chunk;
    1653             : 
    1654             :     /* We rely on priv_type being a private, modifiable string */
    1655       13879 :     for (chunk = priv_type; chunk; chunk = next_chunk)
    1656             :     {
    1657             :         int         chunk_len;
    1658             :         const priv_map *this_priv;
    1659             : 
    1660             :         /* Split string at commas */
    1661        6950 :         next_chunk = strchr(chunk, ',');
    1662        6950 :         if (next_chunk)
    1663          19 :             *next_chunk++ = '\0';
    1664             : 
    1665             :         /* Drop leading/trailing whitespace in this chunk */
    1666       13900 :         while (*chunk && isspace((unsigned char) *chunk))
    1667           0 :             chunk++;
    1668        6950 :         chunk_len = strlen(chunk);
    1669       13900 :         while (chunk_len > 0 && isspace((unsigned char) chunk[chunk_len - 1]))
    1670           0 :             chunk_len--;
    1671        6950 :         chunk[chunk_len] = '\0';
    1672             : 
    1673             :         /* Match to the privileges list */
    1674        7189 :         for (this_priv = privileges; this_priv->name; this_priv++)
    1675             :         {
    1676        7187 :             if (pg_strcasecmp(this_priv->name, chunk) == 0)
    1677             :             {
    1678        6948 :                 result |= this_priv->value;
    1679        6948 :                 break;
    1680             :             }
    1681             :         }
    1682        6950 :         if (!this_priv->name)
    1683           2 :             ereport(ERROR,
    1684             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1685             :                      errmsg("unrecognized privilege type: \"%s\"", chunk)));
    1686             :     }
    1687             : 
    1688        6929 :     pfree(priv_type);
    1689        6929 :     return result;
    1690             : }
    1691             : 
    1692             : 
    1693             : static const char *
    1694          16 : convert_aclright_to_string(int aclright)
    1695             : {
    1696          16 :     switch (aclright)
    1697             :     {
    1698             :         case ACL_INSERT:
    1699           0 :             return "INSERT";
    1700             :         case ACL_SELECT:
    1701           0 :             return "SELECT";
    1702             :         case ACL_UPDATE:
    1703           0 :             return "UPDATE";
    1704             :         case ACL_DELETE:
    1705           0 :             return "DELETE";
    1706             :         case ACL_TRUNCATE:
    1707           0 :             return "TRUNCATE";
    1708             :         case ACL_REFERENCES:
    1709           0 :             return "REFERENCES";
    1710             :         case ACL_TRIGGER:
    1711           0 :             return "TRIGGER";
    1712             :         case ACL_EXECUTE:
    1713           0 :             return "EXECUTE";
    1714             :         case ACL_USAGE:
    1715          16 :             return "USAGE";
    1716             :         case ACL_CREATE:
    1717           0 :             return "CREATE";
    1718             :         case ACL_CREATE_TEMP:
    1719           0 :             return "TEMPORARY";
    1720             :         case ACL_CONNECT:
    1721           0 :             return "CONNECT";
    1722             :         default:
    1723           0 :             elog(ERROR, "unrecognized aclright: %d", aclright);
    1724             :             return NULL;
    1725             :     }
    1726             : }
    1727             : 
    1728             : 
    1729             : /*----------
    1730             :  * Convert an aclitem[] to a table.
    1731             :  *
    1732             :  * Example:
    1733             :  *
    1734             :  * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[])
    1735             :  *
    1736             :  * returns the table
    1737             :  *
    1738             :  * {{ OID(joe), 0::OID,   'SELECT', false },
    1739             :  *  { OID(joe), OID(foo), 'INSERT', true },
    1740             :  *  { OID(joe), OID(foo), 'UPDATE', false }}
    1741             :  *----------
    1742             :  */
    1743             : Datum
    1744          24 : aclexplode(PG_FUNCTION_ARGS)
    1745             : {
    1746          24 :     Acl        *acl = PG_GETARG_ACL_P(0);
    1747             :     FuncCallContext *funcctx;
    1748             :     int        *idx;
    1749             :     AclItem    *aidat;
    1750             : 
    1751          24 :     if (SRF_IS_FIRSTCALL())
    1752             :     {
    1753             :         TupleDesc   tupdesc;
    1754             :         MemoryContext oldcontext;
    1755             : 
    1756           8 :         check_acl(acl);
    1757             : 
    1758           8 :         funcctx = SRF_FIRSTCALL_INIT();
    1759           8 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1760             : 
    1761             :         /*
    1762             :          * build tupdesc for result tuples (matches out parameters in pg_proc
    1763             :          * entry)
    1764             :          */
    1765           8 :         tupdesc = CreateTemplateTupleDesc(4, false);
    1766           8 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor",
    1767             :                            OIDOID, -1, 0);
    1768           8 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee",
    1769             :                            OIDOID, -1, 0);
    1770           8 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type",
    1771             :                            TEXTOID, -1, 0);
    1772           8 :         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable",
    1773             :                            BOOLOID, -1, 0);
    1774             : 
    1775           8 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
    1776             : 
    1777             :         /* allocate memory for user context */
    1778           8 :         idx = (int *) palloc(sizeof(int[2]));
    1779           8 :         idx[0] = 0;             /* ACL array item index */
    1780           8 :         idx[1] = -1;            /* privilege type counter */
    1781           8 :         funcctx->user_fctx = (void *) idx;
    1782             : 
    1783           8 :         MemoryContextSwitchTo(oldcontext);
    1784             :     }
    1785             : 
    1786          24 :     funcctx = SRF_PERCALL_SETUP();
    1787          24 :     idx = (int *) funcctx->user_fctx;
    1788          24 :     aidat = ACL_DAT(acl);
    1789             : 
    1790             :     /* need test here in case acl has no items */
    1791         224 :     while (idx[0] < ACL_NUM(acl))
    1792             :     {
    1793             :         AclItem    *aidata;
    1794             :         AclMode     priv_bit;
    1795             : 
    1796         200 :         idx[1]++;
    1797         200 :         if (idx[1] == N_ACL_RIGHTS)
    1798             :         {
    1799          16 :             idx[1] = 0;
    1800          16 :             idx[0]++;
    1801          16 :             if (idx[0] >= ACL_NUM(acl)) /* done */
    1802           8 :                 break;
    1803             :         }
    1804         192 :         aidata = &aidat[idx[0]];
    1805         192 :         priv_bit = 1 << idx[1];
    1806             : 
    1807         192 :         if (ACLITEM_GET_PRIVS(*aidata) & priv_bit)
    1808             :         {
    1809             :             Datum       result;
    1810             :             Datum       values[4];
    1811             :             bool        nulls[4];
    1812             :             HeapTuple   tuple;
    1813             : 
    1814          16 :             values[0] = ObjectIdGetDatum(aidata->ai_grantor);
    1815          16 :             values[1] = ObjectIdGetDatum(aidata->ai_grantee);
    1816          16 :             values[2] = CStringGetTextDatum(convert_aclright_to_string(priv_bit));
    1817          16 :             values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0);
    1818             : 
    1819          16 :             MemSet(nulls, 0, sizeof(nulls));
    1820             : 
    1821          16 :             tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
    1822          16 :             result = HeapTupleGetDatum(tuple);
    1823             : 
    1824          16 :             SRF_RETURN_NEXT(funcctx, result);
    1825             :         }
    1826             :     }
    1827             : 
    1828           8 :     SRF_RETURN_DONE(funcctx);
    1829             : }
    1830             : 
    1831             : 
    1832             : /*
    1833             :  * has_table_privilege variants
    1834             :  *      These are all named "has_table_privilege" at the SQL level.
    1835             :  *      They take various combinations of relation name, relation OID,
    1836             :  *      user name, user OID, or implicit user = current_user.
    1837             :  *
    1838             :  *      The result is a boolean value: true if user has the indicated
    1839             :  *      privilege, false if not.  The variants that take a relation OID
    1840             :  *      return NULL if the OID doesn't exist (rather than failing, as
    1841             :  *      they did before Postgres 8.4).
    1842             :  */
    1843             : 
    1844             : /*
    1845             :  * has_table_privilege_name_name
    1846             :  *      Check user privileges on a table given
    1847             :  *      name username, text tablename, and text priv name.
    1848             :  */
    1849             : Datum
    1850          26 : has_table_privilege_name_name(PG_FUNCTION_ARGS)
    1851             : {
    1852          26 :     Name        rolename = PG_GETARG_NAME(0);
    1853          26 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    1854          26 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    1855             :     Oid         roleid;
    1856             :     Oid         tableoid;
    1857             :     AclMode     mode;
    1858             :     AclResult   aclresult;
    1859             : 
    1860          26 :     roleid = get_role_oid_or_public(NameStr(*rolename));
    1861          25 :     tableoid = convert_table_name(tablename);
    1862          25 :     mode = convert_table_priv_string(priv_type_text);
    1863             : 
    1864          25 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1865             : 
    1866          25 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1867             : }
    1868             : 
    1869             : /*
    1870             :  * has_table_privilege_name
    1871             :  *      Check user privileges on a table given
    1872             :  *      text tablename and text priv name.
    1873             :  *      current_user is assumed
    1874             :  */
    1875             : Datum
    1876          11 : has_table_privilege_name(PG_FUNCTION_ARGS)
    1877             : {
    1878          11 :     text       *tablename = PG_GETARG_TEXT_PP(0);
    1879          11 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    1880             :     Oid         roleid;
    1881             :     Oid         tableoid;
    1882             :     AclMode     mode;
    1883             :     AclResult   aclresult;
    1884             : 
    1885          11 :     roleid = GetUserId();
    1886          11 :     tableoid = convert_table_name(tablename);
    1887          10 :     mode = convert_table_priv_string(priv_type_text);
    1888             : 
    1889           9 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1890             : 
    1891           9 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1892             : }
    1893             : 
    1894             : /*
    1895             :  * has_table_privilege_name_id
    1896             :  *      Check user privileges on a table given
    1897             :  *      name usename, table oid, and text priv name.
    1898             :  */
    1899             : Datum
    1900           4 : has_table_privilege_name_id(PG_FUNCTION_ARGS)
    1901             : {
    1902           4 :     Name        username = PG_GETARG_NAME(0);
    1903           4 :     Oid         tableoid = PG_GETARG_OID(1);
    1904           4 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    1905             :     Oid         roleid;
    1906             :     AclMode     mode;
    1907             :     AclResult   aclresult;
    1908             : 
    1909           4 :     roleid = get_role_oid_or_public(NameStr(*username));
    1910           4 :     mode = convert_table_priv_string(priv_type_text);
    1911             : 
    1912           4 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    1913           0 :         PG_RETURN_NULL();
    1914             : 
    1915           4 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1916             : 
    1917           4 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1918             : }
    1919             : 
    1920             : /*
    1921             :  * has_table_privilege_id
    1922             :  *      Check user privileges on a table given
    1923             :  *      table oid, and text priv name.
    1924             :  *      current_user is assumed
    1925             :  */
    1926             : Datum
    1927          13 : has_table_privilege_id(PG_FUNCTION_ARGS)
    1928             : {
    1929          13 :     Oid         tableoid = PG_GETARG_OID(0);
    1930          13 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    1931             :     Oid         roleid;
    1932             :     AclMode     mode;
    1933             :     AclResult   aclresult;
    1934             : 
    1935          13 :     roleid = GetUserId();
    1936          13 :     mode = convert_table_priv_string(priv_type_text);
    1937             : 
    1938          13 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    1939           1 :         PG_RETURN_NULL();
    1940             : 
    1941          12 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1942             : 
    1943          12 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1944             : }
    1945             : 
    1946             : /*
    1947             :  * has_table_privilege_id_name
    1948             :  *      Check user privileges on a table given
    1949             :  *      roleid, text tablename, and text priv name.
    1950             :  */
    1951             : Datum
    1952           7 : has_table_privilege_id_name(PG_FUNCTION_ARGS)
    1953             : {
    1954           7 :     Oid         roleid = PG_GETARG_OID(0);
    1955           7 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    1956           7 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    1957             :     Oid         tableoid;
    1958             :     AclMode     mode;
    1959             :     AclResult   aclresult;
    1960             : 
    1961           7 :     tableoid = convert_table_name(tablename);
    1962           7 :     mode = convert_table_priv_string(priv_type_text);
    1963             : 
    1964           7 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1965             : 
    1966           7 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1967             : }
    1968             : 
    1969             : /*
    1970             :  * has_table_privilege_id_id
    1971             :  *      Check user privileges on a table given
    1972             :  *      roleid, table oid, and text priv name.
    1973             :  */
    1974             : Datum
    1975           6 : has_table_privilege_id_id(PG_FUNCTION_ARGS)
    1976             : {
    1977           6 :     Oid         roleid = PG_GETARG_OID(0);
    1978           6 :     Oid         tableoid = PG_GETARG_OID(1);
    1979           6 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    1980             :     AclMode     mode;
    1981             :     AclResult   aclresult;
    1982             : 
    1983           6 :     mode = convert_table_priv_string(priv_type_text);
    1984             : 
    1985           6 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    1986           0 :         PG_RETURN_NULL();
    1987             : 
    1988           6 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    1989             : 
    1990           6 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    1991             : }
    1992             : 
    1993             : /*
    1994             :  *      Support routines for has_table_privilege family.
    1995             :  */
    1996             : 
    1997             : /*
    1998             :  * Given a table name expressed as a string, look it up and return Oid
    1999             :  */
    2000             : static Oid
    2001          46 : convert_table_name(text *tablename)
    2002             : {
    2003             :     RangeVar   *relrv;
    2004             : 
    2005          46 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
    2006             : 
    2007             :     /* We might not even have permissions on this relation; don't lock it. */
    2008          46 :     return RangeVarGetRelid(relrv, NoLock, false);
    2009             : }
    2010             : 
    2011             : /*
    2012             :  * convert_table_priv_string
    2013             :  *      Convert text string to AclMode value.
    2014             :  */
    2015             : static AclMode
    2016          65 : convert_table_priv_string(text *priv_type_text)
    2017             : {
    2018             :     static const priv_map table_priv_map[] = {
    2019             :         {"SELECT", ACL_SELECT},
    2020             :         {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
    2021             :         {"INSERT", ACL_INSERT},
    2022             :         {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
    2023             :         {"UPDATE", ACL_UPDATE},
    2024             :         {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
    2025             :         {"DELETE", ACL_DELETE},
    2026             :         {"DELETE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_DELETE)},
    2027             :         {"TRUNCATE", ACL_TRUNCATE},
    2028             :         {"TRUNCATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRUNCATE)},
    2029             :         {"REFERENCES", ACL_REFERENCES},
    2030             :         {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
    2031             :         {"TRIGGER", ACL_TRIGGER},
    2032             :         {"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
    2033             :         {"RULE", 0},          /* ignore old RULE privileges */
    2034             :         {"RULE WITH GRANT OPTION", 0},
    2035             :         {NULL, 0}
    2036             :     };
    2037             : 
    2038          65 :     return convert_any_priv_string(priv_type_text, table_priv_map);
    2039             : }
    2040             : 
    2041             : /*
    2042             :  * has_sequence_privilege variants
    2043             :  *      These are all named "has_sequence_privilege" at the SQL level.
    2044             :  *      They take various combinations of relation name, relation OID,
    2045             :  *      user name, user OID, or implicit user = current_user.
    2046             :  *
    2047             :  *      The result is a boolean value: true if user has the indicated
    2048             :  *      privilege, false if not.  The variants that take a relation OID
    2049             :  *      return NULL if the OID doesn't exist.
    2050             :  */
    2051             : 
    2052             : /*
    2053             :  * has_sequence_privilege_name_name
    2054             :  *      Check user privileges on a sequence given
    2055             :  *      name username, text sequencename, and text priv name.
    2056             :  */
    2057             : Datum
    2058           3 : has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
    2059             : {
    2060           3 :     Name        rolename = PG_GETARG_NAME(0);
    2061           3 :     text       *sequencename = PG_GETARG_TEXT_PP(1);
    2062           3 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2063             :     Oid         roleid;
    2064             :     Oid         sequenceoid;
    2065             :     AclMode     mode;
    2066             :     AclResult   aclresult;
    2067             : 
    2068           3 :     roleid = get_role_oid_or_public(NameStr(*rolename));
    2069           3 :     mode = convert_sequence_priv_string(priv_type_text);
    2070           2 :     sequenceoid = convert_table_name(sequencename);
    2071           2 :     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
    2072           1 :         ereport(ERROR,
    2073             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2074             :                  errmsg("\"%s\" is not a sequence",
    2075             :                         text_to_cstring(sequencename))));
    2076             : 
    2077           1 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2078             : 
    2079           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2080             : }
    2081             : 
    2082             : /*
    2083             :  * has_sequence_privilege_name
    2084             :  *      Check user privileges on a sequence given
    2085             :  *      text sequencename and text priv name.
    2086             :  *      current_user is assumed
    2087             :  */
    2088             : Datum
    2089           1 : has_sequence_privilege_name(PG_FUNCTION_ARGS)
    2090             : {
    2091           1 :     text       *sequencename = PG_GETARG_TEXT_PP(0);
    2092           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2093             :     Oid         roleid;
    2094             :     Oid         sequenceoid;
    2095             :     AclMode     mode;
    2096             :     AclResult   aclresult;
    2097             : 
    2098           1 :     roleid = GetUserId();
    2099           1 :     mode = convert_sequence_priv_string(priv_type_text);
    2100           1 :     sequenceoid = convert_table_name(sequencename);
    2101           1 :     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
    2102           0 :         ereport(ERROR,
    2103             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2104             :                  errmsg("\"%s\" is not a sequence",
    2105             :                         text_to_cstring(sequencename))));
    2106             : 
    2107           1 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2108             : 
    2109           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2110             : }
    2111             : 
    2112             : /*
    2113             :  * has_sequence_privilege_name_id
    2114             :  *      Check user privileges on a sequence given
    2115             :  *      name usename, sequence oid, and text priv name.
    2116             :  */
    2117             : Datum
    2118           0 : has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
    2119             : {
    2120           0 :     Name        username = PG_GETARG_NAME(0);
    2121           0 :     Oid         sequenceoid = PG_GETARG_OID(1);
    2122           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2123             :     Oid         roleid;
    2124             :     AclMode     mode;
    2125             :     AclResult   aclresult;
    2126             :     char        relkind;
    2127             : 
    2128           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2129           0 :     mode = convert_sequence_priv_string(priv_type_text);
    2130           0 :     relkind = get_rel_relkind(sequenceoid);
    2131           0 :     if (relkind == '\0')
    2132           0 :         PG_RETURN_NULL();
    2133           0 :     else if (relkind != RELKIND_SEQUENCE)
    2134           0 :         ereport(ERROR,
    2135             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2136             :                  errmsg("\"%s\" is not a sequence",
    2137             :                         get_rel_name(sequenceoid))));
    2138             : 
    2139           0 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2140             : 
    2141           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2142             : }
    2143             : 
    2144             : /*
    2145             :  * has_sequence_privilege_id
    2146             :  *      Check user privileges on a sequence given
    2147             :  *      sequence oid, and text priv name.
    2148             :  *      current_user is assumed
    2149             :  */
    2150             : Datum
    2151          19 : has_sequence_privilege_id(PG_FUNCTION_ARGS)
    2152             : {
    2153          19 :     Oid         sequenceoid = PG_GETARG_OID(0);
    2154          19 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2155             :     Oid         roleid;
    2156             :     AclMode     mode;
    2157             :     AclResult   aclresult;
    2158             :     char        relkind;
    2159             : 
    2160          19 :     roleid = GetUserId();
    2161          19 :     mode = convert_sequence_priv_string(priv_type_text);
    2162          19 :     relkind = get_rel_relkind(sequenceoid);
    2163          19 :     if (relkind == '\0')
    2164           0 :         PG_RETURN_NULL();
    2165          19 :     else if (relkind != RELKIND_SEQUENCE)
    2166           0 :         ereport(ERROR,
    2167             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2168             :                  errmsg("\"%s\" is not a sequence",
    2169             :                         get_rel_name(sequenceoid))));
    2170             : 
    2171          19 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2172             : 
    2173          19 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2174             : }
    2175             : 
    2176             : /*
    2177             :  * has_sequence_privilege_id_name
    2178             :  *      Check user privileges on a sequence given
    2179             :  *      roleid, text sequencename, and text priv name.
    2180             :  */
    2181             : Datum
    2182           0 : has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
    2183             : {
    2184           0 :     Oid         roleid = PG_GETARG_OID(0);
    2185           0 :     text       *sequencename = PG_GETARG_TEXT_PP(1);
    2186           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2187             :     Oid         sequenceoid;
    2188             :     AclMode     mode;
    2189             :     AclResult   aclresult;
    2190             : 
    2191           0 :     mode = convert_sequence_priv_string(priv_type_text);
    2192           0 :     sequenceoid = convert_table_name(sequencename);
    2193           0 :     if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
    2194           0 :         ereport(ERROR,
    2195             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2196             :                  errmsg("\"%s\" is not a sequence",
    2197             :                         text_to_cstring(sequencename))));
    2198             : 
    2199           0 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2200             : 
    2201           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2202             : }
    2203             : 
    2204             : /*
    2205             :  * has_sequence_privilege_id_id
    2206             :  *      Check user privileges on a sequence given
    2207             :  *      roleid, sequence oid, and text priv name.
    2208             :  */
    2209             : Datum
    2210           0 : has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
    2211             : {
    2212           0 :     Oid         roleid = PG_GETARG_OID(0);
    2213           0 :     Oid         sequenceoid = PG_GETARG_OID(1);
    2214           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2215             :     AclMode     mode;
    2216             :     AclResult   aclresult;
    2217             :     char        relkind;
    2218             : 
    2219           0 :     mode = convert_sequence_priv_string(priv_type_text);
    2220           0 :     relkind = get_rel_relkind(sequenceoid);
    2221           0 :     if (relkind == '\0')
    2222           0 :         PG_RETURN_NULL();
    2223           0 :     else if (relkind != RELKIND_SEQUENCE)
    2224           0 :         ereport(ERROR,
    2225             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2226             :                  errmsg("\"%s\" is not a sequence",
    2227             :                         get_rel_name(sequenceoid))));
    2228             : 
    2229           0 :     aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
    2230             : 
    2231           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2232             : }
    2233             : 
    2234             : /*
    2235             :  * convert_sequence_priv_string
    2236             :  *      Convert text string to AclMode value.
    2237             :  */
    2238             : static AclMode
    2239          23 : convert_sequence_priv_string(text *priv_type_text)
    2240             : {
    2241             :     static const priv_map sequence_priv_map[] = {
    2242             :         {"USAGE", ACL_USAGE},
    2243             :         {"SELECT", ACL_SELECT},
    2244             :         {"UPDATE", ACL_UPDATE},
    2245             :         {NULL, 0}
    2246             :     };
    2247             : 
    2248          23 :     return convert_any_priv_string(priv_type_text, sequence_priv_map);
    2249             : }
    2250             : 
    2251             : 
    2252             : /*
    2253             :  * has_any_column_privilege variants
    2254             :  *      These are all named "has_any_column_privilege" at the SQL level.
    2255             :  *      They take various combinations of relation name, relation OID,
    2256             :  *      user name, user OID, or implicit user = current_user.
    2257             :  *
    2258             :  *      The result is a boolean value: true if user has the indicated
    2259             :  *      privilege for any column of the table, false if not.  The variants
    2260             :  *      that take a relation OID return NULL if the OID doesn't exist.
    2261             :  */
    2262             : 
    2263             : /*
    2264             :  * has_any_column_privilege_name_name
    2265             :  *      Check user privileges on any column of a table given
    2266             :  *      name username, text tablename, and text priv name.
    2267             :  */
    2268             : Datum
    2269           0 : has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
    2270             : {
    2271           0 :     Name        rolename = PG_GETARG_NAME(0);
    2272           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2273           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2274             :     Oid         roleid;
    2275             :     Oid         tableoid;
    2276             :     AclMode     mode;
    2277             :     AclResult   aclresult;
    2278             : 
    2279           0 :     roleid = get_role_oid_or_public(NameStr(*rolename));
    2280           0 :     tableoid = convert_table_name(tablename);
    2281           0 :     mode = convert_column_priv_string(priv_type_text);
    2282             : 
    2283             :     /* First check at table level, then examine each column if needed */
    2284           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2285           0 :     if (aclresult != ACLCHECK_OK)
    2286           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2287             :                                               ACLMASK_ANY);
    2288             : 
    2289           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2290             : }
    2291             : 
    2292             : /*
    2293             :  * has_any_column_privilege_name
    2294             :  *      Check user privileges on any column of a table given
    2295             :  *      text tablename and text priv name.
    2296             :  *      current_user is assumed
    2297             :  */
    2298             : Datum
    2299           0 : has_any_column_privilege_name(PG_FUNCTION_ARGS)
    2300             : {
    2301           0 :     text       *tablename = PG_GETARG_TEXT_PP(0);
    2302           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2303             :     Oid         roleid;
    2304             :     Oid         tableoid;
    2305             :     AclMode     mode;
    2306             :     AclResult   aclresult;
    2307             : 
    2308           0 :     roleid = GetUserId();
    2309           0 :     tableoid = convert_table_name(tablename);
    2310           0 :     mode = convert_column_priv_string(priv_type_text);
    2311             : 
    2312             :     /* First check at table level, then examine each column if needed */
    2313           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2314           0 :     if (aclresult != ACLCHECK_OK)
    2315           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2316             :                                               ACLMASK_ANY);
    2317             : 
    2318           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2319             : }
    2320             : 
    2321             : /*
    2322             :  * has_any_column_privilege_name_id
    2323             :  *      Check user privileges on any column of a table given
    2324             :  *      name usename, table oid, and text priv name.
    2325             :  */
    2326             : Datum
    2327           0 : has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
    2328             : {
    2329           0 :     Name        username = PG_GETARG_NAME(0);
    2330           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2331           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2332             :     Oid         roleid;
    2333             :     AclMode     mode;
    2334             :     AclResult   aclresult;
    2335             : 
    2336           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2337           0 :     mode = convert_column_priv_string(priv_type_text);
    2338             : 
    2339           0 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    2340           0 :         PG_RETURN_NULL();
    2341             : 
    2342             :     /* First check at table level, then examine each column if needed */
    2343           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2344           0 :     if (aclresult != ACLCHECK_OK)
    2345           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2346             :                                               ACLMASK_ANY);
    2347             : 
    2348           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2349             : }
    2350             : 
    2351             : /*
    2352             :  * has_any_column_privilege_id
    2353             :  *      Check user privileges on any column of a table given
    2354             :  *      table oid, and text priv name.
    2355             :  *      current_user is assumed
    2356             :  */
    2357             : Datum
    2358           0 : has_any_column_privilege_id(PG_FUNCTION_ARGS)
    2359             : {
    2360           0 :     Oid         tableoid = PG_GETARG_OID(0);
    2361           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2362             :     Oid         roleid;
    2363             :     AclMode     mode;
    2364             :     AclResult   aclresult;
    2365             : 
    2366           0 :     roleid = GetUserId();
    2367           0 :     mode = convert_column_priv_string(priv_type_text);
    2368             : 
    2369           0 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    2370           0 :         PG_RETURN_NULL();
    2371             : 
    2372             :     /* First check at table level, then examine each column if needed */
    2373           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2374           0 :     if (aclresult != ACLCHECK_OK)
    2375           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2376             :                                               ACLMASK_ANY);
    2377             : 
    2378           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2379             : }
    2380             : 
    2381             : /*
    2382             :  * has_any_column_privilege_id_name
    2383             :  *      Check user privileges on any column of a table given
    2384             :  *      roleid, text tablename, and text priv name.
    2385             :  */
    2386             : Datum
    2387           0 : has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
    2388             : {
    2389           0 :     Oid         roleid = PG_GETARG_OID(0);
    2390           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2391           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2392             :     Oid         tableoid;
    2393             :     AclMode     mode;
    2394             :     AclResult   aclresult;
    2395             : 
    2396           0 :     tableoid = convert_table_name(tablename);
    2397           0 :     mode = convert_column_priv_string(priv_type_text);
    2398             : 
    2399             :     /* First check at table level, then examine each column if needed */
    2400           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2401           0 :     if (aclresult != ACLCHECK_OK)
    2402           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2403             :                                               ACLMASK_ANY);
    2404             : 
    2405           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2406             : }
    2407             : 
    2408             : /*
    2409             :  * has_any_column_privilege_id_id
    2410             :  *      Check user privileges on any column of a table given
    2411             :  *      roleid, table oid, and text priv name.
    2412             :  */
    2413             : Datum
    2414           0 : has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
    2415             : {
    2416           0 :     Oid         roleid = PG_GETARG_OID(0);
    2417           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2418           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2419             :     AclMode     mode;
    2420             :     AclResult   aclresult;
    2421             : 
    2422           0 :     mode = convert_column_priv_string(priv_type_text);
    2423             : 
    2424           0 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    2425           0 :         PG_RETURN_NULL();
    2426             : 
    2427             :     /* First check at table level, then examine each column if needed */
    2428           0 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2429           0 :     if (aclresult != ACLCHECK_OK)
    2430           0 :         aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
    2431             :                                               ACLMASK_ANY);
    2432             : 
    2433           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2434             : }
    2435             : 
    2436             : 
    2437             : /*
    2438             :  * has_column_privilege variants
    2439             :  *      These are all named "has_column_privilege" at the SQL level.
    2440             :  *      They take various combinations of relation name, relation OID,
    2441             :  *      column name, column attnum, user name, user OID, or
    2442             :  *      implicit user = current_user.
    2443             :  *
    2444             :  *      The result is a boolean value: true if user has the indicated
    2445             :  *      privilege, false if not.  The variants that take a relation OID
    2446             :  *      and an integer attnum return NULL (rather than throwing an error)
    2447             :  *      if the column doesn't exist or is dropped.
    2448             :  */
    2449             : 
    2450             : /*
    2451             :  * column_privilege_check: check column privileges, but don't throw an error
    2452             :  *      for dropped column or table
    2453             :  *
    2454             :  * Returns 1 if have the privilege, 0 if not, -1 if dropped column/table.
    2455             :  */
    2456             : static int
    2457         298 : column_privilege_check(Oid tableoid, AttrNumber attnum,
    2458             :                        Oid roleid, AclMode mode)
    2459             : {
    2460             :     AclResult   aclresult;
    2461             :     HeapTuple   attTuple;
    2462             :     Form_pg_attribute attributeForm;
    2463             : 
    2464             :     /*
    2465             :      * First check if we have the privilege at the table level.  We check
    2466             :      * existence of the pg_class row before risking calling pg_class_aclcheck.
    2467             :      * Note: it might seem there's a race condition against concurrent DROP,
    2468             :      * but really it's safe because there will be no syscache flush between
    2469             :      * here and there.  So if we see the row in the syscache, so will
    2470             :      * pg_class_aclcheck.
    2471             :      */
    2472         298 :     if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
    2473           0 :         return -1;
    2474             : 
    2475         298 :     aclresult = pg_class_aclcheck(tableoid, roleid, mode);
    2476             : 
    2477         298 :     if (aclresult == ACLCHECK_OK)
    2478         298 :         return true;
    2479             : 
    2480             :     /*
    2481             :      * No table privilege, so try per-column privileges.  Again, we have to
    2482             :      * check for dropped attribute first, and we rely on the syscache not to
    2483             :      * notice a concurrent drop before pg_attribute_aclcheck fetches the row.
    2484             :      */
    2485           0 :     attTuple = SearchSysCache2(ATTNUM,
    2486             :                                ObjectIdGetDatum(tableoid),
    2487             :                                Int16GetDatum(attnum));
    2488           0 :     if (!HeapTupleIsValid(attTuple))
    2489           0 :         return -1;
    2490           0 :     attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
    2491           0 :     if (attributeForm->attisdropped)
    2492             :     {
    2493           0 :         ReleaseSysCache(attTuple);
    2494           0 :         return -1;
    2495             :     }
    2496           0 :     ReleaseSysCache(attTuple);
    2497             : 
    2498           0 :     aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode);
    2499             : 
    2500           0 :     return (aclresult == ACLCHECK_OK);
    2501             : }
    2502             : 
    2503             : /*
    2504             :  * has_column_privilege_name_name_name
    2505             :  *      Check user privileges on a column given
    2506             :  *      name username, text tablename, text colname, and text priv name.
    2507             :  */
    2508             : Datum
    2509           0 : has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
    2510             : {
    2511           0 :     Name        rolename = PG_GETARG_NAME(0);
    2512           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2513           0 :     text       *column = PG_GETARG_TEXT_PP(2);
    2514           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2515             :     Oid         roleid;
    2516             :     Oid         tableoid;
    2517             :     AttrNumber  colattnum;
    2518             :     AclMode     mode;
    2519             :     int         privresult;
    2520             : 
    2521           0 :     roleid = get_role_oid_or_public(NameStr(*rolename));
    2522           0 :     tableoid = convert_table_name(tablename);
    2523           0 :     colattnum = convert_column_name(tableoid, column);
    2524           0 :     mode = convert_column_priv_string(priv_type_text);
    2525             : 
    2526           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2527           0 :     if (privresult < 0)
    2528           0 :         PG_RETURN_NULL();
    2529           0 :     PG_RETURN_BOOL(privresult);
    2530             : }
    2531             : 
    2532             : /*
    2533             :  * has_column_privilege_name_name_attnum
    2534             :  *      Check user privileges on a column given
    2535             :  *      name username, text tablename, int attnum, and text priv name.
    2536             :  */
    2537             : Datum
    2538           0 : has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
    2539             : {
    2540           0 :     Name        rolename = PG_GETARG_NAME(0);
    2541           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2542           0 :     AttrNumber  colattnum = PG_GETARG_INT16(2);
    2543           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2544             :     Oid         roleid;
    2545             :     Oid         tableoid;
    2546             :     AclMode     mode;
    2547             :     int         privresult;
    2548             : 
    2549           0 :     roleid = get_role_oid_or_public(NameStr(*rolename));
    2550           0 :     tableoid = convert_table_name(tablename);
    2551           0 :     mode = convert_column_priv_string(priv_type_text);
    2552             : 
    2553           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2554           0 :     if (privresult < 0)
    2555           0 :         PG_RETURN_NULL();
    2556           0 :     PG_RETURN_BOOL(privresult);
    2557             : }
    2558             : 
    2559             : /*
    2560             :  * has_column_privilege_name_id_name
    2561             :  *      Check user privileges on a column given
    2562             :  *      name username, table oid, text colname, and text priv name.
    2563             :  */
    2564             : Datum
    2565           0 : has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
    2566             : {
    2567           0 :     Name        username = PG_GETARG_NAME(0);
    2568           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2569           0 :     text       *column = PG_GETARG_TEXT_PP(2);
    2570           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2571             :     Oid         roleid;
    2572             :     AttrNumber  colattnum;
    2573             :     AclMode     mode;
    2574             :     int         privresult;
    2575             : 
    2576           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2577           0 :     colattnum = convert_column_name(tableoid, column);
    2578           0 :     mode = convert_column_priv_string(priv_type_text);
    2579             : 
    2580           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2581           0 :     if (privresult < 0)
    2582           0 :         PG_RETURN_NULL();
    2583           0 :     PG_RETURN_BOOL(privresult);
    2584             : }
    2585             : 
    2586             : /*
    2587             :  * has_column_privilege_name_id_attnum
    2588             :  *      Check user privileges on a column given
    2589             :  *      name username, table oid, int attnum, and text priv name.
    2590             :  */
    2591             : Datum
    2592           0 : has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS)
    2593             : {
    2594           0 :     Name        username = PG_GETARG_NAME(0);
    2595           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2596           0 :     AttrNumber  colattnum = PG_GETARG_INT16(2);
    2597           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2598             :     Oid         roleid;
    2599             :     AclMode     mode;
    2600             :     int         privresult;
    2601             : 
    2602           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2603           0 :     mode = convert_column_priv_string(priv_type_text);
    2604             : 
    2605           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2606           0 :     if (privresult < 0)
    2607           0 :         PG_RETURN_NULL();
    2608           0 :     PG_RETURN_BOOL(privresult);
    2609             : }
    2610             : 
    2611             : /*
    2612             :  * has_column_privilege_id_name_name
    2613             :  *      Check user privileges on a column given
    2614             :  *      oid roleid, text tablename, text colname, and text priv name.
    2615             :  */
    2616             : Datum
    2617           0 : has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
    2618             : {
    2619           0 :     Oid         roleid = PG_GETARG_OID(0);
    2620           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2621           0 :     text       *column = PG_GETARG_TEXT_PP(2);
    2622           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2623             :     Oid         tableoid;
    2624             :     AttrNumber  colattnum;
    2625             :     AclMode     mode;
    2626             :     int         privresult;
    2627             : 
    2628           0 :     tableoid = convert_table_name(tablename);
    2629           0 :     colattnum = convert_column_name(tableoid, column);
    2630           0 :     mode = convert_column_priv_string(priv_type_text);
    2631             : 
    2632           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2633           0 :     if (privresult < 0)
    2634           0 :         PG_RETURN_NULL();
    2635           0 :     PG_RETURN_BOOL(privresult);
    2636             : }
    2637             : 
    2638             : /*
    2639             :  * has_column_privilege_id_name_attnum
    2640             :  *      Check user privileges on a column given
    2641             :  *      oid roleid, text tablename, int attnum, and text priv name.
    2642             :  */
    2643             : Datum
    2644           0 : has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS)
    2645             : {
    2646           0 :     Oid         roleid = PG_GETARG_OID(0);
    2647           0 :     text       *tablename = PG_GETARG_TEXT_PP(1);
    2648           0 :     AttrNumber  colattnum = PG_GETARG_INT16(2);
    2649           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2650             :     Oid         tableoid;
    2651             :     AclMode     mode;
    2652             :     int         privresult;
    2653             : 
    2654           0 :     tableoid = convert_table_name(tablename);
    2655           0 :     mode = convert_column_priv_string(priv_type_text);
    2656             : 
    2657           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2658           0 :     if (privresult < 0)
    2659           0 :         PG_RETURN_NULL();
    2660           0 :     PG_RETURN_BOOL(privresult);
    2661             : }
    2662             : 
    2663             : /*
    2664             :  * has_column_privilege_id_id_name
    2665             :  *      Check user privileges on a column given
    2666             :  *      oid roleid, table oid, text colname, and text priv name.
    2667             :  */
    2668             : Datum
    2669           0 : has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
    2670             : {
    2671           0 :     Oid         roleid = PG_GETARG_OID(0);
    2672           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2673           0 :     text       *column = PG_GETARG_TEXT_PP(2);
    2674           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2675             :     AttrNumber  colattnum;
    2676             :     AclMode     mode;
    2677             :     int         privresult;
    2678             : 
    2679           0 :     colattnum = convert_column_name(tableoid, column);
    2680           0 :     mode = convert_column_priv_string(priv_type_text);
    2681             : 
    2682           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2683           0 :     if (privresult < 0)
    2684           0 :         PG_RETURN_NULL();
    2685           0 :     PG_RETURN_BOOL(privresult);
    2686             : }
    2687             : 
    2688             : /*
    2689             :  * has_column_privilege_id_id_attnum
    2690             :  *      Check user privileges on a column given
    2691             :  *      oid roleid, table oid, int attnum, and text priv name.
    2692             :  */
    2693             : Datum
    2694           0 : has_column_privilege_id_id_attnum(PG_FUNCTION_ARGS)
    2695             : {
    2696           0 :     Oid         roleid = PG_GETARG_OID(0);
    2697           0 :     Oid         tableoid = PG_GETARG_OID(1);
    2698           0 :     AttrNumber  colattnum = PG_GETARG_INT16(2);
    2699           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(3);
    2700             :     AclMode     mode;
    2701             :     int         privresult;
    2702             : 
    2703           0 :     mode = convert_column_priv_string(priv_type_text);
    2704             : 
    2705           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2706           0 :     if (privresult < 0)
    2707           0 :         PG_RETURN_NULL();
    2708           0 :     PG_RETURN_BOOL(privresult);
    2709             : }
    2710             : 
    2711             : /*
    2712             :  * has_column_privilege_name_name
    2713             :  *      Check user privileges on a column given
    2714             :  *      text tablename, text colname, and text priv name.
    2715             :  *      current_user is assumed
    2716             :  */
    2717             : Datum
    2718           0 : has_column_privilege_name_name(PG_FUNCTION_ARGS)
    2719             : {
    2720           0 :     text       *tablename = PG_GETARG_TEXT_PP(0);
    2721           0 :     text       *column = PG_GETARG_TEXT_PP(1);
    2722           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2723             :     Oid         roleid;
    2724             :     Oid         tableoid;
    2725             :     AttrNumber  colattnum;
    2726             :     AclMode     mode;
    2727             :     int         privresult;
    2728             : 
    2729           0 :     roleid = GetUserId();
    2730           0 :     tableoid = convert_table_name(tablename);
    2731           0 :     colattnum = convert_column_name(tableoid, column);
    2732           0 :     mode = convert_column_priv_string(priv_type_text);
    2733             : 
    2734           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2735           0 :     if (privresult < 0)
    2736           0 :         PG_RETURN_NULL();
    2737           0 :     PG_RETURN_BOOL(privresult);
    2738             : }
    2739             : 
    2740             : /*
    2741             :  * has_column_privilege_name_attnum
    2742             :  *      Check user privileges on a column given
    2743             :  *      text tablename, int attnum, and text priv name.
    2744             :  *      current_user is assumed
    2745             :  */
    2746             : Datum
    2747           0 : has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
    2748             : {
    2749           0 :     text       *tablename = PG_GETARG_TEXT_PP(0);
    2750           0 :     AttrNumber  colattnum = PG_GETARG_INT16(1);
    2751           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2752             :     Oid         roleid;
    2753             :     Oid         tableoid;
    2754             :     AclMode     mode;
    2755             :     int         privresult;
    2756             : 
    2757           0 :     roleid = GetUserId();
    2758           0 :     tableoid = convert_table_name(tablename);
    2759           0 :     mode = convert_column_priv_string(priv_type_text);
    2760             : 
    2761           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2762           0 :     if (privresult < 0)
    2763           0 :         PG_RETURN_NULL();
    2764           0 :     PG_RETURN_BOOL(privresult);
    2765             : }
    2766             : 
    2767             : /*
    2768             :  * has_column_privilege_id_name
    2769             :  *      Check user privileges on a column given
    2770             :  *      table oid, text colname, and text priv name.
    2771             :  *      current_user is assumed
    2772             :  */
    2773             : Datum
    2774           0 : has_column_privilege_id_name(PG_FUNCTION_ARGS)
    2775             : {
    2776           0 :     Oid         tableoid = PG_GETARG_OID(0);
    2777           0 :     text       *column = PG_GETARG_TEXT_PP(1);
    2778           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2779             :     Oid         roleid;
    2780             :     AttrNumber  colattnum;
    2781             :     AclMode     mode;
    2782             :     int         privresult;
    2783             : 
    2784           0 :     roleid = GetUserId();
    2785           0 :     colattnum = convert_column_name(tableoid, column);
    2786           0 :     mode = convert_column_priv_string(priv_type_text);
    2787             : 
    2788           0 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2789           0 :     if (privresult < 0)
    2790           0 :         PG_RETURN_NULL();
    2791           0 :     PG_RETURN_BOOL(privresult);
    2792             : }
    2793             : 
    2794             : /*
    2795             :  * has_column_privilege_id_attnum
    2796             :  *      Check user privileges on a column given
    2797             :  *      table oid, int attnum, and text priv name.
    2798             :  *      current_user is assumed
    2799             :  */
    2800             : Datum
    2801         298 : has_column_privilege_id_attnum(PG_FUNCTION_ARGS)
    2802             : {
    2803         298 :     Oid         tableoid = PG_GETARG_OID(0);
    2804         298 :     AttrNumber  colattnum = PG_GETARG_INT16(1);
    2805         298 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2806             :     Oid         roleid;
    2807             :     AclMode     mode;
    2808             :     int         privresult;
    2809             : 
    2810         298 :     roleid = GetUserId();
    2811         298 :     mode = convert_column_priv_string(priv_type_text);
    2812             : 
    2813         298 :     privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
    2814         298 :     if (privresult < 0)
    2815           0 :         PG_RETURN_NULL();
    2816         298 :     PG_RETURN_BOOL(privresult);
    2817             : }
    2818             : 
    2819             : /*
    2820             :  *      Support routines for has_column_privilege family.
    2821             :  */
    2822             : 
    2823             : /*
    2824             :  * Given a table OID and a column name expressed as a string, look it up
    2825             :  * and return the column number
    2826             :  */
    2827             : static AttrNumber
    2828           0 : convert_column_name(Oid tableoid, text *column)
    2829             : {
    2830             :     AttrNumber  attnum;
    2831             :     char       *colname;
    2832             : 
    2833           0 :     colname = text_to_cstring(column);
    2834           0 :     attnum = get_attnum(tableoid, colname);
    2835           0 :     if (attnum == InvalidAttrNumber)
    2836           0 :         ereport(ERROR,
    2837             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    2838             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    2839             :                         colname, get_rel_name(tableoid))));
    2840           0 :     pfree(colname);
    2841           0 :     return attnum;
    2842             : }
    2843             : 
    2844             : /*
    2845             :  * convert_column_priv_string
    2846             :  *      Convert text string to AclMode value.
    2847             :  */
    2848             : static AclMode
    2849         298 : convert_column_priv_string(text *priv_type_text)
    2850             : {
    2851             :     static const priv_map column_priv_map[] = {
    2852             :         {"SELECT", ACL_SELECT},
    2853             :         {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
    2854             :         {"INSERT", ACL_INSERT},
    2855             :         {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
    2856             :         {"UPDATE", ACL_UPDATE},
    2857             :         {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
    2858             :         {"REFERENCES", ACL_REFERENCES},
    2859             :         {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
    2860             :         {NULL, 0}
    2861             :     };
    2862             : 
    2863         298 :     return convert_any_priv_string(priv_type_text, column_priv_map);
    2864             : }
    2865             : 
    2866             : 
    2867             : /*
    2868             :  * has_database_privilege variants
    2869             :  *      These are all named "has_database_privilege" at the SQL level.
    2870             :  *      They take various combinations of database name, database OID,
    2871             :  *      user name, user OID, or implicit user = current_user.
    2872             :  *
    2873             :  *      The result is a boolean value: true if user has the indicated
    2874             :  *      privilege, false if not, or NULL if object doesn't exist.
    2875             :  */
    2876             : 
    2877             : /*
    2878             :  * has_database_privilege_name_name
    2879             :  *      Check user privileges on a database given
    2880             :  *      name username, text databasename, and text priv name.
    2881             :  */
    2882             : Datum
    2883           0 : has_database_privilege_name_name(PG_FUNCTION_ARGS)
    2884             : {
    2885           0 :     Name        username = PG_GETARG_NAME(0);
    2886           0 :     text       *databasename = PG_GETARG_TEXT_PP(1);
    2887           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2888             :     Oid         roleid;
    2889             :     Oid         databaseoid;
    2890             :     AclMode     mode;
    2891             :     AclResult   aclresult;
    2892             : 
    2893           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2894           0 :     databaseoid = convert_database_name(databasename);
    2895           0 :     mode = convert_database_priv_string(priv_type_text);
    2896             : 
    2897           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    2898             : 
    2899           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2900             : }
    2901             : 
    2902             : /*
    2903             :  * has_database_privilege_name
    2904             :  *      Check user privileges on a database given
    2905             :  *      text databasename and text priv name.
    2906             :  *      current_user is assumed
    2907             :  */
    2908             : Datum
    2909           0 : has_database_privilege_name(PG_FUNCTION_ARGS)
    2910             : {
    2911           0 :     text       *databasename = PG_GETARG_TEXT_PP(0);
    2912           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2913             :     Oid         roleid;
    2914             :     Oid         databaseoid;
    2915             :     AclMode     mode;
    2916             :     AclResult   aclresult;
    2917             : 
    2918           0 :     roleid = GetUserId();
    2919           0 :     databaseoid = convert_database_name(databasename);
    2920           0 :     mode = convert_database_priv_string(priv_type_text);
    2921             : 
    2922           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    2923             : 
    2924           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2925             : }
    2926             : 
    2927             : /*
    2928             :  * has_database_privilege_name_id
    2929             :  *      Check user privileges on a database given
    2930             :  *      name usename, database oid, and text priv name.
    2931             :  */
    2932             : Datum
    2933           0 : has_database_privilege_name_id(PG_FUNCTION_ARGS)
    2934             : {
    2935           0 :     Name        username = PG_GETARG_NAME(0);
    2936           0 :     Oid         databaseoid = PG_GETARG_OID(1);
    2937           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2938             :     Oid         roleid;
    2939             :     AclMode     mode;
    2940             :     AclResult   aclresult;
    2941             : 
    2942           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    2943           0 :     mode = convert_database_priv_string(priv_type_text);
    2944             : 
    2945           0 :     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
    2946           0 :         PG_RETURN_NULL();
    2947             : 
    2948           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    2949             : 
    2950           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2951             : }
    2952             : 
    2953             : /*
    2954             :  * has_database_privilege_id
    2955             :  *      Check user privileges on a database given
    2956             :  *      database oid, and text priv name.
    2957             :  *      current_user is assumed
    2958             :  */
    2959             : Datum
    2960           0 : has_database_privilege_id(PG_FUNCTION_ARGS)
    2961             : {
    2962           0 :     Oid         databaseoid = PG_GETARG_OID(0);
    2963           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    2964             :     Oid         roleid;
    2965             :     AclMode     mode;
    2966             :     AclResult   aclresult;
    2967             : 
    2968           0 :     roleid = GetUserId();
    2969           0 :     mode = convert_database_priv_string(priv_type_text);
    2970             : 
    2971           0 :     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
    2972           0 :         PG_RETURN_NULL();
    2973             : 
    2974           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    2975             : 
    2976           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    2977             : }
    2978             : 
    2979             : /*
    2980             :  * has_database_privilege_id_name
    2981             :  *      Check user privileges on a database given
    2982             :  *      roleid, text databasename, and text priv name.
    2983             :  */
    2984             : Datum
    2985           0 : has_database_privilege_id_name(PG_FUNCTION_ARGS)
    2986             : {
    2987           0 :     Oid         roleid = PG_GETARG_OID(0);
    2988           0 :     text       *databasename = PG_GETARG_TEXT_PP(1);
    2989           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    2990             :     Oid         databaseoid;
    2991             :     AclMode     mode;
    2992             :     AclResult   aclresult;
    2993             : 
    2994           0 :     databaseoid = convert_database_name(databasename);
    2995           0 :     mode = convert_database_priv_string(priv_type_text);
    2996             : 
    2997           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    2998             : 
    2999           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3000             : }
    3001             : 
    3002             : /*
    3003             :  * has_database_privilege_id_id
    3004             :  *      Check user privileges on a database given
    3005             :  *      roleid, database oid, and text priv name.
    3006             :  */
    3007             : Datum
    3008           0 : has_database_privilege_id_id(PG_FUNCTION_ARGS)
    3009             : {
    3010           0 :     Oid         roleid = PG_GETARG_OID(0);
    3011           0 :     Oid         databaseoid = PG_GETARG_OID(1);
    3012           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3013             :     AclMode     mode;
    3014             :     AclResult   aclresult;
    3015             : 
    3016           0 :     mode = convert_database_priv_string(priv_type_text);
    3017             : 
    3018           0 :     if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
    3019           0 :         PG_RETURN_NULL();
    3020             : 
    3021           0 :     aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
    3022             : 
    3023           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3024             : }
    3025             : 
    3026             : /*
    3027             :  *      Support routines for has_database_privilege family.
    3028             :  */
    3029             : 
    3030             : /*
    3031             :  * Given a database name expressed as a string, look it up and return Oid
    3032             :  */
    3033             : static Oid
    3034           0 : convert_database_name(text *databasename)
    3035             : {
    3036           0 :     char       *dbname = text_to_cstring(databasename);
    3037             : 
    3038           0 :     return get_database_oid(dbname, false);
    3039             : }
    3040             : 
    3041             : /*
    3042             :  * convert_database_priv_string
    3043             :  *      Convert text string to AclMode value.
    3044             :  */
    3045             : static AclMode
    3046           0 : convert_database_priv_string(text *priv_type_text)
    3047             : {
    3048             :     static const priv_map database_priv_map[] = {
    3049             :         {"CREATE", ACL_CREATE},
    3050             :         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    3051             :         {"TEMPORARY", ACL_CREATE_TEMP},
    3052             :         {"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
    3053             :         {"TEMP", ACL_CREATE_TEMP},
    3054             :         {"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
    3055             :         {"CONNECT", ACL_CONNECT},
    3056             :         {"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
    3057             :         {NULL, 0}
    3058             :     };
    3059             : 
    3060           0 :     return convert_any_priv_string(priv_type_text, database_priv_map);
    3061             : 
    3062             : }
    3063             : 
    3064             : 
    3065             : /*
    3066             :  * has_foreign_data_wrapper_privilege variants
    3067             :  *      These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
    3068             :  *      They take various combinations of foreign-data wrapper name,
    3069             :  *      fdw OID, user name, user OID, or implicit user = current_user.
    3070             :  *
    3071             :  *      The result is a boolean value: true if user has the indicated
    3072             :  *      privilege, false if not.
    3073             :  */
    3074             : 
    3075             : /*
    3076             :  * has_foreign_data_wrapper_privilege_name_name
    3077             :  *      Check user privileges on a foreign-data wrapper given
    3078             :  *      name username, text fdwname, and text priv name.
    3079             :  */
    3080             : Datum
    3081           2 : has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
    3082             : {
    3083           2 :     Name        username = PG_GETARG_NAME(0);
    3084           2 :     text       *fdwname = PG_GETARG_TEXT_PP(1);
    3085           2 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3086             :     Oid         roleid;
    3087             :     Oid         fdwid;
    3088             :     AclMode     mode;
    3089             :     AclResult   aclresult;
    3090             : 
    3091           2 :     roleid = get_role_oid_or_public(NameStr(*username));
    3092           2 :     fdwid = convert_foreign_data_wrapper_name(fdwname);
    3093           2 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3094             : 
    3095           2 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3096             : 
    3097           2 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3098             : }
    3099             : 
    3100             : /*
    3101             :  * has_foreign_data_wrapper_privilege_name
    3102             :  *      Check user privileges on a foreign-data wrapper given
    3103             :  *      text fdwname and text priv name.
    3104             :  *      current_user is assumed
    3105             :  */
    3106             : Datum
    3107           1 : has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
    3108             : {
    3109           1 :     text       *fdwname = PG_GETARG_TEXT_PP(0);
    3110           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3111             :     Oid         roleid;
    3112             :     Oid         fdwid;
    3113             :     AclMode     mode;
    3114             :     AclResult   aclresult;
    3115             : 
    3116           1 :     roleid = GetUserId();
    3117           1 :     fdwid = convert_foreign_data_wrapper_name(fdwname);
    3118           1 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3119             : 
    3120           1 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3121             : 
    3122           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3123             : }
    3124             : 
    3125             : /*
    3126             :  * has_foreign_data_wrapper_privilege_name_id
    3127             :  *      Check user privileges on a foreign-data wrapper given
    3128             :  *      name usename, foreign-data wrapper oid, and text priv name.
    3129             :  */
    3130             : Datum
    3131           1 : has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
    3132             : {
    3133           1 :     Name        username = PG_GETARG_NAME(0);
    3134           1 :     Oid         fdwid = PG_GETARG_OID(1);
    3135           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3136             :     Oid         roleid;
    3137             :     AclMode     mode;
    3138             :     AclResult   aclresult;
    3139             : 
    3140           1 :     roleid = get_role_oid_or_public(NameStr(*username));
    3141           1 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3142             : 
    3143           1 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3144             : 
    3145           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3146             : }
    3147             : 
    3148             : /*
    3149             :  * has_foreign_data_wrapper_privilege_id
    3150             :  *      Check user privileges on a foreign-data wrapper given
    3151             :  *      foreign-data wrapper oid, and text priv name.
    3152             :  *      current_user is assumed
    3153             :  */
    3154             : Datum
    3155           1 : has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
    3156             : {
    3157           1 :     Oid         fdwid = PG_GETARG_OID(0);
    3158           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3159             :     Oid         roleid;
    3160             :     AclMode     mode;
    3161             :     AclResult   aclresult;
    3162             : 
    3163           1 :     roleid = GetUserId();
    3164           1 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3165             : 
    3166           1 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3167             : 
    3168           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3169             : }
    3170             : 
    3171             : /*
    3172             :  * has_foreign_data_wrapper_privilege_id_name
    3173             :  *      Check user privileges on a foreign-data wrapper given
    3174             :  *      roleid, text fdwname, and text priv name.
    3175             :  */
    3176             : Datum
    3177           1 : has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
    3178             : {
    3179           1 :     Oid         roleid = PG_GETARG_OID(0);
    3180           1 :     text       *fdwname = PG_GETARG_TEXT_PP(1);
    3181           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3182             :     Oid         fdwid;
    3183             :     AclMode     mode;
    3184             :     AclResult   aclresult;
    3185             : 
    3186           1 :     fdwid = convert_foreign_data_wrapper_name(fdwname);
    3187           1 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3188             : 
    3189           1 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3190             : 
    3191           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3192             : }
    3193             : 
    3194             : /*
    3195             :  * has_foreign_data_wrapper_privilege_id_id
    3196             :  *      Check user privileges on a foreign-data wrapper given
    3197             :  *      roleid, fdw oid, and text priv name.
    3198             :  */
    3199             : Datum
    3200           1 : has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
    3201             : {
    3202           1 :     Oid         roleid = PG_GETARG_OID(0);
    3203           1 :     Oid         fdwid = PG_GETARG_OID(1);
    3204           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3205             :     AclMode     mode;
    3206             :     AclResult   aclresult;
    3207             : 
    3208           1 :     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
    3209             : 
    3210           1 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
    3211             : 
    3212           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3213             : }
    3214             : 
    3215             : /*
    3216             :  *      Support routines for has_foreign_data_wrapper_privilege family.
    3217             :  */
    3218             : 
    3219             : /*
    3220             :  * Given a FDW name expressed as a string, look it up and return Oid
    3221             :  */
    3222             : static Oid
    3223           4 : convert_foreign_data_wrapper_name(text *fdwname)
    3224             : {
    3225           4 :     char       *fdwstr = text_to_cstring(fdwname);
    3226             : 
    3227           4 :     return get_foreign_data_wrapper_oid(fdwstr, false);
    3228             : }
    3229             : 
    3230             : /*
    3231             :  * convert_foreign_data_wrapper_priv_string
    3232             :  *      Convert text string to AclMode value.
    3233             :  */
    3234             : static AclMode
    3235           7 : convert_foreign_data_wrapper_priv_string(text *priv_type_text)
    3236             : {
    3237             :     static const priv_map foreign_data_wrapper_priv_map[] = {
    3238             :         {"USAGE", ACL_USAGE},
    3239             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
    3240             :         {NULL, 0}
    3241             :     };
    3242             : 
    3243           7 :     return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map);
    3244             : }
    3245             : 
    3246             : 
    3247             : /*
    3248             :  * has_function_privilege variants
    3249             :  *      These are all named "has_function_privilege" at the SQL level.
    3250             :  *      They take various combinations of function name, function OID,
    3251             :  *      user name, user OID, or implicit user = current_user.
    3252             :  *
    3253             :  *      The result is a boolean value: true if user has the indicated
    3254             :  *      privilege, false if not, or NULL if object doesn't exist.
    3255             :  */
    3256             : 
    3257             : /*
    3258             :  * has_function_privilege_name_name
    3259             :  *      Check user privileges on a function given
    3260             :  *      name username, text functionname, and text priv name.
    3261             :  */
    3262             : Datum
    3263           4 : has_function_privilege_name_name(PG_FUNCTION_ARGS)
    3264             : {
    3265           4 :     Name        username = PG_GETARG_NAME(0);
    3266           4 :     text       *functionname = PG_GETARG_TEXT_PP(1);
    3267           4 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3268             :     Oid         roleid;
    3269             :     Oid         functionoid;
    3270             :     AclMode     mode;
    3271             :     AclResult   aclresult;
    3272             : 
    3273           4 :     roleid = get_role_oid_or_public(NameStr(*username));
    3274           4 :     functionoid = convert_function_name(functionname);
    3275           4 :     mode = convert_function_priv_string(priv_type_text);
    3276             : 
    3277           4 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3278             : 
    3279           4 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3280             : }
    3281             : 
    3282             : /*
    3283             :  * has_function_privilege_name
    3284             :  *      Check user privileges on a function given
    3285             :  *      text functionname and text priv name.
    3286             :  *      current_user is assumed
    3287             :  */
    3288             : Datum
    3289           0 : has_function_privilege_name(PG_FUNCTION_ARGS)
    3290             : {
    3291           0 :     text       *functionname = PG_GETARG_TEXT_PP(0);
    3292           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3293             :     Oid         roleid;
    3294             :     Oid         functionoid;
    3295             :     AclMode     mode;
    3296             :     AclResult   aclresult;
    3297             : 
    3298           0 :     roleid = GetUserId();
    3299           0 :     functionoid = convert_function_name(functionname);
    3300           0 :     mode = convert_function_priv_string(priv_type_text);
    3301             : 
    3302           0 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3303             : 
    3304           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3305             : }
    3306             : 
    3307             : /*
    3308             :  * has_function_privilege_name_id
    3309             :  *      Check user privileges on a function given
    3310             :  *      name usename, function oid, and text priv name.
    3311             :  */
    3312             : Datum
    3313           0 : has_function_privilege_name_id(PG_FUNCTION_ARGS)
    3314             : {
    3315           0 :     Name        username = PG_GETARG_NAME(0);
    3316           0 :     Oid         functionoid = PG_GETARG_OID(1);
    3317           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3318             :     Oid         roleid;
    3319             :     AclMode     mode;
    3320             :     AclResult   aclresult;
    3321             : 
    3322           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    3323           0 :     mode = convert_function_priv_string(priv_type_text);
    3324             : 
    3325           0 :     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
    3326           0 :         PG_RETURN_NULL();
    3327             : 
    3328           0 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3329             : 
    3330           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3331             : }
    3332             : 
    3333             : /*
    3334             :  * has_function_privilege_id
    3335             :  *      Check user privileges on a function given
    3336             :  *      function oid, and text priv name.
    3337             :  *      current_user is assumed
    3338             :  */
    3339             : Datum
    3340           0 : has_function_privilege_id(PG_FUNCTION_ARGS)
    3341             : {
    3342           0 :     Oid         functionoid = PG_GETARG_OID(0);
    3343           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3344             :     Oid         roleid;
    3345             :     AclMode     mode;
    3346             :     AclResult   aclresult;
    3347             : 
    3348           0 :     roleid = GetUserId();
    3349           0 :     mode = convert_function_priv_string(priv_type_text);
    3350             : 
    3351           0 :     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
    3352           0 :         PG_RETURN_NULL();
    3353             : 
    3354           0 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3355             : 
    3356           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3357             : }
    3358             : 
    3359             : /*
    3360             :  * has_function_privilege_id_name
    3361             :  *      Check user privileges on a function given
    3362             :  *      roleid, text functionname, and text priv name.
    3363             :  */
    3364             : Datum
    3365           0 : has_function_privilege_id_name(PG_FUNCTION_ARGS)
    3366             : {
    3367           0 :     Oid         roleid = PG_GETARG_OID(0);
    3368           0 :     text       *functionname = PG_GETARG_TEXT_PP(1);
    3369           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3370             :     Oid         functionoid;
    3371             :     AclMode     mode;
    3372             :     AclResult   aclresult;
    3373             : 
    3374           0 :     functionoid = convert_function_name(functionname);
    3375           0 :     mode = convert_function_priv_string(priv_type_text);
    3376             : 
    3377           0 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3378             : 
    3379           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3380             : }
    3381             : 
    3382             : /*
    3383             :  * has_function_privilege_id_id
    3384             :  *      Check user privileges on a function given
    3385             :  *      roleid, function oid, and text priv name.
    3386             :  */
    3387             : Datum
    3388           0 : has_function_privilege_id_id(PG_FUNCTION_ARGS)
    3389             : {
    3390           0 :     Oid         roleid = PG_GETARG_OID(0);
    3391           0 :     Oid         functionoid = PG_GETARG_OID(1);
    3392           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3393             :     AclMode     mode;
    3394             :     AclResult   aclresult;
    3395             : 
    3396           0 :     mode = convert_function_priv_string(priv_type_text);
    3397             : 
    3398           0 :     if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
    3399           0 :         PG_RETURN_NULL();
    3400             : 
    3401           0 :     aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
    3402             : 
    3403           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3404             : }
    3405             : 
    3406             : /*
    3407             :  *      Support routines for has_function_privilege family.
    3408             :  */
    3409             : 
    3410             : /*
    3411             :  * Given a function name expressed as a string, look it up and return Oid
    3412             :  */
    3413             : static Oid
    3414           4 : convert_function_name(text *functionname)
    3415             : {
    3416           4 :     char       *funcname = text_to_cstring(functionname);
    3417             :     Oid         oid;
    3418             : 
    3419           4 :     oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
    3420             :                                                CStringGetDatum(funcname)));
    3421             : 
    3422           4 :     if (!OidIsValid(oid))
    3423           0 :         ereport(ERROR,
    3424             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
    3425             :                  errmsg("function \"%s\" does not exist", funcname)));
    3426             : 
    3427           4 :     return oid;
    3428             : }
    3429             : 
    3430             : /*
    3431             :  * convert_function_priv_string
    3432             :  *      Convert text string to AclMode value.
    3433             :  */
    3434             : static AclMode
    3435           4 : convert_function_priv_string(text *priv_type_text)
    3436             : {
    3437             :     static const priv_map function_priv_map[] = {
    3438             :         {"EXECUTE", ACL_EXECUTE},
    3439             :         {"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
    3440             :         {NULL, 0}
    3441             :     };
    3442             : 
    3443           4 :     return convert_any_priv_string(priv_type_text, function_priv_map);
    3444             : }
    3445             : 
    3446             : 
    3447             : /*
    3448             :  * has_language_privilege variants
    3449             :  *      These are all named "has_language_privilege" at the SQL level.
    3450             :  *      They take various combinations of language name, language OID,
    3451             :  *      user name, user OID, or implicit user = current_user.
    3452             :  *
    3453             :  *      The result is a boolean value: true if user has the indicated
    3454             :  *      privilege, false if not, or NULL if object doesn't exist.
    3455             :  */
    3456             : 
    3457             : /*
    3458             :  * has_language_privilege_name_name
    3459             :  *      Check user privileges on a language given
    3460             :  *      name username, text languagename, and text priv name.
    3461             :  */
    3462             : Datum
    3463           0 : has_language_privilege_name_name(PG_FUNCTION_ARGS)
    3464             : {
    3465           0 :     Name        username = PG_GETARG_NAME(0);
    3466           0 :     text       *languagename = PG_GETARG_TEXT_PP(1);
    3467           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3468             :     Oid         roleid;
    3469             :     Oid         languageoid;
    3470             :     AclMode     mode;
    3471             :     AclResult   aclresult;
    3472             : 
    3473           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    3474           0 :     languageoid = convert_language_name(languagename);
    3475           0 :     mode = convert_language_priv_string(priv_type_text);
    3476             : 
    3477           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3478             : 
    3479           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3480             : }
    3481             : 
    3482             : /*
    3483             :  * has_language_privilege_name
    3484             :  *      Check user privileges on a language given
    3485             :  *      text languagename and text priv name.
    3486             :  *      current_user is assumed
    3487             :  */
    3488             : Datum
    3489           0 : has_language_privilege_name(PG_FUNCTION_ARGS)
    3490             : {
    3491           0 :     text       *languagename = PG_GETARG_TEXT_PP(0);
    3492           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3493             :     Oid         roleid;
    3494             :     Oid         languageoid;
    3495             :     AclMode     mode;
    3496             :     AclResult   aclresult;
    3497             : 
    3498           0 :     roleid = GetUserId();
    3499           0 :     languageoid = convert_language_name(languagename);
    3500           0 :     mode = convert_language_priv_string(priv_type_text);
    3501             : 
    3502           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3503             : 
    3504           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3505             : }
    3506             : 
    3507             : /*
    3508             :  * has_language_privilege_name_id
    3509             :  *      Check user privileges on a language given
    3510             :  *      name usename, language oid, and text priv name.
    3511             :  */
    3512             : Datum
    3513           0 : has_language_privilege_name_id(PG_FUNCTION_ARGS)
    3514             : {
    3515           0 :     Name        username = PG_GETARG_NAME(0);
    3516           0 :     Oid         languageoid = PG_GETARG_OID(1);
    3517           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3518             :     Oid         roleid;
    3519             :     AclMode     mode;
    3520             :     AclResult   aclresult;
    3521             : 
    3522           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    3523           0 :     mode = convert_language_priv_string(priv_type_text);
    3524             : 
    3525           0 :     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
    3526           0 :         PG_RETURN_NULL();
    3527             : 
    3528           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3529             : 
    3530           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3531             : }
    3532             : 
    3533             : /*
    3534             :  * has_language_privilege_id
    3535             :  *      Check user privileges on a language given
    3536             :  *      language oid, and text priv name.
    3537             :  *      current_user is assumed
    3538             :  */
    3539             : Datum
    3540           0 : has_language_privilege_id(PG_FUNCTION_ARGS)
    3541             : {
    3542           0 :     Oid         languageoid = PG_GETARG_OID(0);
    3543           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3544             :     Oid         roleid;
    3545             :     AclMode     mode;
    3546             :     AclResult   aclresult;
    3547             : 
    3548           0 :     roleid = GetUserId();
    3549           0 :     mode = convert_language_priv_string(priv_type_text);
    3550             : 
    3551           0 :     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
    3552           0 :         PG_RETURN_NULL();
    3553             : 
    3554           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3555             : 
    3556           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3557             : }
    3558             : 
    3559             : /*
    3560             :  * has_language_privilege_id_name
    3561             :  *      Check user privileges on a language given
    3562             :  *      roleid, text languagename, and text priv name.
    3563             :  */
    3564             : Datum
    3565           0 : has_language_privilege_id_name(PG_FUNCTION_ARGS)
    3566             : {
    3567           0 :     Oid         roleid = PG_GETARG_OID(0);
    3568           0 :     text       *languagename = PG_GETARG_TEXT_PP(1);
    3569           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3570             :     Oid         languageoid;
    3571             :     AclMode     mode;
    3572             :     AclResult   aclresult;
    3573             : 
    3574           0 :     languageoid = convert_language_name(languagename);
    3575           0 :     mode = convert_language_priv_string(priv_type_text);
    3576             : 
    3577           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3578             : 
    3579           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3580             : }
    3581             : 
    3582             : /*
    3583             :  * has_language_privilege_id_id
    3584             :  *      Check user privileges on a language given
    3585             :  *      roleid, language oid, and text priv name.
    3586             :  */
    3587             : Datum
    3588           0 : has_language_privilege_id_id(PG_FUNCTION_ARGS)
    3589             : {
    3590           0 :     Oid         roleid = PG_GETARG_OID(0);
    3591           0 :     Oid         languageoid = PG_GETARG_OID(1);
    3592           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3593             :     AclMode     mode;
    3594             :     AclResult   aclresult;
    3595             : 
    3596           0 :     mode = convert_language_priv_string(priv_type_text);
    3597             : 
    3598           0 :     if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
    3599           0 :         PG_RETURN_NULL();
    3600             : 
    3601           0 :     aclresult = pg_language_aclcheck(languageoid, roleid, mode);
    3602             : 
    3603           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3604             : }
    3605             : 
    3606             : /*
    3607             :  *      Support routines for has_language_privilege family.
    3608             :  */
    3609             : 
    3610             : /*
    3611             :  * Given a language name expressed as a string, look it up and return Oid
    3612             :  */
    3613             : static Oid
    3614           0 : convert_language_name(text *languagename)
    3615             : {
    3616           0 :     char       *langname = text_to_cstring(languagename);
    3617             : 
    3618           0 :     return get_language_oid(langname, false);
    3619             : }
    3620             : 
    3621             : /*
    3622             :  * convert_language_priv_string
    3623             :  *      Convert text string to AclMode value.
    3624             :  */
    3625             : static AclMode
    3626           0 : convert_language_priv_string(text *priv_type_text)
    3627             : {
    3628             :     static const priv_map language_priv_map[] = {
    3629             :         {"USAGE", ACL_USAGE},
    3630             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
    3631             :         {NULL, 0}
    3632             :     };
    3633             : 
    3634           0 :     return convert_any_priv_string(priv_type_text, language_priv_map);
    3635             : }
    3636             : 
    3637             : 
    3638             : /*
    3639             :  * has_schema_privilege variants
    3640             :  *      These are all named "has_schema_privilege" at the SQL level.
    3641             :  *      They take various combinations of schema name, schema OID,
    3642             :  *      user name, user OID, or implicit user = current_user.
    3643             :  *
    3644             :  *      The result is a boolean value: true if user has the indicated
    3645             :  *      privilege, false if not, or NULL if object doesn't exist.
    3646             :  */
    3647             : 
    3648             : /*
    3649             :  * has_schema_privilege_name_name
    3650             :  *      Check user privileges on a schema given
    3651             :  *      name username, text schemaname, and text priv name.
    3652             :  */
    3653             : Datum
    3654           8 : has_schema_privilege_name_name(PG_FUNCTION_ARGS)
    3655             : {
    3656           8 :     Name        username = PG_GETARG_NAME(0);
    3657           8 :     text       *schemaname = PG_GETARG_TEXT_PP(1);
    3658           8 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3659             :     Oid         roleid;
    3660             :     Oid         schemaoid;
    3661             :     AclMode     mode;
    3662             :     AclResult   aclresult;
    3663             : 
    3664           8 :     roleid = get_role_oid_or_public(NameStr(*username));
    3665           8 :     schemaoid = convert_schema_name(schemaname);
    3666           8 :     mode = convert_schema_priv_string(priv_type_text);
    3667             : 
    3668           8 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3669             : 
    3670           8 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3671             : }
    3672             : 
    3673             : /*
    3674             :  * has_schema_privilege_name
    3675             :  *      Check user privileges on a schema given
    3676             :  *      text schemaname and text priv name.
    3677             :  *      current_user is assumed
    3678             :  */
    3679             : Datum
    3680           0 : has_schema_privilege_name(PG_FUNCTION_ARGS)
    3681             : {
    3682           0 :     text       *schemaname = PG_GETARG_TEXT_PP(0);
    3683           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3684             :     Oid         roleid;
    3685             :     Oid         schemaoid;
    3686             :     AclMode     mode;
    3687             :     AclResult   aclresult;
    3688             : 
    3689           0 :     roleid = GetUserId();
    3690           0 :     schemaoid = convert_schema_name(schemaname);
    3691           0 :     mode = convert_schema_priv_string(priv_type_text);
    3692             : 
    3693           0 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3694             : 
    3695           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3696             : }
    3697             : 
    3698             : /*
    3699             :  * has_schema_privilege_name_id
    3700             :  *      Check user privileges on a schema given
    3701             :  *      name usename, schema oid, and text priv name.
    3702             :  */
    3703             : Datum
    3704           0 : has_schema_privilege_name_id(PG_FUNCTION_ARGS)
    3705             : {
    3706           0 :     Name        username = PG_GETARG_NAME(0);
    3707           0 :     Oid         schemaoid = PG_GETARG_OID(1);
    3708           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3709             :     Oid         roleid;
    3710             :     AclMode     mode;
    3711             :     AclResult   aclresult;
    3712             : 
    3713           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    3714           0 :     mode = convert_schema_priv_string(priv_type_text);
    3715             : 
    3716           0 :     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
    3717           0 :         PG_RETURN_NULL();
    3718             : 
    3719           0 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3720             : 
    3721           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3722             : }
    3723             : 
    3724             : /*
    3725             :  * has_schema_privilege_id
    3726             :  *      Check user privileges on a schema given
    3727             :  *      schema oid, and text priv name.
    3728             :  *      current_user is assumed
    3729             :  */
    3730             : Datum
    3731           0 : has_schema_privilege_id(PG_FUNCTION_ARGS)
    3732             : {
    3733           0 :     Oid         schemaoid = PG_GETARG_OID(0);
    3734           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3735             :     Oid         roleid;
    3736             :     AclMode     mode;
    3737             :     AclResult   aclresult;
    3738             : 
    3739           0 :     roleid = GetUserId();
    3740           0 :     mode = convert_schema_priv_string(priv_type_text);
    3741             : 
    3742           0 :     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
    3743           0 :         PG_RETURN_NULL();
    3744             : 
    3745           0 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3746             : 
    3747           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3748             : }
    3749             : 
    3750             : /*
    3751             :  * has_schema_privilege_id_name
    3752             :  *      Check user privileges on a schema given
    3753             :  *      roleid, text schemaname, and text priv name.
    3754             :  */
    3755             : Datum
    3756           0 : has_schema_privilege_id_name(PG_FUNCTION_ARGS)
    3757             : {
    3758           0 :     Oid         roleid = PG_GETARG_OID(0);
    3759           0 :     text       *schemaname = PG_GETARG_TEXT_PP(1);
    3760           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3761             :     Oid         schemaoid;
    3762             :     AclMode     mode;
    3763             :     AclResult   aclresult;
    3764             : 
    3765           0 :     schemaoid = convert_schema_name(schemaname);
    3766           0 :     mode = convert_schema_priv_string(priv_type_text);
    3767             : 
    3768           0 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3769             : 
    3770           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3771             : }
    3772             : 
    3773             : /*
    3774             :  * has_schema_privilege_id_id
    3775             :  *      Check user privileges on a schema given
    3776             :  *      roleid, schema oid, and text priv name.
    3777             :  */
    3778             : Datum
    3779           0 : has_schema_privilege_id_id(PG_FUNCTION_ARGS)
    3780             : {
    3781           0 :     Oid         roleid = PG_GETARG_OID(0);
    3782           0 :     Oid         schemaoid = PG_GETARG_OID(1);
    3783           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3784             :     AclMode     mode;
    3785             :     AclResult   aclresult;
    3786             : 
    3787           0 :     mode = convert_schema_priv_string(priv_type_text);
    3788             : 
    3789           0 :     if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
    3790           0 :         PG_RETURN_NULL();
    3791             : 
    3792           0 :     aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
    3793             : 
    3794           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3795             : }
    3796             : 
    3797             : /*
    3798             :  *      Support routines for has_schema_privilege family.
    3799             :  */
    3800             : 
    3801             : /*
    3802             :  * Given a schema name expressed as a string, look it up and return Oid
    3803             :  */
    3804             : static Oid
    3805           8 : convert_schema_name(text *schemaname)
    3806             : {
    3807           8 :     char       *nspname = text_to_cstring(schemaname);
    3808             : 
    3809           8 :     return get_namespace_oid(nspname, false);
    3810             : }
    3811             : 
    3812             : /*
    3813             :  * convert_schema_priv_string
    3814             :  *      Convert text string to AclMode value.
    3815             :  */
    3816             : static AclMode
    3817           8 : convert_schema_priv_string(text *priv_type_text)
    3818             : {
    3819             :     static const priv_map schema_priv_map[] = {
    3820             :         {"CREATE", ACL_CREATE},
    3821             :         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    3822             :         {"USAGE", ACL_USAGE},
    3823             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
    3824             :         {NULL, 0}
    3825             :     };
    3826             : 
    3827           8 :     return convert_any_priv_string(priv_type_text, schema_priv_map);
    3828             : }
    3829             : 
    3830             : 
    3831             : /*
    3832             :  * has_server_privilege variants
    3833             :  *      These are all named "has_server_privilege" at the SQL level.
    3834             :  *      They take various combinations of foreign server name,
    3835             :  *      server OID, user name, user OID, or implicit user = current_user.
    3836             :  *
    3837             :  *      The result is a boolean value: true if user has the indicated
    3838             :  *      privilege, false if not.
    3839             :  */
    3840             : 
    3841             : /*
    3842             :  * has_server_privilege_name_name
    3843             :  *      Check user privileges on a foreign server given
    3844             :  *      name username, text servername, and text priv name.
    3845             :  */
    3846             : Datum
    3847           2 : has_server_privilege_name_name(PG_FUNCTION_ARGS)
    3848             : {
    3849           2 :     Name        username = PG_GETARG_NAME(0);
    3850           2 :     text       *servername = PG_GETARG_TEXT_PP(1);
    3851           2 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3852             :     Oid         roleid;
    3853             :     Oid         serverid;
    3854             :     AclMode     mode;
    3855             :     AclResult   aclresult;
    3856             : 
    3857           2 :     roleid = get_role_oid_or_public(NameStr(*username));
    3858           2 :     serverid = convert_server_name(servername);
    3859           2 :     mode = convert_server_priv_string(priv_type_text);
    3860             : 
    3861           2 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3862             : 
    3863           2 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3864             : }
    3865             : 
    3866             : /*
    3867             :  * has_server_privilege_name
    3868             :  *      Check user privileges on a foreign server given
    3869             :  *      text servername and text priv name.
    3870             :  *      current_user is assumed
    3871             :  */
    3872             : Datum
    3873           1 : has_server_privilege_name(PG_FUNCTION_ARGS)
    3874             : {
    3875           1 :     text       *servername = PG_GETARG_TEXT_PP(0);
    3876           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3877             :     Oid         roleid;
    3878             :     Oid         serverid;
    3879             :     AclMode     mode;
    3880             :     AclResult   aclresult;
    3881             : 
    3882           1 :     roleid = GetUserId();
    3883           1 :     serverid = convert_server_name(servername);
    3884           1 :     mode = convert_server_priv_string(priv_type_text);
    3885             : 
    3886           1 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3887             : 
    3888           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3889             : }
    3890             : 
    3891             : /*
    3892             :  * has_server_privilege_name_id
    3893             :  *      Check user privileges on a foreign server given
    3894             :  *      name usename, foreign server oid, and text priv name.
    3895             :  */
    3896             : Datum
    3897           1 : has_server_privilege_name_id(PG_FUNCTION_ARGS)
    3898             : {
    3899           1 :     Name        username = PG_GETARG_NAME(0);
    3900           1 :     Oid         serverid = PG_GETARG_OID(1);
    3901           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3902             :     Oid         roleid;
    3903             :     AclMode     mode;
    3904             :     AclResult   aclresult;
    3905             : 
    3906           1 :     roleid = get_role_oid_or_public(NameStr(*username));
    3907           1 :     mode = convert_server_priv_string(priv_type_text);
    3908             : 
    3909           1 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3910             : 
    3911           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3912             : }
    3913             : 
    3914             : /*
    3915             :  * has_server_privilege_id
    3916             :  *      Check user privileges on a foreign server given
    3917             :  *      server oid, and text priv name.
    3918             :  *      current_user is assumed
    3919             :  */
    3920             : Datum
    3921          13 : has_server_privilege_id(PG_FUNCTION_ARGS)
    3922             : {
    3923          13 :     Oid         serverid = PG_GETARG_OID(0);
    3924          13 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    3925             :     Oid         roleid;
    3926             :     AclMode     mode;
    3927             :     AclResult   aclresult;
    3928             : 
    3929          13 :     roleid = GetUserId();
    3930          13 :     mode = convert_server_priv_string(priv_type_text);
    3931             : 
    3932          13 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3933             : 
    3934          13 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3935             : }
    3936             : 
    3937             : /*
    3938             :  * has_server_privilege_id_name
    3939             :  *      Check user privileges on a foreign server given
    3940             :  *      roleid, text servername, and text priv name.
    3941             :  */
    3942             : Datum
    3943           1 : has_server_privilege_id_name(PG_FUNCTION_ARGS)
    3944             : {
    3945           1 :     Oid         roleid = PG_GETARG_OID(0);
    3946           1 :     text       *servername = PG_GETARG_TEXT_PP(1);
    3947           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3948             :     Oid         serverid;
    3949             :     AclMode     mode;
    3950             :     AclResult   aclresult;
    3951             : 
    3952           1 :     serverid = convert_server_name(servername);
    3953           1 :     mode = convert_server_priv_string(priv_type_text);
    3954             : 
    3955           1 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3956             : 
    3957           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3958             : }
    3959             : 
    3960             : /*
    3961             :  * has_server_privilege_id_id
    3962             :  *      Check user privileges on a foreign server given
    3963             :  *      roleid, server oid, and text priv name.
    3964             :  */
    3965             : Datum
    3966           1 : has_server_privilege_id_id(PG_FUNCTION_ARGS)
    3967             : {
    3968           1 :     Oid         roleid = PG_GETARG_OID(0);
    3969           1 :     Oid         serverid = PG_GETARG_OID(1);
    3970           1 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    3971             :     AclMode     mode;
    3972             :     AclResult   aclresult;
    3973             : 
    3974           1 :     mode = convert_server_priv_string(priv_type_text);
    3975             : 
    3976           1 :     aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
    3977             : 
    3978           1 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    3979             : }
    3980             : 
    3981             : /*
    3982             :  *      Support routines for has_server_privilege family.
    3983             :  */
    3984             : 
    3985             : /*
    3986             :  * Given a server name expressed as a string, look it up and return Oid
    3987             :  */
    3988             : static Oid
    3989           4 : convert_server_name(text *servername)
    3990             : {
    3991           4 :     char       *serverstr = text_to_cstring(servername);
    3992             : 
    3993           4 :     return get_foreign_server_oid(serverstr, false);
    3994             : }
    3995             : 
    3996             : /*
    3997             :  * convert_server_priv_string
    3998             :  *      Convert text string to AclMode value.
    3999             :  */
    4000             : static AclMode
    4001          19 : convert_server_priv_string(text *priv_type_text)
    4002             : {
    4003             :     static const priv_map server_priv_map[] = {
    4004             :         {"USAGE", ACL_USAGE},
    4005             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
    4006             :         {NULL, 0}
    4007             :     };
    4008             : 
    4009          19 :     return convert_any_priv_string(priv_type_text, server_priv_map);
    4010             : }
    4011             : 
    4012             : 
    4013             : /*
    4014             :  * has_tablespace_privilege variants
    4015             :  *      These are all named "has_tablespace_privilege" at the SQL level.
    4016             :  *      They take various combinations of tablespace name, tablespace OID,
    4017             :  *      user name, user OID, or implicit user = current_user.
    4018             :  *
    4019             :  *      The result is a boolean value: true if user has the indicated
    4020             :  *      privilege, false if not.
    4021             :  */
    4022             : 
    4023             : /*
    4024             :  * has_tablespace_privilege_name_name
    4025             :  *      Check user privileges on a tablespace given
    4026             :  *      name username, text tablespacename, and text priv name.
    4027             :  */
    4028             : Datum
    4029           0 : has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
    4030             : {
    4031           0 :     Name        username = PG_GETARG_NAME(0);
    4032           0 :     text       *tablespacename = PG_GETARG_TEXT_PP(1);
    4033           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4034             :     Oid         roleid;
    4035             :     Oid         tablespaceoid;
    4036             :     AclMode     mode;
    4037             :     AclResult   aclresult;
    4038             : 
    4039           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    4040           0 :     tablespaceoid = convert_tablespace_name(tablespacename);
    4041           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4042             : 
    4043           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4044             : 
    4045           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4046             : }
    4047             : 
    4048             : /*
    4049             :  * has_tablespace_privilege_name
    4050             :  *      Check user privileges on a tablespace given
    4051             :  *      text tablespacename and text priv name.
    4052             :  *      current_user is assumed
    4053             :  */
    4054             : Datum
    4055           0 : has_tablespace_privilege_name(PG_FUNCTION_ARGS)
    4056             : {
    4057           0 :     text       *tablespacename = PG_GETARG_TEXT_PP(0);
    4058           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4059             :     Oid         roleid;
    4060             :     Oid         tablespaceoid;
    4061             :     AclMode     mode;
    4062             :     AclResult   aclresult;
    4063             : 
    4064           0 :     roleid = GetUserId();
    4065           0 :     tablespaceoid = convert_tablespace_name(tablespacename);
    4066           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4067             : 
    4068           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4069             : 
    4070           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4071             : }
    4072             : 
    4073             : /*
    4074             :  * has_tablespace_privilege_name_id
    4075             :  *      Check user privileges on a tablespace given
    4076             :  *      name usename, tablespace oid, and text priv name.
    4077             :  */
    4078             : Datum
    4079           0 : has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
    4080             : {
    4081           0 :     Name        username = PG_GETARG_NAME(0);
    4082           0 :     Oid         tablespaceoid = PG_GETARG_OID(1);
    4083           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4084             :     Oid         roleid;
    4085             :     AclMode     mode;
    4086             :     AclResult   aclresult;
    4087             : 
    4088           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    4089           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4090             : 
    4091           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4092             : 
    4093           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4094             : }
    4095             : 
    4096             : /*
    4097             :  * has_tablespace_privilege_id
    4098             :  *      Check user privileges on a tablespace given
    4099             :  *      tablespace oid, and text priv name.
    4100             :  *      current_user is assumed
    4101             :  */
    4102             : Datum
    4103           0 : has_tablespace_privilege_id(PG_FUNCTION_ARGS)
    4104             : {
    4105           0 :     Oid         tablespaceoid = PG_GETARG_OID(0);
    4106           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4107             :     Oid         roleid;
    4108             :     AclMode     mode;
    4109             :     AclResult   aclresult;
    4110             : 
    4111           0 :     roleid = GetUserId();
    4112           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4113             : 
    4114           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4115             : 
    4116           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4117             : }
    4118             : 
    4119             : /*
    4120             :  * has_tablespace_privilege_id_name
    4121             :  *      Check user privileges on a tablespace given
    4122             :  *      roleid, text tablespacename, and text priv name.
    4123             :  */
    4124             : Datum
    4125           0 : has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
    4126             : {
    4127           0 :     Oid         roleid = PG_GETARG_OID(0);
    4128           0 :     text       *tablespacename = PG_GETARG_TEXT_PP(1);
    4129           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4130             :     Oid         tablespaceoid;
    4131             :     AclMode     mode;
    4132             :     AclResult   aclresult;
    4133             : 
    4134           0 :     tablespaceoid = convert_tablespace_name(tablespacename);
    4135           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4136             : 
    4137           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4138             : 
    4139           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4140             : }
    4141             : 
    4142             : /*
    4143             :  * has_tablespace_privilege_id_id
    4144             :  *      Check user privileges on a tablespace given
    4145             :  *      roleid, tablespace oid, and text priv name.
    4146             :  */
    4147             : Datum
    4148           0 : has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
    4149             : {
    4150           0 :     Oid         roleid = PG_GETARG_OID(0);
    4151           0 :     Oid         tablespaceoid = PG_GETARG_OID(1);
    4152           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4153             :     AclMode     mode;
    4154             :     AclResult   aclresult;
    4155             : 
    4156           0 :     mode = convert_tablespace_priv_string(priv_type_text);
    4157             : 
    4158           0 :     aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
    4159             : 
    4160           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4161             : }
    4162             : 
    4163             : /*
    4164             :  *      Support routines for has_tablespace_privilege family.
    4165             :  */
    4166             : 
    4167             : /*
    4168             :  * Given a tablespace name expressed as a string, look it up and return Oid
    4169             :  */
    4170             : static Oid
    4171           0 : convert_tablespace_name(text *tablespacename)
    4172             : {
    4173           0 :     char       *spcname = text_to_cstring(tablespacename);
    4174             : 
    4175           0 :     return get_tablespace_oid(spcname, false);
    4176             : }
    4177             : 
    4178             : /*
    4179             :  * convert_tablespace_priv_string
    4180             :  *      Convert text string to AclMode value.
    4181             :  */
    4182             : static AclMode
    4183           0 : convert_tablespace_priv_string(text *priv_type_text)
    4184             : {
    4185             :     static const priv_map tablespace_priv_map[] = {
    4186             :         {"CREATE", ACL_CREATE},
    4187             :         {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    4188             :         {NULL, 0}
    4189             :     };
    4190             : 
    4191           0 :     return convert_any_priv_string(priv_type_text, tablespace_priv_map);
    4192             : }
    4193             : 
    4194             : /*
    4195             :  * has_type_privilege variants
    4196             :  *      These are all named "has_type_privilege" at the SQL level.
    4197             :  *      They take various combinations of type name, type OID,
    4198             :  *      user name, user OID, or implicit user = current_user.
    4199             :  *
    4200             :  *      The result is a boolean value: true if user has the indicated
    4201             :  *      privilege, false if not, or NULL if object doesn't exist.
    4202             :  */
    4203             : 
    4204             : /*
    4205             :  * has_type_privilege_name_name
    4206             :  *      Check user privileges on a type given
    4207             :  *      name username, text typename, and text priv name.
    4208             :  */
    4209             : Datum
    4210           2 : has_type_privilege_name_name(PG_FUNCTION_ARGS)
    4211             : {
    4212           2 :     Name        username = PG_GETARG_NAME(0);
    4213           2 :     text       *typename = PG_GETARG_TEXT_PP(1);
    4214           2 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4215             :     Oid         roleid;
    4216             :     Oid         typeoid;
    4217             :     AclMode     mode;
    4218             :     AclResult   aclresult;
    4219             : 
    4220           2 :     roleid = get_role_oid_or_public(NameStr(*username));
    4221           2 :     typeoid = convert_type_name(typename);
    4222           2 :     mode = convert_type_priv_string(priv_type_text);
    4223             : 
    4224           2 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4225             : 
    4226           2 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4227             : }
    4228             : 
    4229             : /*
    4230             :  * has_type_privilege_name
    4231             :  *      Check user privileges on a type given
    4232             :  *      text typename and text priv name.
    4233             :  *      current_user is assumed
    4234             :  */
    4235             : Datum
    4236           0 : has_type_privilege_name(PG_FUNCTION_ARGS)
    4237             : {
    4238           0 :     text       *typename = PG_GETARG_TEXT_PP(0);
    4239           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4240             :     Oid         roleid;
    4241             :     Oid         typeoid;
    4242             :     AclMode     mode;
    4243             :     AclResult   aclresult;
    4244             : 
    4245           0 :     roleid = GetUserId();
    4246           0 :     typeoid = convert_type_name(typename);
    4247           0 :     mode = convert_type_priv_string(priv_type_text);
    4248             : 
    4249           0 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4250             : 
    4251           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4252             : }
    4253             : 
    4254             : /*
    4255             :  * has_type_privilege_name_id
    4256             :  *      Check user privileges on a type given
    4257             :  *      name usename, type oid, and text priv name.
    4258             :  */
    4259             : Datum
    4260           0 : has_type_privilege_name_id(PG_FUNCTION_ARGS)
    4261             : {
    4262           0 :     Name        username = PG_GETARG_NAME(0);
    4263           0 :     Oid         typeoid = PG_GETARG_OID(1);
    4264           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4265             :     Oid         roleid;
    4266             :     AclMode     mode;
    4267             :     AclResult   aclresult;
    4268             : 
    4269           0 :     roleid = get_role_oid_or_public(NameStr(*username));
    4270           0 :     mode = convert_type_priv_string(priv_type_text);
    4271             : 
    4272           0 :     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
    4273           0 :         PG_RETURN_NULL();
    4274             : 
    4275           0 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4276             : 
    4277           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4278             : }
    4279             : 
    4280             : /*
    4281             :  * has_type_privilege_id
    4282             :  *      Check user privileges on a type given
    4283             :  *      type oid, and text priv name.
    4284             :  *      current_user is assumed
    4285             :  */
    4286             : Datum
    4287           0 : has_type_privilege_id(PG_FUNCTION_ARGS)
    4288             : {
    4289           0 :     Oid         typeoid = PG_GETARG_OID(0);
    4290           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4291             :     Oid         roleid;
    4292             :     AclMode     mode;
    4293             :     AclResult   aclresult;
    4294             : 
    4295           0 :     roleid = GetUserId();
    4296           0 :     mode = convert_type_priv_string(priv_type_text);
    4297             : 
    4298           0 :     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
    4299           0 :         PG_RETURN_NULL();
    4300             : 
    4301           0 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4302             : 
    4303           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4304             : }
    4305             : 
    4306             : /*
    4307             :  * has_type_privilege_id_name
    4308             :  *      Check user privileges on a type given
    4309             :  *      roleid, text typename, and text priv name.
    4310             :  */
    4311             : Datum
    4312           0 : has_type_privilege_id_name(PG_FUNCTION_ARGS)
    4313             : {
    4314           0 :     Oid         roleid = PG_GETARG_OID(0);
    4315           0 :     text       *typename = PG_GETARG_TEXT_PP(1);
    4316           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4317             :     Oid         typeoid;
    4318             :     AclMode     mode;
    4319             :     AclResult   aclresult;
    4320             : 
    4321           0 :     typeoid = convert_type_name(typename);
    4322           0 :     mode = convert_type_priv_string(priv_type_text);
    4323             : 
    4324           0 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4325             : 
    4326           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4327             : }
    4328             : 
    4329             : /*
    4330             :  * has_type_privilege_id_id
    4331             :  *      Check user privileges on a type given
    4332             :  *      roleid, type oid, and text priv name.
    4333             :  */
    4334             : Datum
    4335           0 : has_type_privilege_id_id(PG_FUNCTION_ARGS)
    4336             : {
    4337           0 :     Oid         roleid = PG_GETARG_OID(0);
    4338           0 :     Oid         typeoid = PG_GETARG_OID(1);
    4339           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4340             :     AclMode     mode;
    4341             :     AclResult   aclresult;
    4342             : 
    4343           0 :     mode = convert_type_priv_string(priv_type_text);
    4344             : 
    4345           0 :     if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
    4346           0 :         PG_RETURN_NULL();
    4347             : 
    4348           0 :     aclresult = pg_type_aclcheck(typeoid, roleid, mode);
    4349             : 
    4350           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4351             : }
    4352             : 
    4353             : /*
    4354             :  *      Support routines for has_type_privilege family.
    4355             :  */
    4356             : 
    4357             : /*
    4358             :  * Given a type name expressed as a string, look it up and return Oid
    4359             :  */
    4360             : static Oid
    4361           2 : convert_type_name(text *typename)
    4362             : {
    4363           2 :     char       *typname = text_to_cstring(typename);
    4364             :     Oid         oid;
    4365             : 
    4366           2 :     oid = DatumGetObjectId(DirectFunctionCall1(regtypein,
    4367             :                                                CStringGetDatum(typname)));
    4368             : 
    4369           2 :     if (!OidIsValid(oid))
    4370           0 :         ereport(ERROR,
    4371             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    4372             :                  errmsg("type \"%s\" does not exist", typname)));
    4373             : 
    4374           2 :     return oid;
    4375             : }
    4376             : 
    4377             : /*
    4378             :  * convert_type_priv_string
    4379             :  *      Convert text string to AclMode value.
    4380             :  */
    4381             : static AclMode
    4382           2 : convert_type_priv_string(text *priv_type_text)
    4383             : {
    4384             :     static const priv_map type_priv_map[] = {
    4385             :         {"USAGE", ACL_USAGE},
    4386             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
    4387             :         {NULL, 0}
    4388             :     };
    4389             : 
    4390           2 :     return convert_any_priv_string(priv_type_text, type_priv_map);
    4391             : }
    4392             : 
    4393             : 
    4394             : /*
    4395             :  * pg_has_role variants
    4396             :  *      These are all named "pg_has_role" at the SQL level.
    4397             :  *      They take various combinations of role name, role OID,
    4398             :  *      user name, user OID, or implicit user = current_user.
    4399             :  *
    4400             :  *      The result is a boolean value: true if user has the indicated
    4401             :  *      privilege, false if not.
    4402             :  */
    4403             : 
    4404             : /*
    4405             :  * pg_has_role_name_name
    4406             :  *      Check user privileges on a role given
    4407             :  *      name username, name rolename, and text priv name.
    4408             :  */
    4409             : Datum
    4410           0 : pg_has_role_name_name(PG_FUNCTION_ARGS)
    4411             : {
    4412           0 :     Name        username = PG_GETARG_NAME(0);
    4413           0 :     Name        rolename = PG_GETARG_NAME(1);
    4414           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4415             :     Oid         roleid;
    4416             :     Oid         roleoid;
    4417             :     AclMode     mode;
    4418             :     AclResult   aclresult;
    4419             : 
    4420           0 :     roleid = get_role_oid(NameStr(*username), false);
    4421           0 :     roleoid = get_role_oid(NameStr(*rolename), false);
    4422           0 :     mode = convert_role_priv_string(priv_type_text);
    4423             : 
    4424           0 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4425             : 
    4426           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4427             : }
    4428             : 
    4429             : /*
    4430             :  * pg_has_role_name
    4431             :  *      Check user privileges on a role given
    4432             :  *      name rolename and text priv name.
    4433             :  *      current_user is assumed
    4434             :  */
    4435             : Datum
    4436           3 : pg_has_role_name(PG_FUNCTION_ARGS)
    4437             : {
    4438           3 :     Name        rolename = PG_GETARG_NAME(0);
    4439           3 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4440             :     Oid         roleid;
    4441             :     Oid         roleoid;
    4442             :     AclMode     mode;
    4443             :     AclResult   aclresult;
    4444             : 
    4445           3 :     roleid = GetUserId();
    4446           3 :     roleoid = get_role_oid(NameStr(*rolename), false);
    4447           3 :     mode = convert_role_priv_string(priv_type_text);
    4448             : 
    4449           3 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4450             : 
    4451           3 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4452             : }
    4453             : 
    4454             : /*
    4455             :  * pg_has_role_name_id
    4456             :  *      Check user privileges on a role given
    4457             :  *      name usename, role oid, and text priv name.
    4458             :  */
    4459             : Datum
    4460           0 : pg_has_role_name_id(PG_FUNCTION_ARGS)
    4461             : {
    4462           0 :     Name        username = PG_GETARG_NAME(0);
    4463           0 :     Oid         roleoid = PG_GETARG_OID(1);
    4464           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4465             :     Oid         roleid;
    4466             :     AclMode     mode;
    4467             :     AclResult   aclresult;
    4468             : 
    4469           0 :     roleid = get_role_oid(NameStr(*username), false);
    4470           0 :     mode = convert_role_priv_string(priv_type_text);
    4471             : 
    4472           0 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4473             : 
    4474           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4475             : }
    4476             : 
    4477             : /*
    4478             :  * pg_has_role_id
    4479             :  *      Check user privileges on a role given
    4480             :  *      role oid, and text priv name.
    4481             :  *      current_user is assumed
    4482             :  */
    4483             : Datum
    4484        6488 : pg_has_role_id(PG_FUNCTION_ARGS)
    4485             : {
    4486        6488 :     Oid         roleoid = PG_GETARG_OID(0);
    4487        6488 :     text       *priv_type_text = PG_GETARG_TEXT_PP(1);
    4488             :     Oid         roleid;
    4489             :     AclMode     mode;
    4490             :     AclResult   aclresult;
    4491             : 
    4492        6488 :     roleid = GetUserId();
    4493        6488 :     mode = convert_role_priv_string(priv_type_text);
    4494             : 
    4495        6488 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4496             : 
    4497        6488 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4498             : }
    4499             : 
    4500             : /*
    4501             :  * pg_has_role_id_name
    4502             :  *      Check user privileges on a role given
    4503             :  *      roleid, name rolename, and text priv name.
    4504             :  */
    4505             : Datum
    4506           0 : pg_has_role_id_name(PG_FUNCTION_ARGS)
    4507             : {
    4508           0 :     Oid         roleid = PG_GETARG_OID(0);
    4509           0 :     Name        rolename = PG_GETARG_NAME(1);
    4510           0 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4511             :     Oid         roleoid;
    4512             :     AclMode     mode;
    4513             :     AclResult   aclresult;
    4514             : 
    4515           0 :     roleoid = get_role_oid(NameStr(*rolename), false);
    4516           0 :     mode = convert_role_priv_string(priv_type_text);
    4517             : 
    4518           0 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4519             : 
    4520           0 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4521             : }
    4522             : 
    4523             : /*
    4524             :  * pg_has_role_id_id
    4525             :  *      Check user privileges on a role given
    4526             :  *      roleid, role oid, and text priv name.
    4527             :  */
    4528             : Datum
    4529          14 : pg_has_role_id_id(PG_FUNCTION_ARGS)
    4530             : {
    4531          14 :     Oid         roleid = PG_GETARG_OID(0);
    4532          14 :     Oid         roleoid = PG_GETARG_OID(1);
    4533          14 :     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
    4534             :     AclMode     mode;
    4535             :     AclResult   aclresult;
    4536             : 
    4537          14 :     mode = convert_role_priv_string(priv_type_text);
    4538             : 
    4539          14 :     aclresult = pg_role_aclcheck(roleoid, roleid, mode);
    4540             : 
    4541          14 :     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
    4542             : }
    4543             : 
    4544             : /*
    4545             :  *      Support routines for pg_has_role family.
    4546             :  */
    4547             : 
    4548             : /*
    4549             :  * convert_role_priv_string
    4550             :  *      Convert text string to AclMode value.
    4551             :  *
    4552             :  * We use USAGE to denote whether the privileges of the role are accessible
    4553             :  * (has_privs), MEMBER to denote is_member, and MEMBER WITH GRANT OPTION
    4554             :  * (or ADMIN OPTION) to denote is_admin.  There is no ACL bit corresponding
    4555             :  * to MEMBER so we cheat and use ACL_CREATE for that.  This convention
    4556             :  * is shared only with pg_role_aclcheck, below.
    4557             :  */
    4558             : static AclMode
    4559        6505 : convert_role_priv_string(text *priv_type_text)
    4560             : {
    4561             :     static const priv_map role_priv_map[] = {
    4562             :         {"USAGE", ACL_USAGE},
    4563             :         {"MEMBER", ACL_CREATE},
    4564             :         {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    4565             :         {"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    4566             :         {"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    4567             :         {"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
    4568             :         {NULL, 0}
    4569             :     };
    4570             : 
    4571        6505 :     return convert_any_priv_string(priv_type_text, role_priv_map);
    4572             : }
    4573             : 
    4574             : /*
    4575             :  * pg_role_aclcheck
    4576             :  *      Quick-and-dirty support for pg_has_role
    4577             :  */
    4578             : static AclResult
    4579        6505 : pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
    4580             : {
    4581        6505 :     if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
    4582             :     {
    4583             :         /*
    4584             :          * XXX For roleid == role_oid, is_admin_of_role() also examines the
    4585             :          * session and call stack.  That suits two-argument pg_has_role(), but
    4586             :          * it gives the three-argument version a lamentable whimsy.
    4587             :          */
    4588           0 :         if (is_admin_of_role(roleid, role_oid))
    4589           0 :             return ACLCHECK_OK;
    4590             :     }
    4591        6505 :     if (mode & ACL_CREATE)
    4592             :     {
    4593           0 :         if (is_member_of_role(roleid, role_oid))
    4594           0 :             return ACLCHECK_OK;
    4595             :     }
    4596        6505 :     if (mode & ACL_USAGE)
    4597             :     {
    4598        6505 :         if (has_privs_of_role(roleid, role_oid))
    4599        6439 :             return ACLCHECK_OK;
    4600             :     }
    4601          66 :     return ACLCHECK_NO_PRIV;
    4602             : }
    4603             : 
    4604             : 
    4605             : /*
    4606             :  * initialization function (called by InitPostgres)
    4607             :  */
    4608             : void
    4609         336 : initialize_acl(void)
    4610             : {
    4611         336 :     if (!IsBootstrapProcessingMode())
    4612             :     {
    4613             :         /*
    4614             :          * In normal mode, set a callback on any syscache invalidation of
    4615             :          * pg_auth_members rows
    4616             :          */
    4617         335 :         CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
    4618             :                                       RoleMembershipCacheCallback,
    4619             :                                       (Datum) 0);
    4620             :     }
    4621         336 : }
    4622             : 
    4623             : /*
    4624             :  * RoleMembershipCacheCallback
    4625             :  *      Syscache inval callback function
    4626             :  */
    4627             : static void
    4628         296 : RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
    4629             : {
    4630             :     /* Force membership caches to be recomputed on next use */
    4631         296 :     cached_privs_role = InvalidOid;
    4632         296 :     cached_member_role = InvalidOid;
    4633         296 : }
    4634             : 
    4635             : 
    4636             : /* Check if specified role has rolinherit set */
    4637             : static bool
    4638         241 : has_rolinherit(Oid roleid)
    4639             : {
    4640         241 :     bool        result = false;
    4641             :     HeapTuple   utup;
    4642             : 
    4643         241 :     utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
    4644         241 :     if (HeapTupleIsValid(utup))
    4645             :     {
    4646         241 :         result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
    4647         241 :         ReleaseSysCache(utup);
    4648             :     }
    4649         241 :     return result;
    4650             : }
    4651             : 
    4652             : 
    4653             : /*
    4654             :  * Get a list of roles that the specified roleid has the privileges of
    4655             :  *
    4656             :  * This is defined not to recurse through roles that don't have rolinherit
    4657             :  * set; for such roles, membership implies the ability to do SET ROLE, but
    4658             :  * the privileges are not available until you've done so.
    4659             :  *
    4660             :  * Since indirect membership testing is relatively expensive, we cache
    4661             :  * a list of memberships.  Hence, the result is only guaranteed good until
    4662             :  * the next call of roles_has_privs_of()!
    4663             :  *
    4664             :  * For the benefit of select_best_grantor, the result is defined to be
    4665             :  * in breadth-first order, ie, closer relationships earlier.
    4666             :  */
    4667             : static List *
    4668        1123 : roles_has_privs_of(Oid roleid)
    4669             : {
    4670             :     List       *roles_list;
    4671             :     ListCell   *l;
    4672             :     List       *new_cached_privs_roles;
    4673             :     MemoryContext oldctx;
    4674             : 
    4675             :     /* If cache is already valid, just return the list */
    4676        1123 :     if (OidIsValid(cached_privs_role) && cached_privs_role == roleid)
    4677         969 :         return cached_privs_roles;
    4678             : 
    4679             :     /*
    4680             :      * Find all the roles that roleid is a member of, including multi-level
    4681             :      * recursion.  The role itself will always be the first element of the
    4682             :      * resulting list.
    4683             :      *
    4684             :      * Each element of the list is scanned to see if it adds any indirect
    4685             :      * memberships.  We can use a single list as both the record of
    4686             :      * already-found memberships and the agenda of roles yet to be scanned.
    4687             :      * This is a bit tricky but works because the foreach() macro doesn't
    4688             :      * fetch the next list element until the bottom of the loop.
    4689             :      */
    4690         154 :     roles_list = list_make1_oid(roleid);
    4691             : 
    4692         395 :     foreach(l, roles_list)
    4693             :     {
    4694         241 :         Oid         memberid = lfirst_oid(l);
    4695             :         CatCList   *memlist;
    4696             :         int         i;
    4697             : 
    4698             :         /* Ignore non-inheriting roles */
    4699         241 :         if (!has_rolinherit(memberid))
    4700           0 :             continue;
    4701             : 
    4702             :         /* Find roles that memberid is directly a member of */
    4703         241 :         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
    4704             :                                       ObjectIdGetDatum(memberid));
    4705         328 :         for (i = 0; i < memlist->n_members; i++)
    4706             :         {
    4707          87 :             HeapTuple   tup = &memlist->members[i]->tuple;
    4708          87 :             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
    4709             : 
    4710             :             /*
    4711             :              * Even though there shouldn't be any loops in the membership
    4712             :              * graph, we must test for having already seen this role. It is
    4713             :              * legal for instance to have both A->B and A->C->B.
    4714             :              */
    4715          87 :             roles_list = list_append_unique_oid(roles_list, otherid);
    4716             :         }
    4717         241 :         ReleaseSysCacheList(memlist);
    4718             :     }
    4719             : 
    4720             :     /*
    4721             :      * Copy the completed list into TopMemoryContext so it will persist.
    4722             :      */
    4723         154 :     oldctx = MemoryContextSwitchTo(TopMemoryContext);
    4724         154 :     new_cached_privs_roles = list_copy(roles_list);
    4725         154 :     MemoryContextSwitchTo(oldctx);
    4726         154 :     list_free(roles_list);
    4727             : 
    4728             :     /*
    4729             :      * Now safe to assign to state variable
    4730             :      */
    4731         154 :     cached_privs_role = InvalidOid; /* just paranoia */
    4732         154 :     list_free(cached_privs_roles);
    4733         154 :     cached_privs_roles = new_cached_privs_roles;
    4734         154 :     cached_privs_role = roleid;
    4735             : 
    4736             :     /* And now we can return the answer */
    4737         154 :     return cached_privs_roles;
    4738             : }
    4739             : 
    4740             : 
    4741             : /*
    4742             :  * Get a list of roles that the specified roleid is a member of
    4743             :  *
    4744             :  * This is defined to recurse through roles regardless of rolinherit.
    4745             :  *
    4746             :  * Since indirect membership testing is relatively expensive, we cache
    4747             :  * a list of memberships.  Hence, the result is only guaranteed good until
    4748             :  * the next call of roles_is_member_of()!
    4749             :  */
    4750             : static List *
    4751          56 : roles_is_member_of(Oid roleid)
    4752             : {
    4753             :     List       *roles_list;
    4754             :     ListCell   *l;
    4755             :     List       *new_cached_membership_roles;
    4756             :     MemoryContext oldctx;
    4757             : 
    4758             :     /* If cache is already valid, just return the list */
    4759          56 :     if (OidIsValid(cached_member_role) && cached_member_role == roleid)
    4760          16 :         return cached_membership_roles;
    4761             : 
    4762             :     /*
    4763             :      * Find all the roles that roleid is a member of, including multi-level
    4764             :      * recursion.  The role itself will always be the first element of the
    4765             :      * resulting list.
    4766             :      *
    4767             :      * Each element of the list is scanned to see if it adds any indirect
    4768             :      * memberships.  We can use a single list as both the record of
    4769             :      * already-found memberships and the agenda of roles yet to be scanned.
    4770             :      * This is a bit tricky but works because the foreach() macro doesn't
    4771             :      * fetch the next list element until the bottom of the loop.
    4772             :      */
    4773          40 :     roles_list = list_make1_oid(roleid);
    4774             : 
    4775          95 :     foreach(l, roles_list)
    4776             :     {
    4777          55 :         Oid         memberid = lfirst_oid(l);
    4778             :         CatCList   *memlist;
    4779             :         int         i;
    4780             : 
    4781             :         /* Find roles that memberid is directly a member of */
    4782          55 :         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
    4783             :                                       ObjectIdGetDatum(memberid));
    4784          70 :         for (i = 0; i < memlist->n_members; i++)
    4785             :         {
    4786          15 :             HeapTuple   tup = &memlist->members[i]->tuple;
    4787          15 :             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
    4788             : 
    4789             :             /*
    4790             :              * Even though there shouldn't be any loops in the membership
    4791             :              * graph, we must test for having already seen this role. It is
    4792             :              * legal for instance to have both A->B and A->C->B.
    4793             :              */
    4794          15 :             roles_list = list_append_unique_oid(roles_list, otherid);
    4795             :         }
    4796          55 :         ReleaseSysCacheList(memlist);
    4797             :     }
    4798             : 
    4799             :     /*
    4800             :      * Copy the completed list into TopMemoryContext so it will persist.
    4801             :      */
    4802          40 :     oldctx = MemoryContextSwitchTo(TopMemoryContext);
    4803          40 :     new_cached_membership_roles = list_copy(roles_list);
    4804          40 :     MemoryContextSwitchTo(oldctx);
    4805          40 :     list_free(roles_list);
    4806             : 
    4807             :     /*
    4808             :      * Now safe to assign to state variable
    4809             :      */
    4810          40 :     cached_member_role = InvalidOid;    /* just paranoia */
    4811          40 :     list_free(cached_membership_roles);
    4812          40 :     cached_membership_roles = new_cached_membership_roles;
    4813          40 :     cached_member_role = roleid;
    4814             : 
    4815             :     /* And now we can return the answer */
    4816          40 :     return cached_membership_roles;
    4817             : }
    4818             : 
    4819             : 
    4820             : /*
    4821             :  * Does member have the privileges of role (directly or indirectly)?
    4822             :  *
    4823             :  * This is defined not to recurse through roles that don't have rolinherit
    4824             :  * set; for such roles, membership implies the ability to do SET ROLE, but
    4825             :  * the privileges are not available until you've done so.
    4826             :  */
    4827             : bool
    4828        8003 : has_privs_of_role(Oid member, Oid role)
    4829             : {
    4830             :     /* Fast path for simple case */
    4831        8003 :     if (member == role)
    4832        6830 :         return true;
    4833             : 
    4834             :     /* Superusers have every privilege, so are part of every role */
    4835        1173 :     if (superuser_arg(member))
    4836          84 :         return true;
    4837             : 
    4838             :     /*
    4839             :      * Find all the roles that member has the privileges of, including
    4840             :      * multi-level recursion, then see if target role is any one of them.
    4841             :      */
    4842        1089 :     return list_member_oid(roles_has_privs_of(member), role);
    4843             : }
    4844             : 
    4845             : 
    4846             : /*
    4847             :  * Is member a member of role (directly or indirectly)?
    4848             :  *
    4849             :  * This is defined to recurse through roles regardless of rolinherit.
    4850             :  */
    4851             : bool
    4852         192 : is_member_of_role(Oid member, Oid role)
    4853             : {
    4854             :     /* Fast path for simple case */
    4855         192 :     if (member == role)
    4856          52 :         return true;
    4857             : 
    4858             :     /* Superusers have every privilege, so are part of every role */
    4859         140 :     if (superuser_arg(member))
    4860         104 :         return true;
    4861             : 
    4862             :     /*
    4863             :      * Find all the roles that member is a member of, including multi-level
    4864             :      * recursion, then see if target role is any one of them.
    4865             :      */
    4866          36 :     return list_member_oid(roles_is_member_of(member), role);
    4867             : }
    4868             : 
    4869             : /*
    4870             :  * check_is_member_of_role
    4871             :  *      is_member_of_role with a standard permission-violation error if not
    4872             :  */
    4873             : void
    4874         101 : check_is_member_of_role(Oid member, Oid role)
    4875             : {
    4876         101 :     if (!is_member_of_role(member, role))
    4877          20 :         ereport(ERROR,
    4878             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    4879             :                  errmsg("must be member of role \"%s\"",
    4880             :                         GetUserNameFromId(role, false))));
    4881          81 : }
    4882             : 
    4883             : /*
    4884             :  * Is member a member of role, not considering superuserness?
    4885             :  *
    4886             :  * This is identical to is_member_of_role except we ignore superuser
    4887             :  * status.
    4888             :  */
    4889             : bool
    4890          20 : is_member_of_role_nosuper(Oid member, Oid role)
    4891             : {
    4892             :     /* Fast path for simple case */
    4893          20 :     if (member == role)
    4894           0 :         return true;
    4895             : 
    4896             :     /*
    4897             :      * Find all the roles that member is a member of, including multi-level
    4898             :      * recursion, then see if target role is any one of them.
    4899             :      */
    4900          20 :     return list_member_oid(roles_is_member_of(member), role);
    4901             : }
    4902             : 
    4903             : 
    4904             : /*
    4905             :  * Is member an admin of role?  That is, is member the role itself (subject to
    4906             :  * restrictions below), a member (directly or indirectly) WITH ADMIN OPTION,
    4907             :  * or a superuser?
    4908             :  */
    4909             : bool
    4910           8 : is_admin_of_role(Oid member, Oid role)
    4911             : {
    4912           8 :     bool        result = false;
    4913             :     List       *roles_list;
    4914             :     ListCell   *l;
    4915             : 
    4916           8 :     if (superuser_arg(member))
    4917           0 :         return true;
    4918             : 
    4919           8 :     if (member == role)
    4920             : 
    4921             :         /*
    4922             :          * A role can admin itself when it matches the session user and we're
    4923             :          * outside any security-restricted operation, SECURITY DEFINER or
    4924             :          * similar context.  SQL-standard roles cannot self-admin.  However,
    4925             :          * SQL-standard users are distinct from roles, and they are not
    4926             :          * grantable like roles: PostgreSQL's role-user duality extends the
    4927             :          * standard.  Checking for a session user match has the effect of
    4928             :          * letting a role self-admin only when it's conspicuously behaving
    4929             :          * like a user.  Note that allowing self-admin under a mere SET ROLE
    4930             :          * would make WITH ADMIN OPTION largely irrelevant; any member could
    4931             :          * SET ROLE to issue the otherwise-forbidden command.
    4932             :          *
    4933             :          * Withholding self-admin in a security-restricted operation prevents
    4934             :          * object owners from harnessing the session user identity during
    4935             :          * administrative maintenance.  Suppose Alice owns a database, has
    4936             :          * issued "GRANT alice TO bob", and runs a daily ANALYZE.  Bob creates
    4937             :          * an alice-owned SECURITY DEFINER function that issues "REVOKE alice
    4938             :          * FROM carol".  If he creates an expression index calling that
    4939             :          * function, Alice will attempt the REVOKE during each ANALYZE.
    4940             :          * Checking InSecurityRestrictedOperation() thwarts that attack.
    4941             :          *
    4942             :          * Withholding self-admin in SECURITY DEFINER functions makes their
    4943             :          * behavior independent of the calling user.  There's no security or
    4944             :          * SQL-standard-conformance need for that restriction, though.
    4945             :          *
    4946             :          * A role cannot have actual WITH ADMIN OPTION on itself, because that
    4947             :          * would imply a membership loop.  Therefore, we're done either way.
    4948             :          */
    4949          10 :         return member == GetSessionUserId() &&
    4950           5 :             !InLocalUserIdChange() && !InSecurityRestrictedOperation();
    4951             : 
    4952             :     /*
    4953             :      * Find all the roles that member is a member of, including multi-level
    4954             :      * recursion.  We build a list in the same way that is_member_of_role does
    4955             :      * to track visited and unvisited roles.
    4956             :      */
    4957           4 :     roles_list = list_make1_oid(member);
    4958             : 
    4959           6 :     foreach(l, roles_list)
    4960             :     {
    4961           5 :         Oid         memberid = lfirst_oid(l);
    4962             :         CatCList   *memlist;
    4963             :         int         i;
    4964             : 
    4965             :         /* Find roles that memberid is directly a member of */
    4966           5 :         memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
    4967             :                                       ObjectIdGetDatum(memberid));
    4968           9 :         for (i = 0; i < memlist->n_members; i++)
    4969             :         {
    4970           7 :             HeapTuple   tup = &memlist->members[i]->tuple;
    4971           7 :             Oid         otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
    4972             : 
    4973          11 :             if (otherid == role &&
    4974           4 :                 ((Form_pg_auth_members) GETSTRUCT(tup))->admin_option)
    4975             :             {
    4976             :                 /* Found what we came for, so can stop searching */
    4977           3 :                 result = true;
    4978           3 :                 break;
    4979             :             }
    4980             : 
    4981           4 :             roles_list = list_append_unique_oid(roles_list, otherid);
    4982             :         }
    4983           5 :         ReleaseSysCacheList(memlist);
    4984           5 :         if (result)
    4985           3 :             break;
    4986             :     }
    4987             : 
    4988           4 :     list_free(roles_list);
    4989             : 
    4990           4 :     return result;
    4991             : }
    4992             : 
    4993             : 
    4994             : /* does what it says ... */
    4995             : static int
    4996           0 : count_one_bits(AclMode mask)
    4997             : {
    4998           0 :     int         nbits = 0;
    4999             : 
    5000             :     /* this code relies on AclMode being an unsigned type */
    5001           0 :     while (mask)
    5002             :     {
    5003           0 :         if (mask & 1)
    5004           0 :             nbits++;
    5005           0 :         mask >>= 1;
    5006             :     }
    5007           0 :     return nbits;
    5008             : }
    5009             : 
    5010             : 
    5011             : /*
    5012             :  * Select the effective grantor ID for a GRANT or REVOKE operation.
    5013             :  *
    5014             :  * The grantor must always be either the object owner or some role that has
    5015             :  * been explicitly granted grant options.  This ensures that all granted
    5016             :  * privileges appear to flow from the object owner, and there are never
    5017             :  * multiple "original sources" of a privilege.  Therefore, if the would-be
    5018             :  * grantor is a member of a role that has the needed grant options, we have
    5019             :  * to do the grant as that role instead.
    5020             :  *
    5021             :  * It is possible that the would-be grantor is a member of several roles
    5022             :  * that have different subsets of the desired grant options, but no one
    5023             :  * role has 'em all.  In this case we pick a role with the largest number
    5024             :  * of desired options.  Ties are broken in favor of closer ancestors.
    5025             :  *
    5026             :  * roleId: the role attempting to do the GRANT/REVOKE
    5027             :  * privileges: the privileges to be granted/revoked
    5028             :  * acl: the ACL of the object in question
    5029             :  * ownerId: the role owning the object in question
    5030             :  * *grantorId: receives the OID of the role to do the grant as
    5031             :  * *grantOptions: receives the grant options actually held by grantorId
    5032             :  *
    5033             :  * If no grant options exist, we set grantorId to roleId, grantOptions to 0.
    5034             :  */
    5035             : void
    5036         786 : select_best_grantor(Oid roleId, AclMode privileges,
    5037             :                     const Acl *acl, Oid ownerId,
    5038             :                     Oid *grantorId, AclMode *grantOptions)
    5039             : {
    5040         786 :     AclMode     needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
    5041             :     List       *roles_list;
    5042             :     int         nrights;
    5043             :     ListCell   *l;
    5044             : 
    5045             :     /*
    5046             :      * The object owner is always treated as having all grant options, so if
    5047             :      * roleId is the owner it's easy.  Also, if roleId is a superuser it's
    5048             :      * easy: superusers are implicitly members of every role, so they act as
    5049             :      * the object owner.
    5050             :      */
    5051         786 :     if (roleId == ownerId || superuser_arg(roleId))
    5052             :     {
    5053         752 :         *grantorId = ownerId;
    5054         752 :         *grantOptions = needed_goptions;
    5055         752 :         return;
    5056             :     }
    5057             : 
    5058             :     /*
    5059             :      * Otherwise we have to do a careful search to see if roleId has the
    5060             :      * privileges of any suitable role.  Note: we can hang onto the result of
    5061             :      * roles_has_privs_of() throughout this loop, because aclmask_direct()
    5062             :      * doesn't query any role memberships.
    5063             :      */
    5064          34 :     roles_list = roles_has_privs_of(roleId);
    5065             : 
    5066             :     /* initialize candidate result as default */
    5067          34 :     *grantorId = roleId;
    5068          34 :     *grantOptions = ACL_NO_RIGHTS;
    5069          34 :     nrights = 0;
    5070             : 
    5071          47 :     foreach(l, roles_list)
    5072             :     {
    5073          36 :         Oid         otherrole = lfirst_oid(l);
    5074             :         AclMode     otherprivs;
    5075             : 
    5076          36 :         otherprivs = aclmask_direct(acl, otherrole, ownerId,
    5077             :                                     needed_goptions, ACLMASK_ALL);
    5078          36 :         if (otherprivs == needed_goptions)
    5079             :         {
    5080             :             /* Found a suitable grantor */
    5081          23 :             *grantorId = otherrole;
    5082          23 :             *grantOptions = otherprivs;
    5083          23 :             return;
    5084             :         }
    5085             : 
    5086             :         /*
    5087             :          * If it has just some of the needed privileges, remember best
    5088             :          * candidate.
    5089             :          */
    5090          13 :         if (otherprivs != ACL_NO_RIGHTS)
    5091             :         {
    5092           0 :             int         nnewrights = count_one_bits(otherprivs);
    5093             : 
    5094           0 :             if (nnewrights > nrights)
    5095             :             {
    5096           0 :                 *grantorId = otherrole;
    5097           0 :                 *grantOptions = otherprivs;
    5098           0 :                 nrights = nnewrights;
    5099             :             }
    5100             :         }
    5101             :     }
    5102             : }
    5103             : 
    5104             : /*
    5105             :  * get_role_oid - Given a role name, look up the role's OID.
    5106             :  *
    5107             :  * If missing_ok is false, throw an error if role name not found.  If
    5108             :  * true, just return InvalidOid.
    5109             :  */
    5110             : Oid
    5111         858 : get_role_oid(const char *rolname, bool missing_ok)
    5112             : {
    5113             :     Oid         oid;
    5114             : 
    5115         858 :     oid = GetSysCacheOid1(AUTHNAME, CStringGetDatum(rolname));
    5116         858 :     if (!OidIsValid(oid) && !missing_ok)
    5117          17 :         ereport(ERROR,
    5118             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    5119             :                  errmsg("role \"%s\" does not exist", rolname)));
    5120         841 :     return oid;
    5121             : }
    5122             : 
    5123             : /*
    5124             :  * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
    5125             :  *      role name is "public".
    5126             :  */
    5127             : Oid
    5128          53 : get_role_oid_or_public(const char *rolname)
    5129             : {
    5130          53 :     if (strcmp(rolname, "public") == 0)
    5131           0 :         return ACL_ID_PUBLIC;
    5132             : 
    5133          53 :     return get_role_oid(rolname, false);
    5134             : }
    5135             : 
    5136             : /*
    5137             :  * Given a RoleSpec node, return the OID it corresponds to.  If missing_ok is
    5138             :  * true, return InvalidOid if the role does not exist.
    5139             :  *
    5140             :  * PUBLIC is always disallowed here.  Routines wanting to handle the PUBLIC
    5141             :  * case must check the case separately.
    5142             :  */
    5143             : Oid
    5144         497 : get_rolespec_oid(const RoleSpec *role, bool missing_ok)
    5145             : {
    5146             :     Oid         oid;
    5147             : 
    5148         497 :     switch (role->roletype)
    5149             :     {
    5150             :         case ROLESPEC_CSTRING:
    5151         435 :             Assert(role->rolename);
    5152         435 :             oid = get_role_oid(role->rolename, missing_ok);
    5153         424 :             break;
    5154             : 
    5155             :         case ROLESPEC_CURRENT_USER:
    5156          43 :             oid = GetUserId();
    5157          43 :             break;
    5158             : 
    5159             :         case ROLESPEC_SESSION_USER:
    5160          11 :             oid = GetSessionUserId();
    5161          11 :             break;
    5162             : 
    5163             :         case ROLESPEC_PUBLIC:
    5164           8 :             ereport(ERROR,
    5165             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    5166             :                      errmsg("role \"%s\" does not exist", "public")));
    5167             :             oid = InvalidOid;   /* make compiler happy */
    5168             :             break;
    5169             : 
    5170             :         default:
    5171           0 :             elog(ERROR, "unexpected role type %d", role->roletype);
    5172             :     }
    5173             : 
    5174         478 :     return oid;
    5175             : }
    5176             : 
    5177             : /*
    5178             :  * Given a RoleSpec node, return the pg_authid HeapTuple it corresponds to.
    5179             :  * Caller must ReleaseSysCache when done with the result tuple.
    5180             :  */
    5181             : HeapTuple
    5182          81 : get_rolespec_tuple(const RoleSpec *role)
    5183             : {
    5184             :     HeapTuple   tuple;
    5185             : 
    5186          81 :     switch (role->roletype)
    5187             :     {
    5188             :         case ROLESPEC_CSTRING:
    5189          62 :             Assert(role->rolename);
    5190          62 :             tuple = SearchSysCache1(AUTHNAME, CStringGetDatum(role->rolename));
    5191          62 :             if (!HeapTupleIsValid(tuple))
    5192           6 :                 ereport(ERROR,
    5193             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
    5194             :                          errmsg("role \"%s\" does not exist", role->rolename)));
    5195          56 :             break;
    5196             : 
    5197             :         case ROLESPEC_CURRENT_USER:
    5198           7 :             tuple = SearchSysCache1(AUTHOID, GetUserId());
    5199           7 :             if (!HeapTupleIsValid(tuple))
    5200           0 :                 elog(ERROR, "cache lookup failed for role %u", GetUserId());
    5201           7 :             break;
    5202             : 
    5203             :         case ROLESPEC_SESSION_USER:
    5204           6 :             tuple = SearchSysCache1(AUTHOID, GetSessionUserId());
    5205           6 :             if (!HeapTupleIsValid(tuple))
    5206           0 :                 elog(ERROR, "cache lookup failed for role %u", GetSessionUserId());
    5207           6 :             break;
    5208             : 
    5209             :         case ROLESPEC_PUBLIC:
    5210           6 :             ereport(ERROR,
    5211             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    5212             :                      errmsg("role \"%s\" does not exist", "public")));
    5213             :             tuple = NULL;       /* make compiler happy */
    5214             : 
    5215             :         default:
    5216           0 :             elog(ERROR, "unexpected role type %d", role->roletype);
    5217             :     }
    5218             : 
    5219          69 :     return tuple;
    5220             : }
    5221             : 
    5222             : /*
    5223             :  * Given a RoleSpec, returns a palloc'ed copy of the corresponding role's name.
    5224             :  */
    5225             : char *
    5226           3 : get_rolespec_name(const RoleSpec *role)
    5227             : {
    5228             :     HeapTuple   tp;
    5229             :     Form_pg_authid authForm;
    5230             :     char       *rolename;
    5231             : 
    5232           3 :     tp = get_rolespec_tuple(role);
    5233           3 :     authForm = (Form_pg_authid) GETSTRUCT(tp);
    5234           3 :     rolename = pstrdup(NameStr(authForm->rolname));
    5235           3 :     ReleaseSysCache(tp);
    5236             : 
    5237           3 :     return rolename;
    5238             : }
    5239             : 
    5240             : /*
    5241             :  * Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
    5242             :  * if provided.
    5243             :  *
    5244             :  * If node is NULL, no error is thrown.  If detail_msg is NULL then no detail
    5245             :  * message is provided.
    5246             :  */
    5247             : void
    5248          76 : check_rolespec_name(const RoleSpec *role, const char *detail_msg)
    5249             : {
    5250          76 :     if (!role)
    5251           0 :         return;
    5252             : 
    5253          76 :     if (role->roletype != ROLESPEC_CSTRING)
    5254          19 :         return;
    5255             : 
    5256          57 :     if (IsReservedName(role->rolename))
    5257             :     {
    5258           0 :         if (detail_msg)
    5259           0 :             ereport(ERROR,
    5260             :                     (errcode(ERRCODE_RESERVED_NAME),
    5261             :                      errmsg("role name \"%s\" is reserved",
    5262             :                             role->rolename),
    5263             :                      errdetail("%s", detail_msg)));
    5264             :         else
    5265           0 :             ereport(ERROR,
    5266             :                     (errcode(ERRCODE_RESERVED_NAME),
    5267             :                      errmsg("role name \"%s\" is reserved",
    5268             :                             role->rolename)));
    5269             :     }
    5270             : }

Generated by: LCOV version 1.11