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

Generated by: LCOV version 1.16