LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.7 % 364 301
Test Date: 2026-03-23 19:15:56 Functions: 100.0 % 15 15
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-2026, 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       119337 : sanitize_line(const char *str, bool want_hyphen)
      53              : {
      54              :     char       *result;
      55              :     char       *s;
      56              : 
      57       119337 :     if (!str)
      58         4786 :         return pg_strdup(want_hyphen ? "-" : "");
      59              : 
      60       114551 :     result = pg_strdup(str);
      61              : 
      62      1467393 :     for (s = result; *s != '\0'; s++)
      63              :     {
      64      1352842 :         if (*s == '\n' || *s == '\r')
      65          190 :             *s = ' ';
      66              :     }
      67              : 
      68       114551 :     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        42824 : 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        42824 :     bool        ok = true;
     110        42824 :     char      **aclitems = NULL;
     111        42824 :     char      **baseitems = NULL;
     112        42824 :     char      **grantitems = NULL;
     113        42824 :     char      **revokeitems = NULL;
     114        42824 :     int         naclitems = 0;
     115        42824 :     int         nbaseitems = 0;
     116        42824 :     int         ngrantitems = 0;
     117        42824 :     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        42824 :     if (acls == NULL || *acls == '\0')
     132          187 :         return true;            /* object has default permissions */
     133              : 
     134              :     /* treat empty-string owner same as NULL */
     135        42637 :     if (owner && *owner == '\0')
     136            0 :         owner = NULL;
     137              : 
     138              :     /* Parse the acls array */
     139        42637 :     if (!parsePGArray(acls, &aclitems, &naclitems))
     140              :     {
     141            0 :         free(aclitems);
     142            0 :         return false;
     143              :     }
     144              : 
     145              :     /* Parse the baseacls too */
     146        42637 :     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        42637 :     grantitems = pg_malloc_array(char *, naclitems);
     164       119113 :     for (i = 0; i < naclitems; i++)
     165              :     {
     166        76476 :         bool        found = false;
     167              : 
     168       110877 :         for (int j = 0; j < nbaseitems; j++)
     169              :         {
     170       108981 :             if (strcmp(aclitems[i], baseitems[j]) == 0)
     171              :             {
     172        74580 :                 found = true;
     173        74580 :                 break;
     174              :             }
     175              :         }
     176        76476 :         if (!found)
     177         1896 :             grantitems[ngrantitems++] = aclitems[i];
     178              :     }
     179        42637 :     revokeitems = pg_malloc_array(char *, nbaseitems);
     180       117618 :     for (i = 0; i < nbaseitems; i++)
     181              :     {
     182        74981 :         bool        found = false;
     183              : 
     184       108643 :         for (int j = 0; j < naclitems; j++)
     185              :         {
     186       108242 :             if (strcmp(baseitems[i], aclitems[j]) == 0)
     187              :             {
     188        74580 :                 found = true;
     189        74580 :                 break;
     190              :             }
     191              :         }
     192        74981 :         if (!found)
     193          401 :             revokeitems[nrevokeitems++] = baseitems[i];
     194              :     }
     195              : 
     196              :     /* Prepare working buffers */
     197        42637 :     grantee = createPQExpBuffer();
     198        42637 :     grantor = createPQExpBuffer();
     199        42637 :     privs = createPQExpBuffer();
     200        42637 :     privswgo = createPQExpBuffer();
     201              : 
     202              :     /*
     203              :      * At the end, these two will be pasted together to form the result.
     204              :      */
     205        42637 :     firstsql = createPQExpBuffer();
     206        42637 :     secondsql = createPQExpBuffer();
     207              : 
     208              :     /*
     209              :      * Build REVOKE statements for ACLs listed in revokeitems[].
     210              :      */
     211        43038 :     for (i = 0; i < nrevokeitems; i++)
     212              :     {
     213          401 :         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          401 :         if (privs->len > 0)
     222              :         {
     223          401 :             appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
     224              :                               prefix, privs->data, type);
     225          401 :             if (nspname && *nspname)
     226          236 :                 appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     227          401 :             if (name && *name)
     228          331 :                 appendPQExpBuffer(firstsql, "%s ", name);
     229          401 :             appendPQExpBufferStr(firstsql, "FROM ");
     230          401 :             if (grantee->len == 0)
     231          191 :                 appendPQExpBufferStr(firstsql, "PUBLIC;\n");
     232              :             else
     233          210 :                 appendPQExpBuffer(firstsql, "%s;\n",
     234          210 :                                   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        44533 :     for (i = 0; i < ngrantitems; i++)
     262              :     {
     263         1896 :         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         1896 :             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         1896 :                 if (grantor->len == 0 && owner)
     277            0 :                     printfPQExpBuffer(grantor, "%s", owner);
     278              : 
     279              :                 /* Make sure owner's own grants are output before others */
     280         1896 :                 if (owner &&
     281         1896 :                     strcmp(grantee->data, owner) == 0 &&
     282          154 :                     strcmp(grantor->data, owner) == 0)
     283          154 :                     thissql = firstsql;
     284              :                 else
     285         1742 :                     thissql = secondsql;
     286              : 
     287         1896 :                 if (grantor->len > 0
     288         1896 :                     && (!owner || strcmp(owner, grantor->data) != 0))
     289            0 :                     appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
     290            0 :                                       fmtId(grantor->data));
     291              : 
     292         1896 :                 if (privs->len > 0)
     293              :                 {
     294         1894 :                     appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     295              :                                       prefix, privs->data, type);
     296         1894 :                     if (nspname && *nspname)
     297         1598 :                         appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     298         1894 :                     if (name && *name)
     299         1802 :                         appendPQExpBuffer(thissql, "%s ", name);
     300         1894 :                     appendPQExpBufferStr(thissql, "TO ");
     301         1894 :                     if (grantee->len == 0)
     302         1051 :                         appendPQExpBufferStr(thissql, "PUBLIC;\n");
     303              :                     else
     304          843 :                         appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
     305              :                 }
     306         1896 :                 if (privswgo->len > 0)
     307              :                 {
     308           20 :                     appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     309              :                                       prefix, privswgo->data, type);
     310           20 :                     if (nspname && *nspname)
     311           19 :                         appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     312           20 :                     if (name && *name)
     313           20 :                         appendPQExpBuffer(thissql, "%s ", name);
     314           20 :                     appendPQExpBufferStr(thissql, "TO ");
     315           20 :                     if (grantee->len == 0)
     316            0 :                         appendPQExpBufferStr(thissql, "PUBLIC");
     317              :                     else
     318           20 :                         appendPQExpBufferStr(thissql, fmtId(grantee->data));
     319           20 :                     appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
     320              :                 }
     321              : 
     322         1896 :                 if (grantor->len > 0
     323         1896 :                     && (!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        42637 :     destroyPQExpBuffer(grantee);
     336        42637 :     destroyPQExpBuffer(grantor);
     337        42637 :     destroyPQExpBuffer(privs);
     338        42637 :     destroyPQExpBuffer(privswgo);
     339              : 
     340        42637 :     appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
     341        42637 :     destroyPQExpBuffer(firstsql);
     342        42637 :     destroyPQExpBuffer(secondsql);
     343              : 
     344        42637 :     free(aclitems);
     345        42637 :     free(baseitems);
     346        42637 :     free(grantitems);
     347        42637 :     free(revokeitems);
     348              : 
     349        42637 :     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          132 : 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          132 :     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          132 :     appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
     383              :                       fmtId(owner));
     384          132 :     if (nspname)
     385           62 :         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          132 :     if (!buildACLCommands("", NULL, NULL, type,
     392              :                           acls, acldefault, owner,
     393          132 :                           prefix->data, remoteVersion, sql))
     394              :     {
     395            0 :         destroyPQExpBuffer(prefix);
     396            0 :         return false;
     397              :     }
     398              : 
     399          132 :     destroyPQExpBuffer(prefix);
     400              : 
     401          132 :     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         2297 : 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         2297 :     bool        all_with_go = true;
     430         2297 :     bool        all_without_go = true;
     431              :     char       *eqpos;
     432              :     char       *slpos;
     433              :     char       *pos;
     434              : 
     435         2297 :     buf = pg_strdup(item);
     436              : 
     437              :     /* user or group name is string up to = */
     438         2297 :     eqpos = dequoteAclUserName(grantee, buf);
     439         2297 :     if (*eqpos != '=')
     440              :     {
     441            0 :         pg_free(buf);
     442            0 :         return false;
     443              :     }
     444              : 
     445              :     /* grantor should appear after / */
     446         2297 :     slpos = strchr(eqpos + 1, '/');
     447         2297 :     if (slpos)
     448              :     {
     449         2297 :         *slpos++ = '\0';
     450         2297 :         slpos = dequoteAclUserName(grantor, slpos);
     451         2297 :         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         2297 :     resetPQExpBuffer(privs);
     484         2297 :     resetPQExpBuffer(privswgo);
     485              : 
     486         2297 :     if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
     487          851 :         strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
     488              :     {
     489         1537 :         CONVERT_PRIV('r', "SELECT");
     490              : 
     491         1537 :         if (strcmp(type, "SEQUENCE") == 0 ||
     492         1473 :             strcmp(type, "SEQUENCES") == 0)
     493              :             /* sequence only */
     494          127 :             CONVERT_PRIV('U', "USAGE");
     495              :         else
     496              :         {
     497              :             /* table only */
     498         1473 :             CONVERT_PRIV('a', "INSERT");
     499         1473 :             CONVERT_PRIV('x', "REFERENCES");
     500              :             /* rest are not applicable to columns */
     501         1473 :             if (subname == NULL)
     502              :             {
     503          425 :                 CONVERT_PRIV('d', "DELETE");
     504          425 :                 CONVERT_PRIV('t', "TRIGGER");
     505          425 :                 CONVERT_PRIV('D', "TRUNCATE");
     506          425 :                 CONVERT_PRIV('m', "MAINTAIN");
     507              :             }
     508              :         }
     509              : 
     510              :         /* UPDATE */
     511         1660 :         CONVERT_PRIV('w', "UPDATE");
     512              :     }
     513          760 :     else if (strcmp(type, "PROPERTY GRAPH") == 0 ||
     514          652 :              strcmp(type, "PROPERTY GRAPHS") == 0)
     515          216 :         CONVERT_PRIV('r', "SELECT");
     516          652 :     else if (strcmp(type, "FUNCTION") == 0 ||
     517          496 :              strcmp(type, "FUNCTIONS") == 0)
     518          434 :         CONVERT_PRIV('X', "EXECUTE");
     519          435 :     else if (strcmp(type, "PROCEDURE") == 0 ||
     520          435 :              strcmp(type, "PROCEDURES") == 0)
     521            0 :         CONVERT_PRIV('X', "EXECUTE");
     522          435 :     else if (strcmp(type, "LANGUAGE") == 0)
     523           37 :         CONVERT_PRIV('U', "USAGE");
     524          398 :     else if (strcmp(type, "SCHEMA") == 0 ||
     525          287 :              strcmp(type, "SCHEMAS") == 0)
     526              :     {
     527          111 :         CONVERT_PRIV('C', "CREATE");
     528          222 :         CONVERT_PRIV('U', "USAGE");
     529              :     }
     530          287 :     else if (strcmp(type, "DATABASE") == 0)
     531              :     {
     532           57 :         CONVERT_PRIV('C', "CREATE");
     533           57 :         CONVERT_PRIV('c', "CONNECT");
     534           57 :         CONVERT_PRIV('T', "TEMPORARY");
     535              :     }
     536          230 :     else if (strcmp(type, "TABLESPACE") == 0)
     537            0 :         CONVERT_PRIV('C', "CREATE");
     538          230 :     else if (strcmp(type, "TYPE") == 0 ||
     539          105 :              strcmp(type, "TYPES") == 0)
     540          270 :         CONVERT_PRIV('U', "USAGE");
     541           95 :     else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
     542           30 :         CONVERT_PRIV('U', "USAGE");
     543           65 :     else if (strcmp(type, "FOREIGN SERVER") == 0)
     544           30 :         CONVERT_PRIV('U', "USAGE");
     545           35 :     else if (strcmp(type, "FOREIGN TABLE") == 0)
     546            0 :         CONVERT_PRIV('r', "SELECT");
     547           35 :     else if (strcmp(type, "PARAMETER") == 0)
     548              :     {
     549            3 :         CONVERT_PRIV('s', "SET");
     550            3 :         CONVERT_PRIV('A', "ALTER SYSTEM");
     551              :     }
     552           32 :     else if (strcmp(type, "LARGE OBJECT") == 0 ||
     553            0 :              strcmp(type, "LARGE OBJECTS") == 0)
     554              :     {
     555           32 :         CONVERT_PRIV('r', "SELECT");
     556           64 :         CONVERT_PRIV('w', "UPDATE");
     557              :     }
     558              :     else
     559            0 :         abort();
     560              : 
     561              : #undef CONVERT_PRIV
     562              : 
     563         2297 :     if (all_with_go)
     564              :     {
     565            2 :         resetPQExpBuffer(privs);
     566            2 :         printfPQExpBuffer(privswgo, "ALL");
     567            2 :         if (subname)
     568            0 :             appendPQExpBuffer(privswgo, "(%s)", subname);
     569              :     }
     570         2295 :     else if (all_without_go)
     571              :     {
     572          708 :         resetPQExpBuffer(privswgo);
     573          708 :         printfPQExpBuffer(privs, "ALL");
     574          708 :         if (subname)
     575            0 :             appendPQExpBuffer(privs, "(%s)", subname);
     576              :     }
     577              : 
     578         2297 :     pg_free(buf);
     579              : 
     580         2297 :     return true;
     581              : }
     582              : 
     583              : /*
     584              :  * Transfer the role name at *input into the output buffer, adding
     585              :  * quoting according to the same rules as putid() in backend's acl.c.
     586              :  */
     587              : void
     588          768 : quoteAclUserName(PQExpBuffer output, const char *input)
     589              : {
     590              :     const char *src;
     591          768 :     bool        safe = true;
     592              : 
     593        13374 :     for (src = input; *src; src++)
     594              :     {
     595              :         /* This test had better match what putid() does */
     596        12756 :         if (!isalnum((unsigned char) *src) && *src != '_')
     597              :         {
     598          150 :             safe = false;
     599          150 :             break;
     600              :         }
     601              :     }
     602          768 :     if (!safe)
     603          150 :         appendPQExpBufferChar(output, '"');
     604        14724 :     for (src = input; *src; src++)
     605              :     {
     606              :         /* A double quote character in a username is encoded as "" */
     607        13956 :         if (*src == '"')
     608          150 :             appendPQExpBufferChar(output, '"');
     609        13956 :         appendPQExpBufferChar(output, *src);
     610              :     }
     611          768 :     if (!safe)
     612          150 :         appendPQExpBufferChar(output, '"');
     613          768 : }
     614              : 
     615              : /*
     616              :  * Transfer a user or group name starting at *input into the output buffer,
     617              :  * dequoting if needed.  Returns a pointer to just past the input name.
     618              :  * The name is taken to end at an unquoted '=' or end of string.
     619              :  * Note: unlike quoteAclUserName(), this first clears the output buffer.
     620              :  */
     621              : static char *
     622         4594 : dequoteAclUserName(PQExpBuffer output, char *input)
     623              : {
     624         4594 :     resetPQExpBuffer(output);
     625              : 
     626        43770 :     while (*input && *input != '=')
     627              :     {
     628              :         /*
     629              :          * If user name isn't quoted, then just add it to the output buffer
     630              :          */
     631        39176 :         if (*input != '"')
     632        39085 :             appendPQExpBufferChar(output, *input++);
     633              :         else
     634              :         {
     635              :             /* Otherwise, it's a quoted username */
     636           91 :             input++;
     637              :             /* Loop until we come across an unescaped quote */
     638         2184 :             while (!(*input == '"' && *(input + 1) != '"'))
     639              :             {
     640         2093 :                 if (*input == '\0')
     641            0 :                     return input;   /* really a syntax error... */
     642              : 
     643              :                 /*
     644              :                  * Quoting convention is to escape " as "".  Keep this code in
     645              :                  * sync with putid() in backend's acl.c.
     646              :                  */
     647         2093 :                 if (*input == '"' && *(input + 1) == '"')
     648           91 :                     input++;
     649         2093 :                 appendPQExpBufferChar(output, *input++);
     650              :             }
     651           91 :             input++;
     652              :         }
     653              :     }
     654         4594 :     return input;
     655              : }
     656              : 
     657              : /*
     658              :  * Append a privilege keyword to a keyword list, inserting comma if needed.
     659              :  */
     660              : static void
     661         3018 : AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
     662              : {
     663         3018 :     if (aclbuf->len > 0)
     664          703 :         appendPQExpBufferChar(aclbuf, ',');
     665         3018 :     appendPQExpBufferStr(aclbuf, keyword);
     666         3018 :     if (subname)
     667         1048 :         appendPQExpBuffer(aclbuf, "(%s)", subname);
     668         3018 : }
     669              : 
     670              : 
     671              : /*
     672              :  * buildShSecLabelQuery
     673              :  *
     674              :  * Build a query to retrieve security labels for a shared object.
     675              :  * The object is identified by its OID plus the name of the catalog
     676              :  * it can be found in (e.g., "pg_database" for database names).
     677              :  * The query is appended to "sql".  (We don't execute it here so as to
     678              :  * keep this file free of assumptions about how to deal with SQL errors.)
     679              :  */
     680              : void
     681          399 : buildShSecLabelQuery(const char *catalog_name, Oid objectId,
     682              :                      PQExpBuffer sql)
     683              : {
     684          399 :     appendPQExpBuffer(sql,
     685              :                       "SELECT provider, label FROM pg_catalog.pg_shseclabel "
     686              :                       "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
     687              :                       "AND objoid = '%u'", catalog_name, objectId);
     688          399 : }
     689              : 
     690              : /*
     691              :  * emitShSecLabels
     692              :  *
     693              :  * Construct SECURITY LABEL commands using the data retrieved by the query
     694              :  * generated by buildShSecLabelQuery, and append them to "buffer".
     695              :  * Here, the target object is identified by its type name (e.g. "DATABASE")
     696              :  * and its name (not pre-quoted).
     697              :  */
     698              : void
     699          399 : emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
     700              :                 const char *objtype, const char *objname)
     701              : {
     702              :     int         i;
     703              : 
     704          399 :     for (i = 0; i < PQntuples(res); i++)
     705              :     {
     706            0 :         char       *provider = PQgetvalue(res, i, 0);
     707            0 :         char       *label = PQgetvalue(res, i, 1);
     708              : 
     709              :         /* must use fmtId result before calling it again */
     710            0 :         appendPQExpBuffer(buffer,
     711              :                           "SECURITY LABEL FOR %s ON %s",
     712              :                           fmtId(provider), objtype);
     713            0 :         appendPQExpBuffer(buffer,
     714              :                           " %s IS ",
     715              :                           fmtId(objname));
     716            0 :         appendStringLiteralConn(buffer, label, conn);
     717            0 :         appendPQExpBufferStr(buffer, ";\n");
     718              :     }
     719          399 : }
     720              : 
     721              : 
     722              : /*
     723              :  * Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
     724              :  *
     725              :  * It'd be better if we could inquire this directly from the backend; but even
     726              :  * if there were a function for that, it could only tell us about variables
     727              :  * currently known to guc.c, so that it'd be unsafe for extensions to declare
     728              :  * GUC_LIST_QUOTE variables anyway.  Lacking a solution for that, it doesn't
     729              :  * seem worth the work to do more than have this list, which must be kept in
     730              :  * sync with the variables actually marked GUC_LIST_QUOTE in guc_parameters.dat.
     731              :  */
     732              : bool
     733           70 : variable_is_guc_list_quote(const char *name)
     734              : {
     735          135 :     if (pg_strcasecmp(name, "local_preload_libraries") == 0 ||
     736          130 :         pg_strcasecmp(name, "oauth_validator_libraries") == 0 ||
     737          125 :         pg_strcasecmp(name, "search_path") == 0 ||
     738          120 :         pg_strcasecmp(name, "session_preload_libraries") == 0 ||
     739          120 :         pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
     740          115 :         pg_strcasecmp(name, "temp_tablespaces") == 0 ||
     741           55 :         pg_strcasecmp(name, "unix_socket_directories") == 0)
     742           15 :         return true;
     743              :     else
     744           55 :         return false;
     745              : }
     746              : 
     747              : /*
     748              :  * SplitGUCList --- parse a string containing identifiers or file names
     749              :  *
     750              :  * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
     751              :  * presuming whether the elements will be taken as identifiers or file names.
     752              :  * See comparable code in src/backend/utils/adt/varlena.c.
     753              :  *
     754              :  * Inputs:
     755              :  *  rawstring: the input string; must be overwritable!  On return, it's
     756              :  *             been modified to contain the separated identifiers.
     757              :  *  separator: the separator punctuation expected between identifiers
     758              :  *             (typically '.' or ',').  Whitespace may also appear around
     759              :  *             identifiers.
     760              :  * Outputs:
     761              :  *  namelist: receives a malloc'd, null-terminated array of pointers to
     762              :  *            identifiers within rawstring.  Caller should free this
     763              :  *            even on error return.
     764              :  *
     765              :  * Returns true if okay, false if there is a syntax error in the string.
     766              :  */
     767              : bool
     768           15 : SplitGUCList(char *rawstring, char separator,
     769              :              char ***namelist)
     770              : {
     771           15 :     char       *nextp = rawstring;
     772           15 :     bool        done = false;
     773              :     char      **nextptr;
     774              : 
     775              :     /*
     776              :      * Since we disallow empty identifiers, this is a conservative
     777              :      * overestimate of the number of pointers we could need.  Allow one for
     778              :      * list terminator.
     779              :      */
     780           15 :     *namelist = nextptr =
     781           15 :         pg_malloc_array(char *, (strlen(rawstring) / 2 + 2));
     782           15 :     *nextptr = NULL;
     783              : 
     784           15 :     while (isspace((unsigned char) *nextp))
     785            0 :         nextp++;                /* skip leading whitespace */
     786              : 
     787           15 :     if (*nextp == '\0')
     788            5 :         return true;            /* empty string represents empty list */
     789              : 
     790              :     /* At the top of the loop, we are at start of a new identifier. */
     791              :     do
     792              :     {
     793              :         char       *curname;
     794              :         char       *endp;
     795              : 
     796           25 :         if (*nextp == '"')
     797              :         {
     798              :             /* Quoted name --- collapse quote-quote pairs */
     799           20 :             curname = nextp + 1;
     800              :             for (;;)
     801              :             {
     802           30 :                 endp = strchr(nextp + 1, '"');
     803           25 :                 if (endp == NULL)
     804            0 :                     return false;   /* mismatched quotes */
     805           25 :                 if (endp[1] != '"')
     806           20 :                     break;      /* found end of quoted name */
     807              :                 /* Collapse adjacent quotes into one quote, and look again */
     808            5 :                 memmove(endp, endp + 1, strlen(endp));
     809            5 :                 nextp = endp;
     810              :             }
     811              :             /* endp now points at the terminating quote */
     812           20 :             nextp = endp + 1;
     813              :         }
     814              :         else
     815              :         {
     816              :             /* Unquoted name --- extends to separator or whitespace */
     817            5 :             curname = nextp;
     818           55 :             while (*nextp && *nextp != separator &&
     819           50 :                    !isspace((unsigned char) *nextp))
     820           50 :                 nextp++;
     821            5 :             endp = nextp;
     822            5 :             if (curname == nextp)
     823            0 :                 return false;   /* empty unquoted name not allowed */
     824              :         }
     825              : 
     826           25 :         while (isspace((unsigned char) *nextp))
     827            0 :             nextp++;            /* skip trailing whitespace */
     828              : 
     829           25 :         if (*nextp == separator)
     830              :         {
     831           15 :             nextp++;
     832           30 :             while (isspace((unsigned char) *nextp))
     833           15 :                 nextp++;        /* skip leading whitespace for next */
     834              :             /* we expect another name, so done remains false */
     835              :         }
     836           10 :         else if (*nextp == '\0')
     837           10 :             done = true;
     838              :         else
     839            0 :             return false;       /* invalid syntax */
     840              : 
     841              :         /* Now safe to overwrite separator with a null */
     842           25 :         *endp = '\0';
     843              : 
     844              :         /*
     845              :          * Finished isolating current name --- add it to output array
     846              :          */
     847           25 :         *nextptr++ = curname;
     848              : 
     849              :         /* Loop back if we didn't reach end of string */
     850           25 :     } while (!done);
     851              : 
     852           10 :     *nextptr = NULL;
     853           10 :     return true;
     854              : }
     855              : 
     856              : /*
     857              :  * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
     858              :  *
     859              :  * Parse the contents of configitem (a "name=value" string), wrap it in
     860              :  * a complete ALTER command, and append it to buf.
     861              :  *
     862              :  * type is DATABASE or ROLE, and name is the name of the database or role.
     863              :  * If we need an "IN" clause, type2 and name2 similarly define what to put
     864              :  * there; otherwise they should be NULL.
     865              :  * conn is used only to determine string-literal quoting conventions.
     866              :  */
     867              : void
     868           30 : makeAlterConfigCommand(PGconn *conn, const char *configitem,
     869              :                        const char *type, const char *name,
     870              :                        const char *type2, const char *name2,
     871              :                        PQExpBuffer buf)
     872              : {
     873              :     char       *mine;
     874              :     char       *pos;
     875              : 
     876              :     /* Parse the configitem.  If we can't find an "=", silently do nothing. */
     877           30 :     mine = pg_strdup(configitem);
     878           30 :     pos = strchr(mine, '=');
     879           30 :     if (pos == NULL)
     880              :     {
     881            0 :         pg_free(mine);
     882            0 :         return;
     883              :     }
     884           30 :     *pos++ = '\0';
     885              : 
     886              :     /* Build the command, with suitable quoting for everything. */
     887           30 :     appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
     888           30 :     if (type2 != NULL && name2 != NULL)
     889            0 :         appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
     890           30 :     appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
     891              : 
     892              :     /*
     893              :      * Variables that are marked GUC_LIST_QUOTE were already fully quoted by
     894              :      * flatten_set_variable_args() before they were put into the setconfig
     895              :      * array.  However, because the quoting rules used there aren't exactly
     896              :      * like SQL's, we have to break the list value apart and then quote the
     897              :      * elements as string literals.  (The elements may be double-quoted as-is,
     898              :      * but we can't just feed them to the SQL parser; it would do the wrong
     899              :      * thing with elements that are zero-length or longer than NAMEDATALEN.)
     900              :      * Also, we need a special case for empty lists.
     901              :      *
     902              :      * Variables that are not so marked should just be emitted as simple
     903              :      * string literals.  If the variable is not known to
     904              :      * variable_is_guc_list_quote(), we'll do that; this makes it unsafe to
     905              :      * use GUC_LIST_QUOTE for extension variables.
     906              :      */
     907           30 :     if (variable_is_guc_list_quote(mine))
     908              :     {
     909              :         char      **namelist;
     910              :         char      **nameptr;
     911              : 
     912              :         /* Parse string into list of identifiers */
     913              :         /* this shouldn't fail really */
     914            0 :         if (SplitGUCList(pos, ',', &namelist))
     915              :         {
     916              :             /* Special case: represent an empty list as NULL */
     917            0 :             if (*namelist == NULL)
     918            0 :                 appendPQExpBufferStr(buf, "NULL");
     919            0 :             for (nameptr = namelist; *nameptr; nameptr++)
     920              :             {
     921            0 :                 if (nameptr != namelist)
     922            0 :                     appendPQExpBufferStr(buf, ", ");
     923            0 :                 appendStringLiteralConn(buf, *nameptr, conn);
     924              :             }
     925              :         }
     926            0 :         pg_free(namelist);
     927              :     }
     928              :     else
     929           30 :         appendStringLiteralConn(buf, pos, conn);
     930              : 
     931           30 :     appendPQExpBufferStr(buf, ";\n");
     932              : 
     933           30 :     pg_free(mine);
     934              : }
     935              : 
     936              : /*
     937              :  * create_or_open_dir
     938              :  *
     939              :  * This will create a new directory with the given dirname. If there is
     940              :  * already an empty directory with that name, then use it.
     941              :  */
     942              : void
     943           75 : create_or_open_dir(const char *dirname)
     944              : {
     945              :     int         ret;
     946              : 
     947           75 :     switch ((ret = pg_check_dir(dirname)))
     948              :     {
     949            0 :         case -1:
     950              :             /* opendir failed but not with ENOENT */
     951            0 :             pg_fatal("could not open directory \"%s\": %m", dirname);
     952              :             break;
     953           75 :         case 0:
     954              :             /* directory does not exist */
     955           75 :             if (mkdir(dirname, pg_dir_create_mode) < 0)
     956            0 :                 pg_fatal("could not create directory \"%s\": %m", dirname);
     957           75 :             break;
     958            0 :         case 1:
     959              :             /* exists and is empty, fix perms */
     960            0 :             if (chmod(dirname, pg_dir_create_mode) != 0)
     961            0 :                 pg_fatal("could not change permissions of directory \"%s\": %m",
     962              :                          dirname);
     963            0 :             break;
     964            0 :         default:
     965              :             /* exists and is not empty */
     966            0 :             pg_fatal("directory \"%s\" is not empty", dirname);
     967              :     }
     968           75 : }
     969              : 
     970              : /*
     971              :  * Generates a valid restrict key (i.e., an alphanumeric string) for use with
     972              :  * psql's \restrict and \unrestrict meta-commands.  For safety, the value is
     973              :  * chosen at random.
     974              :  */
     975              : char *
     976          223 : generate_restrict_key(void)
     977              : {
     978              :     uint8       buf[64];
     979          223 :     char       *ret = palloc(sizeof(buf));
     980              : 
     981          223 :     if (!pg_strong_random(buf, sizeof(buf)))
     982            0 :         return NULL;
     983              : 
     984        14272 :     for (int i = 0; i < sizeof(buf) - 1; i++)
     985              :     {
     986        14049 :         uint8       idx = buf[i] % strlen(restrict_chars);
     987              : 
     988        14049 :         ret[i] = restrict_chars[idx];
     989              :     }
     990          223 :     ret[sizeof(buf) - 1] = '\0';
     991              : 
     992          223 :     return ret;
     993              : }
     994              : 
     995              : /*
     996              :  * Checks that a given restrict key (intended for use with psql's \restrict and
     997              :  * \unrestrict meta-commands) contains only alphanumeric characters.
     998              :  */
     999              : bool
    1000          255 : valid_restrict_key(const char *restrict_key)
    1001              : {
    1002          255 :     return restrict_key != NULL &&
    1003          510 :         restrict_key[0] != '\0' &&
    1004          255 :         strspn(restrict_key, restrict_chars) == strlen(restrict_key);
    1005              : }
        

Generated by: LCOV version 2.0-1