LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.5 % 361 298
Test Date: 2026-03-03 14:15:12 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       116260 : sanitize_line(const char *str, bool want_hyphen)
      53              : {
      54              :     char       *result;
      55              :     char       *s;
      56              : 
      57       116260 :     if (!str)
      58         4638 :         return pg_strdup(want_hyphen ? "-" : "");
      59              : 
      60       111622 :     result = pg_strdup(str);
      61              : 
      62      1428410 :     for (s = result; *s != '\0'; s++)
      63              :     {
      64      1316788 :         if (*s == '\n' || *s == '\r')
      65          190 :             *s = ' ';
      66              :     }
      67              : 
      68       111622 :     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        39207 : 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        39207 :     bool        ok = true;
     110        39207 :     char      **aclitems = NULL;
     111        39207 :     char      **baseitems = NULL;
     112        39207 :     char      **grantitems = NULL;
     113        39207 :     char      **revokeitems = NULL;
     114        39207 :     int         naclitems = 0;
     115        39207 :     int         nbaseitems = 0;
     116        39207 :     int         ngrantitems = 0;
     117        39207 :     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        39207 :     if (acls == NULL || *acls == '\0')
     132          178 :         return true;            /* object has default permissions */
     133              : 
     134              :     /* treat empty-string owner same as NULL */
     135        39029 :     if (owner && *owner == '\0')
     136            0 :         owner = NULL;
     137              : 
     138              :     /* Parse the acls array */
     139        39029 :     if (!parsePGArray(acls, &aclitems, &naclitems))
     140              :     {
     141            0 :         free(aclitems);
     142            0 :         return false;
     143              :     }
     144              : 
     145              :     /* Parse the baseacls too */
     146        39029 :     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        39029 :     grantitems = pg_malloc_array(char *, naclitems);
     164       108835 :     for (i = 0; i < naclitems; i++)
     165              :     {
     166        69806 :         bool        found = false;
     167              : 
     168       101103 :         for (int j = 0; j < nbaseitems; j++)
     169              :         {
     170        99333 :             if (strcmp(aclitems[i], baseitems[j]) == 0)
     171              :             {
     172        68036 :                 found = true;
     173        68036 :                 break;
     174              :             }
     175              :         }
     176        69806 :         if (!found)
     177         1770 :             grantitems[ngrantitems++] = aclitems[i];
     178              :     }
     179        39029 :     revokeitems = pg_malloc_array(char *, nbaseitems);
     180       107428 :     for (i = 0; i < nbaseitems; i++)
     181              :     {
     182        68399 :         bool        found = false;
     183              : 
     184        99011 :         for (int j = 0; j < naclitems; j++)
     185              :         {
     186        98648 :             if (strcmp(baseitems[i], aclitems[j]) == 0)
     187              :             {
     188        68036 :                 found = true;
     189        68036 :                 break;
     190              :             }
     191              :         }
     192        68399 :         if (!found)
     193          363 :             revokeitems[nrevokeitems++] = baseitems[i];
     194              :     }
     195              : 
     196              :     /* Prepare working buffers */
     197        39029 :     grantee = createPQExpBuffer();
     198        39029 :     grantor = createPQExpBuffer();
     199        39029 :     privs = createPQExpBuffer();
     200        39029 :     privswgo = createPQExpBuffer();
     201              : 
     202              :     /*
     203              :      * At the end, these two will be pasted together to form the result.
     204              :      */
     205        39029 :     firstsql = createPQExpBuffer();
     206        39029 :     secondsql = createPQExpBuffer();
     207              : 
     208              :     /*
     209              :      * Build REVOKE statements for ACLs listed in revokeitems[].
     210              :      */
     211        39392 :     for (i = 0; i < nrevokeitems; i++)
     212              :     {
     213          363 :         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          363 :         if (privs->len > 0)
     222              :         {
     223          363 :             appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
     224              :                               prefix, privs->data, type);
     225          363 :             if (nspname && *nspname)
     226          200 :                 appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     227          363 :             if (name && *name)
     228          293 :                 appendPQExpBuffer(firstsql, "%s ", name);
     229          363 :             appendPQExpBufferStr(firstsql, "FROM ");
     230          363 :             if (grantee->len == 0)
     231          189 :                 appendPQExpBufferStr(firstsql, "PUBLIC;\n");
     232              :             else
     233          174 :                 appendPQExpBuffer(firstsql, "%s;\n",
     234          174 :                                   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        40799 :     for (i = 0; i < ngrantitems; i++)
     262              :     {
     263         1770 :         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         1770 :             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         1770 :                 if (grantor->len == 0 && owner)
     277            0 :                     printfPQExpBuffer(grantor, "%s", owner);
     278              : 
     279              :                 /* Make sure owner's own grants are output before others */
     280         1770 :                 if (owner &&
     281         1770 :                     strcmp(grantee->data, owner) == 0 &&
     282          118 :                     strcmp(grantor->data, owner) == 0)
     283          118 :                     thissql = firstsql;
     284              :                 else
     285         1652 :                     thissql = secondsql;
     286              : 
     287         1770 :                 if (grantor->len > 0
     288         1770 :                     && (!owner || strcmp(owner, grantor->data) != 0))
     289            0 :                     appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
     290            0 :                                       fmtId(grantor->data));
     291              : 
     292         1770 :                 if (privs->len > 0)
     293              :                 {
     294         1768 :                     appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     295              :                                       prefix, privs->data, type);
     296         1768 :                     if (nspname && *nspname)
     297         1496 :                         appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     298         1768 :                     if (name && *name)
     299         1676 :                         appendPQExpBuffer(thissql, "%s ", name);
     300         1768 :                     appendPQExpBufferStr(thissql, "TO ");
     301         1768 :                     if (grantee->len == 0)
     302          999 :                         appendPQExpBufferStr(thissql, "PUBLIC;\n");
     303              :                     else
     304          769 :                         appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
     305              :                 }
     306         1770 :                 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         1770 :                 if (grantor->len > 0
     323         1770 :                     && (!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        39029 :     destroyPQExpBuffer(grantee);
     336        39029 :     destroyPQExpBuffer(grantor);
     337        39029 :     destroyPQExpBuffer(privs);
     338        39029 :     destroyPQExpBuffer(privswgo);
     339              : 
     340        39029 :     appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
     341        39029 :     destroyPQExpBuffer(firstsql);
     342        39029 :     destroyPQExpBuffer(secondsql);
     343              : 
     344        39029 :     free(aclitems);
     345        39029 :     free(baseitems);
     346        39029 :     free(grantitems);
     347        39029 :     free(revokeitems);
     348              : 
     349        39029 :     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         2133 : 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         2133 :     bool        all_with_go = true;
     430         2133 :     bool        all_without_go = true;
     431              :     char       *eqpos;
     432              :     char       *slpos;
     433              :     char       *pos;
     434              : 
     435         2133 :     buf = pg_strdup(item);
     436              : 
     437              :     /* user or group name is string up to = */
     438         2133 :     eqpos = dequoteAclUserName(grantee, buf);
     439         2133 :     if (*eqpos != '=')
     440              :     {
     441            0 :         pg_free(buf);
     442            0 :         return false;
     443              :     }
     444              : 
     445              :     /* grantor should appear after / */
     446         2133 :     slpos = strchr(eqpos + 1, '/');
     447         2133 :     if (slpos)
     448              :     {
     449         2133 :         *slpos++ = '\0';
     450         2133 :         slpos = dequoteAclUserName(grantor, slpos);
     451         2133 :         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         2133 :     resetPQExpBuffer(privs);
     484         2133 :     resetPQExpBuffer(privswgo);
     485              : 
     486         2133 :     if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
     487          716 :         strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
     488              :     {
     489         1508 :         CONVERT_PRIV('r', "SELECT");
     490              : 
     491         1508 :         if (strcmp(type, "SEQUENCE") == 0 ||
     492         1445 :             strcmp(type, "SEQUENCES") == 0)
     493              :             /* sequence only */
     494          125 :             CONVERT_PRIV('U', "USAGE");
     495              :         else
     496              :         {
     497              :             /* table only */
     498         1445 :             CONVERT_PRIV('a', "INSERT");
     499         1445 :             CONVERT_PRIV('x', "REFERENCES");
     500              :             /* rest are not applicable to columns */
     501         1445 :             if (subname == NULL)
     502              :             {
     503          397 :                 CONVERT_PRIV('d', "DELETE");
     504          397 :                 CONVERT_PRIV('t', "TRIGGER");
     505          397 :                 CONVERT_PRIV('D', "TRUNCATE");
     506          397 :                 CONVERT_PRIV('m', "MAINTAIN");
     507              :             }
     508              :         }
     509              : 
     510              :         /* UPDATE */
     511         1629 :         CONVERT_PRIV('w', "UPDATE");
     512              :     }
     513          625 :     else if (strcmp(type, "FUNCTION") == 0 ||
     514          470 :              strcmp(type, "FUNCTIONS") == 0)
     515          432 :         CONVERT_PRIV('X', "EXECUTE");
     516          409 :     else if (strcmp(type, "PROCEDURE") == 0 ||
     517          409 :              strcmp(type, "PROCEDURES") == 0)
     518            0 :         CONVERT_PRIV('X', "EXECUTE");
     519          409 :     else if (strcmp(type, "LANGUAGE") == 0)
     520           37 :         CONVERT_PRIV('U', "USAGE");
     521          372 :     else if (strcmp(type, "SCHEMA") == 0 ||
     522          282 :              strcmp(type, "SCHEMAS") == 0)
     523              :     {
     524           90 :         CONVERT_PRIV('C', "CREATE");
     525          180 :         CONVERT_PRIV('U', "USAGE");
     526              :     }
     527          282 :     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          230 :     else if (strcmp(type, "TABLESPACE") == 0)
     534            0 :         CONVERT_PRIV('C', "CREATE");
     535          230 :     else if (strcmp(type, "TYPE") == 0 ||
     536          105 :              strcmp(type, "TYPES") == 0)
     537          270 :         CONVERT_PRIV('U', "USAGE");
     538           95 :     else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
     539           30 :         CONVERT_PRIV('U', "USAGE");
     540           65 :     else if (strcmp(type, "FOREIGN SERVER") == 0)
     541           30 :         CONVERT_PRIV('U', "USAGE");
     542           35 :     else if (strcmp(type, "FOREIGN TABLE") == 0)
     543            0 :         CONVERT_PRIV('r', "SELECT");
     544           35 :     else if (strcmp(type, "PARAMETER") == 0)
     545              :     {
     546            3 :         CONVERT_PRIV('s', "SET");
     547            3 :         CONVERT_PRIV('A', "ALTER SYSTEM");
     548              :     }
     549           32 :     else if (strcmp(type, "LARGE OBJECT") == 0 ||
     550            0 :              strcmp(type, "LARGE OBJECTS") == 0)
     551              :     {
     552           32 :         CONVERT_PRIV('r', "SELECT");
     553           64 :         CONVERT_PRIV('w', "UPDATE");
     554              :     }
     555              :     else
     556            0 :         abort();
     557              : 
     558              : #undef CONVERT_PRIV
     559              : 
     560         2133 :     if (all_with_go)
     561              :     {
     562            2 :         resetPQExpBuffer(privs);
     563            2 :         printfPQExpBuffer(privswgo, "ALL");
     564            2 :         if (subname)
     565            0 :             appendPQExpBuffer(privswgo, "(%s)", subname);
     566              :     }
     567         2131 :     else if (all_without_go)
     568              :     {
     569          591 :         resetPQExpBuffer(privswgo);
     570          591 :         printfPQExpBuffer(privs, "ALL");
     571          591 :         if (subname)
     572            0 :             appendPQExpBuffer(privs, "(%s)", subname);
     573              :     }
     574              : 
     575         2133 :     pg_free(buf);
     576              : 
     577         2133 :     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          738 : quoteAclUserName(PQExpBuffer output, const char *input)
     586              : {
     587              :     const char *src;
     588          738 :     bool        safe = true;
     589              : 
     590        12834 :     for (src = input; *src; src++)
     591              :     {
     592              :         /* This test had better match what putid() does */
     593        12246 :         if (!isalnum((unsigned char) *src) && *src != '_')
     594              :         {
     595          150 :             safe = false;
     596          150 :             break;
     597              :         }
     598              :     }
     599          738 :     if (!safe)
     600          150 :         appendPQExpBufferChar(output, '"');
     601        14184 :     for (src = input; *src; src++)
     602              :     {
     603              :         /* A double quote character in a username is encoded as "" */
     604        13446 :         if (*src == '"')
     605          150 :             appendPQExpBufferChar(output, '"');
     606        13446 :         appendPQExpBufferChar(output, *src);
     607              :     }
     608          738 :     if (!safe)
     609          150 :         appendPQExpBufferChar(output, '"');
     610          738 : }
     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         4266 : dequoteAclUserName(PQExpBuffer output, char *input)
     620              : {
     621         4266 :     resetPQExpBuffer(output);
     622              : 
     623        40830 :     while (*input && *input != '=')
     624              :     {
     625              :         /*
     626              :          * If user name isn't quoted, then just add it to the output buffer
     627              :          */
     628        36564 :         if (*input != '"')
     629        36473 :             appendPQExpBufferChar(output, *input++);
     630              :         else
     631              :         {
     632              :             /* Otherwise, it's a quoted username */
     633           91 :             input++;
     634              :             /* Loop until we come across an unescaped quote */
     635         2184 :             while (!(*input == '"' && *(input + 1) != '"'))
     636              :             {
     637         2093 :                 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         2093 :                 if (*input == '"' && *(input + 1) == '"')
     645           91 :                     input++;
     646         2093 :                 appendPQExpBufferChar(output, *input++);
     647              :             }
     648           91 :             input++;
     649              :         }
     650              :     }
     651         4266 :     return input;
     652              : }
     653              : 
     654              : /*
     655              :  * Append a privilege keyword to a keyword list, inserting comma if needed.
     656              :  */
     657              : static void
     658         2836 : AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
     659              : {
     660         2836 :     if (aclbuf->len > 0)
     661          685 :         appendPQExpBufferChar(aclbuf, ',');
     662         2836 :     appendPQExpBufferStr(aclbuf, keyword);
     663         2836 :     if (subname)
     664         1048 :         appendPQExpBuffer(aclbuf, "(%s)", subname);
     665         2836 : }
     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          373 : buildShSecLabelQuery(const char *catalog_name, Oid objectId,
     679              :                      PQExpBuffer sql)
     680              : {
     681          373 :     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          373 : }
     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          373 : emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
     697              :                 const char *objtype, const char *objname)
     698              : {
     699              :     int         i;
     700              : 
     701          373 :     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          373 : }
     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_parameters.dat.
     728              :  */
     729              : bool
     730           70 : variable_is_guc_list_quote(const char *name)
     731              : {
     732          135 :     if (pg_strcasecmp(name, "local_preload_libraries") == 0 ||
     733          130 :         pg_strcasecmp(name, "oauth_validator_libraries") == 0 ||
     734          125 :         pg_strcasecmp(name, "search_path") == 0 ||
     735          120 :         pg_strcasecmp(name, "session_preload_libraries") == 0 ||
     736          120 :         pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
     737          115 :         pg_strcasecmp(name, "temp_tablespaces") == 0 ||
     738           55 :         pg_strcasecmp(name, "unix_socket_directories") == 0)
     739           15 :         return true;
     740              :     else
     741           55 :         return false;
     742              : }
     743              : 
     744              : /*
     745              :  * SplitGUCList --- parse a string containing identifiers or file names
     746              :  *
     747              :  * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
     748              :  * presuming whether the elements will be taken as identifiers or file names.
     749              :  * See comparable code in src/backend/utils/adt/varlena.c.
     750              :  *
     751              :  * Inputs:
     752              :  *  rawstring: the input string; must be overwritable!  On return, it's
     753              :  *             been modified to contain the separated identifiers.
     754              :  *  separator: the separator punctuation expected between identifiers
     755              :  *             (typically '.' or ',').  Whitespace may also appear around
     756              :  *             identifiers.
     757              :  * Outputs:
     758              :  *  namelist: receives a malloc'd, null-terminated array of pointers to
     759              :  *            identifiers within rawstring.  Caller should free this
     760              :  *            even on error return.
     761              :  *
     762              :  * Returns true if okay, false if there is a syntax error in the string.
     763              :  */
     764              : bool
     765           15 : SplitGUCList(char *rawstring, char separator,
     766              :              char ***namelist)
     767              : {
     768           15 :     char       *nextp = rawstring;
     769           15 :     bool        done = false;
     770              :     char      **nextptr;
     771              : 
     772              :     /*
     773              :      * Since we disallow empty identifiers, this is a conservative
     774              :      * overestimate of the number of pointers we could need.  Allow one for
     775              :      * list terminator.
     776              :      */
     777           15 :     *namelist = nextptr =
     778           15 :         pg_malloc_array(char *, (strlen(rawstring) / 2 + 2));
     779           15 :     *nextptr = NULL;
     780              : 
     781           15 :     while (isspace((unsigned char) *nextp))
     782            0 :         nextp++;                /* skip leading whitespace */
     783              : 
     784           15 :     if (*nextp == '\0')
     785            5 :         return true;            /* empty string represents empty list */
     786              : 
     787              :     /* At the top of the loop, we are at start of a new identifier. */
     788              :     do
     789              :     {
     790              :         char       *curname;
     791              :         char       *endp;
     792              : 
     793           25 :         if (*nextp == '"')
     794              :         {
     795              :             /* Quoted name --- collapse quote-quote pairs */
     796           20 :             curname = nextp + 1;
     797              :             for (;;)
     798              :             {
     799           30 :                 endp = strchr(nextp + 1, '"');
     800           25 :                 if (endp == NULL)
     801            0 :                     return false;   /* mismatched quotes */
     802           25 :                 if (endp[1] != '"')
     803           20 :                     break;      /* found end of quoted name */
     804              :                 /* Collapse adjacent quotes into one quote, and look again */
     805            5 :                 memmove(endp, endp + 1, strlen(endp));
     806            5 :                 nextp = endp;
     807              :             }
     808              :             /* endp now points at the terminating quote */
     809           20 :             nextp = endp + 1;
     810              :         }
     811              :         else
     812              :         {
     813              :             /* Unquoted name --- extends to separator or whitespace */
     814            5 :             curname = nextp;
     815           55 :             while (*nextp && *nextp != separator &&
     816           50 :                    !isspace((unsigned char) *nextp))
     817           50 :                 nextp++;
     818            5 :             endp = nextp;
     819            5 :             if (curname == nextp)
     820            0 :                 return false;   /* empty unquoted name not allowed */
     821              :         }
     822              : 
     823           25 :         while (isspace((unsigned char) *nextp))
     824            0 :             nextp++;            /* skip trailing whitespace */
     825              : 
     826           25 :         if (*nextp == separator)
     827              :         {
     828           15 :             nextp++;
     829           30 :             while (isspace((unsigned char) *nextp))
     830           15 :                 nextp++;        /* skip leading whitespace for next */
     831              :             /* we expect another name, so done remains false */
     832              :         }
     833           10 :         else if (*nextp == '\0')
     834           10 :             done = true;
     835              :         else
     836            0 :             return false;       /* invalid syntax */
     837              : 
     838              :         /* Now safe to overwrite separator with a null */
     839           25 :         *endp = '\0';
     840              : 
     841              :         /*
     842              :          * Finished isolating current name --- add it to output array
     843              :          */
     844           25 :         *nextptr++ = curname;
     845              : 
     846              :         /* Loop back if we didn't reach end of string */
     847           25 :     } while (!done);
     848              : 
     849           10 :     *nextptr = NULL;
     850           10 :     return true;
     851              : }
     852              : 
     853              : /*
     854              :  * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
     855              :  *
     856              :  * Parse the contents of configitem (a "name=value" string), wrap it in
     857              :  * a complete ALTER command, and append it to buf.
     858              :  *
     859              :  * type is DATABASE or ROLE, and name is the name of the database or role.
     860              :  * If we need an "IN" clause, type2 and name2 similarly define what to put
     861              :  * there; otherwise they should be NULL.
     862              :  * conn is used only to determine string-literal quoting conventions.
     863              :  */
     864              : void
     865           30 : makeAlterConfigCommand(PGconn *conn, const char *configitem,
     866              :                        const char *type, const char *name,
     867              :                        const char *type2, const char *name2,
     868              :                        PQExpBuffer buf)
     869              : {
     870              :     char       *mine;
     871              :     char       *pos;
     872              : 
     873              :     /* Parse the configitem.  If we can't find an "=", silently do nothing. */
     874           30 :     mine = pg_strdup(configitem);
     875           30 :     pos = strchr(mine, '=');
     876           30 :     if (pos == NULL)
     877              :     {
     878            0 :         pg_free(mine);
     879            0 :         return;
     880              :     }
     881           30 :     *pos++ = '\0';
     882              : 
     883              :     /* Build the command, with suitable quoting for everything. */
     884           30 :     appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
     885           30 :     if (type2 != NULL && name2 != NULL)
     886            0 :         appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
     887           30 :     appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
     888              : 
     889              :     /*
     890              :      * Variables that are marked GUC_LIST_QUOTE were already fully quoted by
     891              :      * flatten_set_variable_args() before they were put into the setconfig
     892              :      * array.  However, because the quoting rules used there aren't exactly
     893              :      * like SQL's, we have to break the list value apart and then quote the
     894              :      * elements as string literals.  (The elements may be double-quoted as-is,
     895              :      * but we can't just feed them to the SQL parser; it would do the wrong
     896              :      * thing with elements that are zero-length or longer than NAMEDATALEN.)
     897              :      * Also, we need a special case for empty lists.
     898              :      *
     899              :      * Variables that are not so marked should just be emitted as simple
     900              :      * string literals.  If the variable is not known to
     901              :      * variable_is_guc_list_quote(), we'll do that; this makes it unsafe to
     902              :      * use GUC_LIST_QUOTE for extension variables.
     903              :      */
     904           30 :     if (variable_is_guc_list_quote(mine))
     905              :     {
     906              :         char      **namelist;
     907              :         char      **nameptr;
     908              : 
     909              :         /* Parse string into list of identifiers */
     910              :         /* this shouldn't fail really */
     911            0 :         if (SplitGUCList(pos, ',', &namelist))
     912              :         {
     913              :             /* Special case: represent an empty list as NULL */
     914            0 :             if (*namelist == NULL)
     915            0 :                 appendPQExpBufferStr(buf, "NULL");
     916            0 :             for (nameptr = namelist; *nameptr; nameptr++)
     917              :             {
     918            0 :                 if (nameptr != namelist)
     919            0 :                     appendPQExpBufferStr(buf, ", ");
     920            0 :                 appendStringLiteralConn(buf, *nameptr, conn);
     921              :             }
     922              :         }
     923            0 :         pg_free(namelist);
     924              :     }
     925              :     else
     926           30 :         appendStringLiteralConn(buf, pos, conn);
     927              : 
     928           30 :     appendPQExpBufferStr(buf, ";\n");
     929              : 
     930           30 :     pg_free(mine);
     931              : }
     932              : 
     933              : /*
     934              :  * create_or_open_dir
     935              :  *
     936              :  * This will create a new directory with the given dirname. If there is
     937              :  * already an empty directory with that name, then use it.
     938              :  */
     939              : void
     940           66 : create_or_open_dir(const char *dirname)
     941              : {
     942              :     int         ret;
     943              : 
     944           66 :     switch ((ret = pg_check_dir(dirname)))
     945              :     {
     946            0 :         case -1:
     947              :             /* opendir failed but not with ENOENT */
     948            0 :             pg_fatal("could not open directory \"%s\": %m", dirname);
     949              :             break;
     950           66 :         case 0:
     951              :             /* directory does not exist */
     952           66 :             if (mkdir(dirname, pg_dir_create_mode) < 0)
     953            0 :                 pg_fatal("could not create directory \"%s\": %m", dirname);
     954           66 :             break;
     955            0 :         case 1:
     956              :             /* exists and is empty, fix perms */
     957            0 :             if (chmod(dirname, pg_dir_create_mode) != 0)
     958            0 :                 pg_fatal("could not change permissions of directory \"%s\": %m",
     959              :                          dirname);
     960            0 :             break;
     961            0 :         default:
     962              :             /* exists and is not empty */
     963            0 :             pg_fatal("directory \"%s\" is not empty", dirname);
     964              :     }
     965           66 : }
     966              : 
     967              : /*
     968              :  * Generates a valid restrict key (i.e., an alphanumeric string) for use with
     969              :  * psql's \restrict and \unrestrict meta-commands.  For safety, the value is
     970              :  * chosen at random.
     971              :  */
     972              : char *
     973          219 : generate_restrict_key(void)
     974              : {
     975              :     uint8       buf[64];
     976          219 :     char       *ret = palloc(sizeof(buf));
     977              : 
     978          219 :     if (!pg_strong_random(buf, sizeof(buf)))
     979            0 :         return NULL;
     980              : 
     981        14016 :     for (int i = 0; i < sizeof(buf) - 1; i++)
     982              :     {
     983        13797 :         uint8       idx = buf[i] % strlen(restrict_chars);
     984              : 
     985        13797 :         ret[i] = restrict_chars[idx];
     986              :     }
     987          219 :     ret[sizeof(buf) - 1] = '\0';
     988              : 
     989          219 :     return ret;
     990              : }
     991              : 
     992              : /*
     993              :  * Checks that a given restrict key (intended for use with psql's \restrict and
     994              :  * \unrestrict meta-commands) contains only alphanumeric characters.
     995              :  */
     996              : bool
     997          251 : valid_restrict_key(const char *restrict_key)
     998              : {
     999          251 :     return restrict_key != NULL &&
    1000          502 :         restrict_key[0] != '\0' &&
    1001          251 :         strspn(restrict_key, restrict_chars) == strlen(restrict_key);
    1002              : }
        

Generated by: LCOV version 2.0-1