LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 271 347 78.1 %
Date: 2021-12-09 03:08:47 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * Utility routines for SQL dumping
       4             :  *
       5             :  * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
       6             :  *
       7             :  *
       8             :  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
       9             :  * Portions Copyright (c) 1994, Regents of the University of California
      10             :  *
      11             :  * src/bin/pg_dump/dumputils.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include <ctype.h>
      18             : 
      19             : #include "dumputils.h"
      20             : #include "fe_utils/string_utils.h"
      21             : 
      22             : 
      23             : static bool parseAclItem(const char *item, const char *type,
      24             :                          const char *name, const char *subname, int remoteVersion,
      25             :                          PQExpBuffer grantee, PQExpBuffer grantor,
      26             :                          PQExpBuffer privs, PQExpBuffer privswgo);
      27             : static char *dequoteAclUserName(PQExpBuffer output, char *input);
      28             : static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
      29             :                    const char *subname);
      30             : 
      31             : 
      32             : /*
      33             :  * Build GRANT/REVOKE command(s) for an object.
      34             :  *
      35             :  *  name: the object name, in the form to use in the commands (already quoted)
      36             :  *  subname: the sub-object name, if any (already quoted); NULL if none
      37             :  *  nspname: the namespace the object is in (NULL if none); not pre-quoted
      38             :  *  type: the object type (as seen in GRANT command: must be one of
      39             :  *      TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
      40             :  *      FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
      41             :  *  acls: the ACL string fetched from the database
      42             :  *  baseacls: the initial ACL string for this object; can be
      43             :  *      NULL or empty string to indicate "not available from server"
      44             :  *  owner: username of object owner (will be passed through fmtId); can be
      45             :  *      NULL or empty string to indicate "no owner known"
      46             :  *  prefix: string to prefix to each generated command; typically empty
      47             :  *  remoteVersion: version of database
      48             :  *
      49             :  * Returns true if okay, false if could not parse the acl string.
      50             :  * The resulting commands (if any) are appended to the contents of 'sql'.
      51             :  *
      52             :  * baseacls is typically the result of acldefault() for the object's type
      53             :  * and owner.  However, if there is a pg_init_privs entry for the object,
      54             :  * it should instead be the initprivs ACLs.  When acls is itself a
      55             :  * pg_init_privs entry, baseacls is what to dump that relative to; then
      56             :  * it can be either an acldefault() value or an empty ACL "{}".
      57             :  *
      58             :  * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
      59             :  * or something similar, and name is an empty string.
      60             :  *
      61             :  * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
      62             :  * since this routine uses fmtId() internally.
      63             :  */
      64             : bool
      65       25210 : buildACLCommands(const char *name, const char *subname, const char *nspname,
      66             :                  const char *type, const char *acls, const char *baseacls,
      67             :                  const char *owner, const char *prefix, int remoteVersion,
      68             :                  PQExpBuffer sql)
      69             : {
      70       25210 :     bool        ok = true;
      71       25210 :     char      **aclitems = NULL;
      72       25210 :     char      **baseitems = NULL;
      73       25210 :     char      **grantitems = NULL;
      74       25210 :     char      **revokeitems = NULL;
      75       25210 :     int         naclitems = 0;
      76       25210 :     int         nbaseitems = 0;
      77       25210 :     int         ngrantitems = 0;
      78       25210 :     int         nrevokeitems = 0;
      79             :     int         i;
      80             :     PQExpBuffer grantee,
      81             :                 grantor,
      82             :                 privs,
      83             :                 privswgo;
      84             :     PQExpBuffer firstsql,
      85             :                 secondsql;
      86             : 
      87             :     /*
      88             :      * If the acl was NULL (initial default state), we need do nothing.  Note
      89             :      * that this is distinguishable from all-privileges-revoked, which will
      90             :      * look like an empty array ("{}").
      91             :      */
      92       25210 :     if (acls == NULL || *acls == '\0')
      93         108 :         return true;            /* object has default permissions */
      94             : 
      95             :     /* treat empty-string owner same as NULL */
      96       25102 :     if (owner && *owner == '\0')
      97           0 :         owner = NULL;
      98             : 
      99             :     /* Parse the acls array */
     100       25102 :     if (!parsePGArray(acls, &aclitems, &naclitems))
     101             :     {
     102           0 :         if (aclitems)
     103           0 :             free(aclitems);
     104           0 :         return false;
     105             :     }
     106             : 
     107             :     /* Parse the baseacls, if provided */
     108       25102 :     if (baseacls && *baseacls != '\0')
     109             :     {
     110       25102 :         if (!parsePGArray(baseacls, &baseitems, &nbaseitems))
     111             :         {
     112           0 :             if (aclitems)
     113           0 :                 free(aclitems);
     114           0 :             if (baseitems)
     115           0 :                 free(baseitems);
     116           0 :             return false;
     117             :         }
     118             :     }
     119             : 
     120             :     /*
     121             :      * Compare the actual ACL with the base ACL, extracting the privileges
     122             :      * that need to be granted (i.e., are in the actual ACL but not the base
     123             :      * ACL) and the ones that need to be revoked (the reverse).  We use plain
     124             :      * string comparisons to check for matches.  In principle that could be
     125             :      * fooled by extraneous issues such as whitespace, but since all these
     126             :      * strings are the work of aclitemout(), it should be OK in practice.
     127             :      * Besides, a false mismatch will just cause the output to be a little
     128             :      * more verbose than it really needed to be.
     129             :      *
     130             :      * (If we weren't given a base ACL, this stanza winds up with all the
     131             :      * ACL's items in grantitems and nothing in revokeitems.  It's not worth
     132             :      * special-casing that.)
     133             :      */
     134       25102 :     grantitems = (char **) pg_malloc(naclitems * sizeof(char *));
     135       70362 :     for (i = 0; i < naclitems; i++)
     136             :     {
     137       45260 :         bool        found = false;
     138             : 
     139       66120 :         for (int j = 0; j < nbaseitems; j++)
     140             :         {
     141       63830 :             if (strcmp(aclitems[i], baseitems[j]) == 0)
     142             :             {
     143       42970 :                 found = true;
     144       42970 :                 break;
     145             :             }
     146             :         }
     147       45260 :         if (!found)
     148        2290 :             grantitems[ngrantitems++] = aclitems[i];
     149             :     }
     150       25102 :     revokeitems = (char **) pg_malloc(nbaseitems * sizeof(char *));
     151       68584 :     for (i = 0; i < nbaseitems; i++)
     152             :     {
     153       43482 :         bool        found = false;
     154             : 
     155       63476 :         for (int j = 0; j < naclitems; j++)
     156             :         {
     157       62964 :             if (strcmp(baseitems[i], aclitems[j]) == 0)
     158             :             {
     159       42970 :                 found = true;
     160       42970 :                 break;
     161             :             }
     162             :         }
     163       43482 :         if (!found)
     164         512 :             revokeitems[nrevokeitems++] = baseitems[i];
     165             :     }
     166             : 
     167             :     /* Prepare working buffers */
     168       25102 :     grantee = createPQExpBuffer();
     169       25102 :     grantor = createPQExpBuffer();
     170       25102 :     privs = createPQExpBuffer();
     171       25102 :     privswgo = createPQExpBuffer();
     172             : 
     173             :     /*
     174             :      * At the end, these two will be pasted together to form the result.
     175             :      */
     176       25102 :     firstsql = createPQExpBuffer();
     177       25102 :     secondsql = createPQExpBuffer();
     178             : 
     179             :     /*
     180             :      * If we weren't given baseacls information, we just revoke everything and
     181             :      * then grant what's listed in the ACL.  This avoids having to embed
     182             :      * detailed knowledge about what the defaults are/were, and it's not very
     183             :      * expensive since servers lacking acldefault() are now rare.
     184             :      *
     185             :      * Otherwise, we need only revoke what's listed in revokeitems.
     186             :      */
     187       25102 :     if (baseacls == NULL || *baseacls == '\0')
     188             :     {
     189             :         /* We assume the old defaults only involved the owner and PUBLIC */
     190           0 :         appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
     191           0 :         if (subname)
     192           0 :             appendPQExpBuffer(firstsql, "(%s)", subname);
     193           0 :         appendPQExpBuffer(firstsql, " ON %s ", type);
     194           0 :         if (nspname && *nspname)
     195           0 :             appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     196           0 :         appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name);
     197           0 :         if (owner)
     198             :         {
     199           0 :             appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
     200           0 :             if (subname)
     201           0 :                 appendPQExpBuffer(firstsql, "(%s)", subname);
     202           0 :             appendPQExpBuffer(firstsql, " ON %s ", type);
     203           0 :             if (nspname && *nspname)
     204           0 :                 appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     205           0 :             appendPQExpBuffer(firstsql, "%s FROM %s;\n", name, fmtId(owner));
     206             :         }
     207             :     }
     208             :     else
     209             :     {
     210             :         /* Scan individual REVOKE ACL items */
     211       25614 :         for (i = 0; i < nrevokeitems; i++)
     212             :         {
     213         512 :             if (!parseAclItem(revokeitems[i],
     214             :                               type, name, subname, remoteVersion,
     215             :                               grantee, grantor, privs, NULL))
     216             :             {
     217           0 :                 ok = false;
     218           0 :                 break;
     219             :             }
     220             : 
     221         512 :             if (privs->len > 0)
     222             :             {
     223         512 :                 appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
     224             :                                   prefix, privs->data, type);
     225         512 :                 if (nspname && *nspname)
     226         324 :                     appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     227         512 :                 appendPQExpBuffer(firstsql, "%s FROM ", name);
     228         512 :                 if (grantee->len == 0)
     229         252 :                     appendPQExpBufferStr(firstsql, "PUBLIC;\n");
     230         260 :                 else if (strncmp(grantee->data, "group ",
     231             :                                  strlen("group ")) == 0)
     232           0 :                     appendPQExpBuffer(firstsql, "GROUP %s;\n",
     233           0 :                                       fmtId(grantee->data + strlen("group ")));
     234             :                 else
     235         260 :                     appendPQExpBuffer(firstsql, "%s;\n",
     236         260 :                                       fmtId(grantee->data));
     237             :             }
     238             :         }
     239             :     }
     240             : 
     241             :     /*
     242             :      * At this point we have issued REVOKE statements for all initial and
     243             :      * default privileges that are no longer present on the object, so we are
     244             :      * almost ready to GRANT the privileges listed in grantitems[].
     245             :      *
     246             :      * We still need some hacking though to cover the case where new default
     247             :      * public privileges are added in new versions: the REVOKE ALL will revoke
     248             :      * them, leading to behavior different from what the old version had,
     249             :      * which is generally not what's wanted.  So add back default privs if the
     250             :      * source database is too old to have had that particular priv.
     251             :      */
     252       25102 :     if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
     253             :     {
     254             :         /* database CONNECT priv didn't exist before 8.2 */
     255           0 :         appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
     256             :                           prefix, type, name);
     257             :     }
     258             : 
     259             :     /*
     260             :      * Scan individual ACL items to be granted.
     261             :      *
     262             :      * The order in which privileges appear in the ACL string (the order they
     263             :      * have been GRANT'd in, which the backend maintains) must be preserved to
     264             :      * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
     265             :      * those are dumped in the correct order.  However, some old server
     266             :      * versions will show grants to PUBLIC before the owner's own grants; for
     267             :      * consistency's sake, force the owner's grants to be output first.
     268             :      */
     269       27392 :     for (i = 0; i < ngrantitems; i++)
     270             :     {
     271        2290 :         if (parseAclItem(grantitems[i], type, name, subname, remoteVersion,
     272             :                          grantee, grantor, privs, privswgo))
     273             :         {
     274             :             /*
     275             :              * If the grantor isn't the owner, we'll need to use SET SESSION
     276             :              * AUTHORIZATION to become the grantor.  Issue the SET/RESET only
     277             :              * if there's something useful to do.
     278             :              */
     279        2290 :             if (privs->len > 0 || privswgo->len > 0)
     280             :             {
     281             :                 PQExpBuffer thissql;
     282             : 
     283             :                 /* Set owner as grantor if that's not explicit in the ACL */
     284        2290 :                 if (grantor->len == 0 && owner)
     285           0 :                     printfPQExpBuffer(grantor, "%s", owner);
     286             : 
     287             :                 /* Make sure owner's own grants are output before others */
     288        2290 :                 if (owner &&
     289        2290 :                     strcmp(grantee->data, owner) == 0 &&
     290         154 :                     strcmp(grantor->data, owner) == 0)
     291         154 :                     thissql = firstsql;
     292             :                 else
     293        2136 :                     thissql = secondsql;
     294             : 
     295        2290 :                 if (grantor->len > 0
     296        2290 :                     && (!owner || strcmp(owner, grantor->data) != 0))
     297           0 :                     appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
     298           0 :                                       fmtId(grantor->data));
     299             : 
     300        2290 :                 if (privs->len > 0)
     301             :                 {
     302        2288 :                     appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     303             :                                       prefix, privs->data, type);
     304        2288 :                     if (nspname && *nspname)
     305        1970 :                         appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     306        2288 :                     appendPQExpBuffer(thissql, "%s TO ", name);
     307        2288 :                     if (grantee->len == 0)
     308        1308 :                         appendPQExpBufferStr(thissql, "PUBLIC;\n");
     309         980 :                     else if (strncmp(grantee->data, "group ",
     310             :                                      strlen("group ")) == 0)
     311           0 :                         appendPQExpBuffer(thissql, "GROUP %s;\n",
     312           0 :                                           fmtId(grantee->data + strlen("group ")));
     313             :                     else
     314         980 :                         appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
     315             :                 }
     316        2290 :                 if (privswgo->len > 0)
     317             :                 {
     318          36 :                     appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     319             :                                       prefix, privswgo->data, type);
     320          36 :                     if (nspname && *nspname)
     321          36 :                         appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     322          36 :                     appendPQExpBuffer(thissql, "%s TO ", name);
     323          36 :                     if (grantee->len == 0)
     324           0 :                         appendPQExpBufferStr(thissql, "PUBLIC");
     325          36 :                     else if (strncmp(grantee->data, "group ",
     326             :                                      strlen("group ")) == 0)
     327           0 :                         appendPQExpBuffer(thissql, "GROUP %s",
     328           0 :                                           fmtId(grantee->data + strlen("group ")));
     329             :                     else
     330          36 :                         appendPQExpBufferStr(thissql, fmtId(grantee->data));
     331          36 :                     appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
     332             :                 }
     333             : 
     334        2290 :                 if (grantor->len > 0
     335        2290 :                     && (!owner || strcmp(owner, grantor->data) != 0))
     336           0 :                     appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n");
     337             :             }
     338             :         }
     339             :         else
     340             :         {
     341             :             /* parseAclItem failed, give up */
     342           0 :             ok = false;
     343           0 :             break;
     344             :         }
     345             :     }
     346             : 
     347       25102 :     destroyPQExpBuffer(grantee);
     348       25102 :     destroyPQExpBuffer(grantor);
     349       25102 :     destroyPQExpBuffer(privs);
     350       25102 :     destroyPQExpBuffer(privswgo);
     351             : 
     352       25102 :     appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
     353       25102 :     destroyPQExpBuffer(firstsql);
     354       25102 :     destroyPQExpBuffer(secondsql);
     355             : 
     356       25102 :     if (aclitems)
     357       25102 :         free(aclitems);
     358       25102 :     if (baseitems)
     359       25102 :         free(baseitems);
     360       25102 :     if (grantitems)
     361       25102 :         free(grantitems);
     362       25102 :     if (revokeitems)
     363       25102 :         free(revokeitems);
     364             : 
     365       25102 :     return ok;
     366             : }
     367             : 
     368             : /*
     369             :  * Build ALTER DEFAULT PRIVILEGES command(s) for a single pg_default_acl entry.
     370             :  *
     371             :  *  type: the object type (TABLES, FUNCTIONS, etc)
     372             :  *  nspname: schema name, or NULL for global default privileges
     373             :  *  acls: the ACL string fetched from the database
     374             :  *  acldefault: the appropriate default ACL for the object type and owner
     375             :  *  owner: username of privileges owner (will be passed through fmtId)
     376             :  *  remoteVersion: version of database
     377             :  *
     378             :  * Returns true if okay, false if could not parse the acl string.
     379             :  * The resulting commands (if any) are appended to the contents of 'sql'.
     380             :  */
     381             : bool
     382         172 : buildDefaultACLCommands(const char *type, const char *nspname,
     383             :                         const char *acls, const char *acldefault,
     384             :                         const char *owner,
     385             :                         int remoteVersion,
     386             :                         PQExpBuffer sql)
     387             : {
     388             :     PQExpBuffer prefix;
     389             : 
     390         172 :     prefix = createPQExpBuffer();
     391             : 
     392             :     /*
     393             :      * We incorporate the target role directly into the command, rather than
     394             :      * playing around with SET ROLE or anything like that.  This is so that a
     395             :      * permissions error leads to nothing happening, rather than changing
     396             :      * default privileges for the wrong user.
     397             :      */
     398         172 :     appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
     399             :                       fmtId(owner));
     400         172 :     if (nspname)
     401          88 :         appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
     402             : 
     403             :     /*
     404             :      * There's no such thing as initprivs for a default ACL, so the base ACL
     405             :      * is always just the object-type-specific default.
     406             :      */
     407         172 :     if (!buildACLCommands("", NULL, NULL, type,
     408             :                           acls, acldefault, owner,
     409         172 :                           prefix->data, remoteVersion, sql))
     410             :     {
     411           0 :         destroyPQExpBuffer(prefix);
     412           0 :         return false;
     413             :     }
     414             : 
     415         172 :     destroyPQExpBuffer(prefix);
     416             : 
     417         172 :     return true;
     418             : }
     419             : 
     420             : /*
     421             :  * This will parse an aclitem string, having the general form
     422             :  *      username=privilegecodes/grantor
     423             :  * or
     424             :  *      group groupname=privilegecodes/grantor
     425             :  * (the "group" case occurs only with servers before 8.1).
     426             :  *
     427             :  * Returns true on success, false on parse error.  On success, the components
     428             :  * of the string are returned in the PQExpBuffer parameters.
     429             :  *
     430             :  * The returned grantee string will be the dequoted username or groupname
     431             :  * (preceded with "group " in the latter case).  Note that a grant to PUBLIC
     432             :  * is represented by an empty grantee string.  The returned grantor is the
     433             :  * dequoted grantor name.  Privilege characters are translated to GRANT/REVOKE
     434             :  * comma-separated privileges lists.  If "privswgo" is non-NULL, the result is
     435             :  * separate lists for privileges with grant option ("privswgo") and without
     436             :  * ("privs").  Otherwise, "privs" bears every relevant privilege, ignoring the
     437             :  * grant option distinction.
     438             :  *
     439             :  * Note: for cross-version compatibility, it's important to use ALL to
     440             :  * represent the privilege sets whenever appropriate.
     441             :  */
     442             : static bool
     443        2802 : parseAclItem(const char *item, const char *type,
     444             :              const char *name, const char *subname, int remoteVersion,
     445             :              PQExpBuffer grantee, PQExpBuffer grantor,
     446             :              PQExpBuffer privs, PQExpBuffer privswgo)
     447             : {
     448             :     char       *buf;
     449        2802 :     bool        all_with_go = true;
     450        2802 :     bool        all_without_go = true;
     451             :     char       *eqpos;
     452             :     char       *slpos;
     453             :     char       *pos;
     454             : 
     455        2802 :     buf = pg_strdup(item);
     456             : 
     457             :     /* user or group name is string up to = */
     458        2802 :     eqpos = dequoteAclUserName(grantee, buf);
     459        2802 :     if (*eqpos != '=')
     460             :     {
     461           0 :         pg_free(buf);
     462           0 :         return false;
     463             :     }
     464             : 
     465             :     /* grantor should appear after / */
     466        2802 :     slpos = strchr(eqpos + 1, '/');
     467        2802 :     if (slpos)
     468             :     {
     469        2802 :         *slpos++ = '\0';
     470        2802 :         slpos = dequoteAclUserName(grantor, slpos);
     471        2802 :         if (*slpos != '\0')
     472             :         {
     473           0 :             pg_free(buf);
     474           0 :             return false;
     475             :         }
     476             :     }
     477             :     else
     478             :     {
     479           0 :         pg_free(buf);
     480           0 :         return false;
     481             :     }
     482             : 
     483             :     /* privilege codes */
     484             : #define CONVERT_PRIV(code, keywd) \
     485             : do { \
     486             :     if ((pos = strchr(eqpos + 1, code))) \
     487             :     { \
     488             :         if (*(pos + 1) == '*' && privswgo != NULL) \
     489             :         { \
     490             :             AddAcl(privswgo, keywd, subname); \
     491             :             all_without_go = false; \
     492             :         } \
     493             :         else \
     494             :         { \
     495             :             AddAcl(privs, keywd, subname); \
     496             :             all_with_go = false; \
     497             :         } \
     498             :     } \
     499             :     else \
     500             :         all_with_go = all_without_go = false; \
     501             : } while (0)
     502             : 
     503        2802 :     resetPQExpBuffer(privs);
     504        2802 :     resetPQExpBuffer(privswgo);
     505             : 
     506        2802 :     if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
     507         912 :         strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
     508             :     {
     509        2018 :         CONVERT_PRIV('r', "SELECT");
     510             : 
     511        2018 :         if (strcmp(type, "SEQUENCE") == 0 ||
     512        1916 :             strcmp(type, "SEQUENCES") == 0)
     513             :             /* sequence only */
     514         102 :             CONVERT_PRIV('U', "USAGE");
     515             :         else
     516             :         {
     517             :             /* table only */
     518        1916 :             CONVERT_PRIV('a', "INSERT");
     519        1916 :             CONVERT_PRIV('x', "REFERENCES");
     520             :             /* rest are not applicable to columns */
     521        1916 :             if (subname == NULL)
     522             :             {
     523         528 :                 CONVERT_PRIV('d', "DELETE");
     524         528 :                 CONVERT_PRIV('t', "TRIGGER");
     525         528 :                 if (remoteVersion >= 80400)
     526         528 :                     CONVERT_PRIV('D', "TRUNCATE");
     527             :             }
     528             :         }
     529             : 
     530             :         /* UPDATE */
     531        2018 :         CONVERT_PRIV('w', "UPDATE");
     532             :     }
     533         784 :     else if (strcmp(type, "FUNCTION") == 0 ||
     534         556 :              strcmp(type, "FUNCTIONS") == 0)
     535         314 :         CONVERT_PRIV('X', "EXECUTE");
     536         470 :     else if (strcmp(type, "PROCEDURE") == 0 ||
     537         470 :              strcmp(type, "PROCEDURES") == 0)
     538           0 :         CONVERT_PRIV('X', "EXECUTE");
     539         470 :     else if (strcmp(type, "LANGUAGE") == 0)
     540          52 :         CONVERT_PRIV('U', "USAGE");
     541         418 :     else if (strcmp(type, "SCHEMA") == 0 ||
     542         318 :              strcmp(type, "SCHEMAS") == 0)
     543             :     {
     544         100 :         CONVERT_PRIV('C', "CREATE");
     545         100 :         CONVERT_PRIV('U', "USAGE");
     546             :     }
     547         318 :     else if (strcmp(type, "DATABASE") == 0)
     548             :     {
     549          10 :         CONVERT_PRIV('C', "CREATE");
     550          10 :         CONVERT_PRIV('c', "CONNECT");
     551          10 :         CONVERT_PRIV('T', "TEMPORARY");
     552             :     }
     553         308 :     else if (strcmp(type, "TABLESPACE") == 0)
     554           0 :         CONVERT_PRIV('C', "CREATE");
     555         308 :     else if (strcmp(type, "TYPE") == 0 ||
     556         130 :              strcmp(type, "TYPES") == 0)
     557         178 :         CONVERT_PRIV('U', "USAGE");
     558         130 :     else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
     559          42 :         CONVERT_PRIV('U', "USAGE");
     560          88 :     else if (strcmp(type, "FOREIGN SERVER") == 0)
     561          42 :         CONVERT_PRIV('U', "USAGE");
     562          46 :     else if (strcmp(type, "FOREIGN TABLE") == 0)
     563           0 :         CONVERT_PRIV('r', "SELECT");
     564          46 :     else if (strcmp(type, "LARGE OBJECT") == 0)
     565             :     {
     566          46 :         CONVERT_PRIV('r', "SELECT");
     567          46 :         CONVERT_PRIV('w', "UPDATE");
     568             :     }
     569             :     else
     570           0 :         abort();
     571             : 
     572             : #undef CONVERT_PRIV
     573             : 
     574        2802 :     if (all_with_go)
     575             :     {
     576           2 :         resetPQExpBuffer(privs);
     577           2 :         printfPQExpBuffer(privswgo, "ALL");
     578           2 :         if (subname)
     579           0 :             appendPQExpBuffer(privswgo, "(%s)", subname);
     580             :     }
     581        2800 :     else if (all_without_go)
     582             :     {
     583         800 :         resetPQExpBuffer(privswgo);
     584         800 :         printfPQExpBuffer(privs, "ALL");
     585         800 :         if (subname)
     586           0 :             appendPQExpBuffer(privs, "(%s)", subname);
     587             :     }
     588             : 
     589        2802 :     pg_free(buf);
     590             : 
     591        2802 :     return true;
     592             : }
     593             : 
     594             : /*
     595             :  * Transfer the role name at *input into the output buffer, adding
     596             :  * quoting according to the same rules as putid() in backend's acl.c.
     597             :  */
     598             : void
     599         534 : quoteAclUserName(PQExpBuffer output, const char *input)
     600             : {
     601             :     const char *src;
     602         534 :     bool        safe = true;
     603             : 
     604        8982 :     for (src = input; *src; src++)
     605             :     {
     606             :         /* This test had better match what putid() does */
     607        8658 :         if (!isalnum((unsigned char) *src) && *src != '_')
     608             :         {
     609         210 :             safe = false;
     610         210 :             break;
     611             :         }
     612             :     }
     613         534 :     if (!safe)
     614         210 :         appendPQExpBufferChar(output, '"');
     615       10872 :     for (src = input; *src; src++)
     616             :     {
     617             :         /* A double quote character in a username is encoded as "" */
     618       10338 :         if (*src == '"')
     619         210 :             appendPQExpBufferChar(output, '"');
     620       10338 :         appendPQExpBufferChar(output, *src);
     621             :     }
     622         534 :     if (!safe)
     623         210 :         appendPQExpBufferChar(output, '"');
     624         534 : }
     625             : 
     626             : /*
     627             :  * Transfer a user or group name starting at *input into the output buffer,
     628             :  * dequoting if needed.  Returns a pointer to just past the input name.
     629             :  * The name is taken to end at an unquoted '=' or end of string.
     630             :  * Note: unlike quoteAclUserName(), this first clears the output buffer.
     631             :  */
     632             : static char *
     633        5604 : dequoteAclUserName(PQExpBuffer output, char *input)
     634             : {
     635        5604 :     resetPQExpBuffer(output);
     636             : 
     637       55532 :     while (*input && *input != '=')
     638             :     {
     639             :         /*
     640             :          * If user name isn't quoted, then just add it to the output buffer
     641             :          */
     642       49928 :         if (*input != '"')
     643       49844 :             appendPQExpBufferChar(output, *input++);
     644             :         else
     645             :         {
     646             :             /* Otherwise, it's a quoted username */
     647          84 :             input++;
     648             :             /* Loop until we come across an unescaped quote */
     649        2016 :             while (!(*input == '"' && *(input + 1) != '"'))
     650             :             {
     651        1932 :                 if (*input == '\0')
     652           0 :                     return input;   /* really a syntax error... */
     653             : 
     654             :                 /*
     655             :                  * Quoting convention is to escape " as "".  Keep this code in
     656             :                  * sync with putid() in backend's acl.c.
     657             :                  */
     658        1932 :                 if (*input == '"' && *(input + 1) == '"')
     659          84 :                     input++;
     660        1932 :                 appendPQExpBufferChar(output, *input++);
     661             :             }
     662          84 :             input++;
     663             :         }
     664             :     }
     665        5604 :     return input;
     666             : }
     667             : 
     668             : /*
     669             :  * Append a privilege keyword to a keyword list, inserting comma if needed.
     670             :  */
     671             : static void
     672        3560 : AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
     673             : {
     674        3560 :     if (aclbuf->len > 0)
     675         724 :         appendPQExpBufferChar(aclbuf, ',');
     676        3560 :     appendPQExpBufferStr(aclbuf, keyword);
     677        3560 :     if (subname)
     678        1388 :         appendPQExpBuffer(aclbuf, "(%s)", subname);
     679        3560 : }
     680             : 
     681             : 
     682             : /*
     683             :  * buildShSecLabelQuery
     684             :  *
     685             :  * Build a query to retrieve security labels for a shared object.
     686             :  * The object is identified by its OID plus the name of the catalog
     687             :  * it can be found in (e.g., "pg_database" for database names).
     688             :  * The query is appended to "sql".  (We don't execute it here so as to
     689             :  * keep this file free of assumptions about how to deal with SQL errors.)
     690             :  */
     691             : void
     692         176 : buildShSecLabelQuery(const char *catalog_name, Oid objectId,
     693             :                      PQExpBuffer sql)
     694             : {
     695         176 :     appendPQExpBuffer(sql,
     696             :                       "SELECT provider, label FROM pg_catalog.pg_shseclabel "
     697             :                       "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
     698             :                       "AND objoid = '%u'", catalog_name, objectId);
     699         176 : }
     700             : 
     701             : /*
     702             :  * emitShSecLabels
     703             :  *
     704             :  * Construct SECURITY LABEL commands using the data retrieved by the query
     705             :  * generated by buildShSecLabelQuery, and append them to "buffer".
     706             :  * Here, the target object is identified by its type name (e.g. "DATABASE")
     707             :  * and its name (not pre-quoted).
     708             :  */
     709             : void
     710         176 : emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
     711             :                 const char *objtype, const char *objname)
     712             : {
     713             :     int         i;
     714             : 
     715         176 :     for (i = 0; i < PQntuples(res); i++)
     716             :     {
     717           0 :         char       *provider = PQgetvalue(res, i, 0);
     718           0 :         char       *label = PQgetvalue(res, i, 1);
     719             : 
     720             :         /* must use fmtId result before calling it again */
     721           0 :         appendPQExpBuffer(buffer,
     722             :                           "SECURITY LABEL FOR %s ON %s",
     723             :                           fmtId(provider), objtype);
     724           0 :         appendPQExpBuffer(buffer,
     725             :                           " %s IS ",
     726             :                           fmtId(objname));
     727           0 :         appendStringLiteralConn(buffer, label, conn);
     728           0 :         appendPQExpBufferStr(buffer, ";\n");
     729             :     }
     730         176 : }
     731             : 
     732             : 
     733             : /*
     734             :  * Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
     735             :  *
     736             :  * It'd be better if we could inquire this directly from the backend; but even
     737             :  * if there were a function for that, it could only tell us about variables
     738             :  * currently known to guc.c, so that it'd be unsafe for extensions to declare
     739             :  * GUC_LIST_QUOTE variables anyway.  Lacking a solution for that, it doesn't
     740             :  * seem worth the work to do more than have this list, which must be kept in
     741             :  * sync with the variables actually marked GUC_LIST_QUOTE in guc.c.
     742             :  */
     743             : bool
     744          78 : variable_is_guc_list_quote(const char *name)
     745             : {
     746         150 :     if (pg_strcasecmp(name, "local_preload_libraries") == 0 ||
     747         138 :         pg_strcasecmp(name, "search_path") == 0 ||
     748         132 :         pg_strcasecmp(name, "session_preload_libraries") == 0 ||
     749         132 :         pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
     750         132 :         pg_strcasecmp(name, "temp_tablespaces") == 0 ||
     751          66 :         pg_strcasecmp(name, "unix_socket_directories") == 0)
     752          12 :         return true;
     753             :     else
     754          66 :         return false;
     755             : }
     756             : 
     757             : /*
     758             :  * SplitGUCList --- parse a string containing identifiers or file names
     759             :  *
     760             :  * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
     761             :  * presuming whether the elements will be taken as identifiers or file names.
     762             :  * See comparable code in src/backend/utils/adt/varlena.c.
     763             :  *
     764             :  * Inputs:
     765             :  *  rawstring: the input string; must be overwritable!  On return, it's
     766             :  *             been modified to contain the separated identifiers.
     767             :  *  separator: the separator punctuation expected between identifiers
     768             :  *             (typically '.' or ',').  Whitespace may also appear around
     769             :  *             identifiers.
     770             :  * Outputs:
     771             :  *  namelist: receives a malloc'd, null-terminated array of pointers to
     772             :  *            identifiers within rawstring.  Caller should free this
     773             :  *            even on error return.
     774             :  *
     775             :  * Returns true if okay, false if there is a syntax error in the string.
     776             :  */
     777             : bool
     778          12 : SplitGUCList(char *rawstring, char separator,
     779             :              char ***namelist)
     780             : {
     781          12 :     char       *nextp = rawstring;
     782          12 :     bool        done = false;
     783             :     char      **nextptr;
     784             : 
     785             :     /*
     786             :      * Since we disallow empty identifiers, this is a conservative
     787             :      * overestimate of the number of pointers we could need.  Allow one for
     788             :      * list terminator.
     789             :      */
     790          12 :     *namelist = nextptr = (char **)
     791          12 :         pg_malloc((strlen(rawstring) / 2 + 2) * sizeof(char *));
     792          12 :     *nextptr = NULL;
     793             : 
     794          12 :     while (isspace((unsigned char) *nextp))
     795           0 :         nextp++;                /* skip leading whitespace */
     796             : 
     797          12 :     if (*nextp == '\0')
     798           0 :         return true;            /* allow empty string */
     799             : 
     800             :     /* At the top of the loop, we are at start of a new identifier. */
     801             :     do
     802             :     {
     803             :         char       *curname;
     804             :         char       *endp;
     805             : 
     806          30 :         if (*nextp == '"')
     807             :         {
     808             :             /* Quoted name --- collapse quote-quote pairs */
     809          24 :             curname = nextp + 1;
     810             :             for (;;)
     811             :             {
     812          36 :                 endp = strchr(nextp + 1, '"');
     813          30 :                 if (endp == NULL)
     814           0 :                     return false;   /* mismatched quotes */
     815          30 :                 if (endp[1] != '"')
     816          24 :                     break;      /* found end of quoted name */
     817             :                 /* Collapse adjacent quotes into one quote, and look again */
     818           6 :                 memmove(endp, endp + 1, strlen(endp));
     819           6 :                 nextp = endp;
     820             :             }
     821             :             /* endp now points at the terminating quote */
     822          24 :             nextp = endp + 1;
     823             :         }
     824             :         else
     825             :         {
     826             :             /* Unquoted name --- extends to separator or whitespace */
     827           6 :             curname = nextp;
     828          66 :             while (*nextp && *nextp != separator &&
     829          60 :                    !isspace((unsigned char) *nextp))
     830          60 :                 nextp++;
     831           6 :             endp = nextp;
     832           6 :             if (curname == nextp)
     833           0 :                 return false;   /* empty unquoted name not allowed */
     834             :         }
     835             : 
     836          30 :         while (isspace((unsigned char) *nextp))
     837           0 :             nextp++;            /* skip trailing whitespace */
     838             : 
     839          30 :         if (*nextp == separator)
     840             :         {
     841          18 :             nextp++;
     842          36 :             while (isspace((unsigned char) *nextp))
     843          18 :                 nextp++;        /* skip leading whitespace for next */
     844             :             /* we expect another name, so done remains false */
     845             :         }
     846          12 :         else if (*nextp == '\0')
     847          12 :             done = true;
     848             :         else
     849           0 :             return false;       /* invalid syntax */
     850             : 
     851             :         /* Now safe to overwrite separator with a null */
     852          30 :         *endp = '\0';
     853             : 
     854             :         /*
     855             :          * Finished isolating current name --- add it to output array
     856             :          */
     857          30 :         *nextptr++ = curname;
     858             : 
     859             :         /* Loop back if we didn't reach end of string */
     860          30 :     } while (!done);
     861             : 
     862          12 :     *nextptr = NULL;
     863          12 :     return true;
     864             : }
     865             : 
     866             : /*
     867             :  * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
     868             :  *
     869             :  * Parse the contents of configitem (a "name=value" string), wrap it in
     870             :  * a complete ALTER command, and append it to buf.
     871             :  *
     872             :  * type is DATABASE or ROLE, and name is the name of the database or role.
     873             :  * If we need an "IN" clause, type2 and name2 similarly define what to put
     874             :  * there; otherwise they should be NULL.
     875             :  * conn is used only to determine string-literal quoting conventions.
     876             :  */
     877             : void
     878          36 : makeAlterConfigCommand(PGconn *conn, const char *configitem,
     879             :                        const char *type, const char *name,
     880             :                        const char *type2, const char *name2,
     881             :                        PQExpBuffer buf)
     882             : {
     883             :     char       *mine;
     884             :     char       *pos;
     885             : 
     886             :     /* Parse the configitem.  If we can't find an "=", silently do nothing. */
     887          36 :     mine = pg_strdup(configitem);
     888          36 :     pos = strchr(mine, '=');
     889          36 :     if (pos == NULL)
     890             :     {
     891           0 :         pg_free(mine);
     892           0 :         return;
     893             :     }
     894          36 :     *pos++ = '\0';
     895             : 
     896             :     /* Build the command, with suitable quoting for everything. */
     897          36 :     appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
     898          36 :     if (type2 != NULL && name2 != NULL)
     899           0 :         appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
     900          36 :     appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
     901             : 
     902             :     /*
     903             :      * Variables that are marked GUC_LIST_QUOTE were already fully quoted by
     904             :      * flatten_set_variable_args() before they were put into the setconfig
     905             :      * array.  However, because the quoting rules used there aren't exactly
     906             :      * like SQL's, we have to break the list value apart and then quote the
     907             :      * elements as string literals.  (The elements may be double-quoted as-is,
     908             :      * but we can't just feed them to the SQL parser; it would do the wrong
     909             :      * thing with elements that are zero-length or longer than NAMEDATALEN.)
     910             :      *
     911             :      * Variables that are not so marked should just be emitted as simple
     912             :      * string literals.  If the variable is not known to
     913             :      * variable_is_guc_list_quote(), we'll do that; this makes it unsafe to
     914             :      * use GUC_LIST_QUOTE for extension variables.
     915             :      */
     916          36 :     if (variable_is_guc_list_quote(mine))
     917             :     {
     918             :         char      **namelist;
     919             :         char      **nameptr;
     920             : 
     921             :         /* Parse string into list of identifiers */
     922             :         /* this shouldn't fail really */
     923           0 :         if (SplitGUCList(pos, ',', &namelist))
     924             :         {
     925           0 :             for (nameptr = namelist; *nameptr; nameptr++)
     926             :             {
     927           0 :                 if (nameptr != namelist)
     928           0 :                     appendPQExpBufferStr(buf, ", ");
     929           0 :                 appendStringLiteralConn(buf, *nameptr, conn);
     930             :             }
     931             :         }
     932           0 :         pg_free(namelist);
     933             :     }
     934             :     else
     935          36 :         appendStringLiteralConn(buf, pos, conn);
     936             : 
     937          36 :     appendPQExpBufferStr(buf, ";\n");
     938             : 
     939          36 :     pg_free(mine);
     940             : }

Generated by: LCOV version 1.14