LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 433 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 15 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * Utility routines for SQL dumping
       4              :  *
       5              :  * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
       6              :  *
       7              :  *
       8              :  * Portions Copyright (c) 1996-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            0 : sanitize_line(const char *str, bool want_hyphen)
      53              : {
      54            0 :         char       *result;
      55            0 :         char       *s;
      56              : 
      57            0 :         if (!str)
      58            0 :                 return pg_strdup(want_hyphen ? "-" : "");
      59              : 
      60            0 :         result = pg_strdup(str);
      61              : 
      62            0 :         for (s = result; *s != '\0'; s++)
      63              :         {
      64            0 :                 if (*s == '\n' || *s == '\r')
      65            0 :                         *s = ' ';
      66            0 :         }
      67              : 
      68            0 :         return result;
      69            0 : }
      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            0 : 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            0 :         bool            ok = true;
     110            0 :         char      **aclitems = NULL;
     111            0 :         char      **baseitems = NULL;
     112            0 :         char      **grantitems = NULL;
     113            0 :         char      **revokeitems = NULL;
     114            0 :         int                     naclitems = 0;
     115            0 :         int                     nbaseitems = 0;
     116            0 :         int                     ngrantitems = 0;
     117            0 :         int                     nrevokeitems = 0;
     118            0 :         int                     i;
     119            0 :         PQExpBuffer grantee,
     120              :                                 grantor,
     121              :                                 privs,
     122              :                                 privswgo;
     123            0 :         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            0 :         if (acls == NULL || *acls == '\0')
     132            0 :                 return true;                    /* object has default permissions */
     133              : 
     134              :         /* treat empty-string owner same as NULL */
     135            0 :         if (owner && *owner == '\0')
     136            0 :                 owner = NULL;
     137              : 
     138              :         /* Parse the acls array */
     139            0 :         if (!parsePGArray(acls, &aclitems, &naclitems))
     140              :         {
     141            0 :                 free(aclitems);
     142            0 :                 return false;
     143              :         }
     144              : 
     145              :         /* Parse the baseacls too */
     146            0 :         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            0 :         grantitems = (char **) pg_malloc(naclitems * sizeof(char *));
     164            0 :         for (i = 0; i < naclitems; i++)
     165              :         {
     166            0 :                 bool            found = false;
     167              : 
     168            0 :                 for (int j = 0; j < nbaseitems; j++)
     169              :                 {
     170            0 :                         if (strcmp(aclitems[i], baseitems[j]) == 0)
     171              :                         {
     172            0 :                                 found = true;
     173            0 :                                 break;
     174              :                         }
     175            0 :                 }
     176            0 :                 if (!found)
     177            0 :                         grantitems[ngrantitems++] = aclitems[i];
     178            0 :         }
     179            0 :         revokeitems = (char **) pg_malloc(nbaseitems * sizeof(char *));
     180            0 :         for (i = 0; i < nbaseitems; i++)
     181              :         {
     182            0 :                 bool            found = false;
     183              : 
     184            0 :                 for (int j = 0; j < naclitems; j++)
     185              :                 {
     186            0 :                         if (strcmp(baseitems[i], aclitems[j]) == 0)
     187              :                         {
     188            0 :                                 found = true;
     189            0 :                                 break;
     190              :                         }
     191            0 :                 }
     192            0 :                 if (!found)
     193            0 :                         revokeitems[nrevokeitems++] = baseitems[i];
     194            0 :         }
     195              : 
     196              :         /* Prepare working buffers */
     197            0 :         grantee = createPQExpBuffer();
     198            0 :         grantor = createPQExpBuffer();
     199            0 :         privs = createPQExpBuffer();
     200            0 :         privswgo = createPQExpBuffer();
     201              : 
     202              :         /*
     203              :          * At the end, these two will be pasted together to form the result.
     204              :          */
     205            0 :         firstsql = createPQExpBuffer();
     206            0 :         secondsql = createPQExpBuffer();
     207              : 
     208              :         /*
     209              :          * Build REVOKE statements for ACLs listed in revokeitems[].
     210              :          */
     211            0 :         for (i = 0; i < nrevokeitems; i++)
     212              :         {
     213            0 :                 if (!parseAclItem(revokeitems[i],
     214            0 :                                                   type, name, subname, remoteVersion,
     215            0 :                                                   grantee, grantor, privs, NULL))
     216              :                 {
     217            0 :                         ok = false;
     218            0 :                         break;
     219              :                 }
     220              : 
     221            0 :                 if (privs->len > 0)
     222              :                 {
     223            0 :                         appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
     224            0 :                                                           prefix, privs->data, type);
     225            0 :                         if (nspname && *nspname)
     226            0 :                                 appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
     227            0 :                         if (name && *name)
     228            0 :                                 appendPQExpBuffer(firstsql, "%s ", name);
     229            0 :                         appendPQExpBufferStr(firstsql, "FROM ");
     230            0 :                         if (grantee->len == 0)
     231            0 :                                 appendPQExpBufferStr(firstsql, "PUBLIC;\n");
     232              :                         else
     233            0 :                                 appendPQExpBuffer(firstsql, "%s;\n",
     234            0 :                                                                   fmtId(grantee->data));
     235            0 :                 }
     236            0 :         }
     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            0 :         for (i = 0; i < ngrantitems; i++)
     262              :         {
     263            0 :                 if (parseAclItem(grantitems[i], type, name, subname, remoteVersion,
     264            0 :                                                  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            0 :                         if (privs->len > 0 || privswgo->len > 0)
     272              :                         {
     273            0 :                                 PQExpBuffer thissql;
     274              : 
     275              :                                 /* Set owner as grantor if that's not explicit in the ACL */
     276            0 :                                 if (grantor->len == 0 && owner)
     277            0 :                                         printfPQExpBuffer(grantor, "%s", owner);
     278              : 
     279              :                                 /* Make sure owner's own grants are output before others */
     280            0 :                                 if (owner &&
     281            0 :                                         strcmp(grantee->data, owner) == 0 &&
     282            0 :                                         strcmp(grantor->data, owner) == 0)
     283            0 :                                         thissql = firstsql;
     284              :                                 else
     285            0 :                                         thissql = secondsql;
     286              : 
     287            0 :                                 if (grantor->len > 0
     288            0 :                                         && (!owner || strcmp(owner, grantor->data) != 0))
     289            0 :                                         appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
     290            0 :                                                                           fmtId(grantor->data));
     291              : 
     292            0 :                                 if (privs->len > 0)
     293              :                                 {
     294            0 :                                         appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     295            0 :                                                                           prefix, privs->data, type);
     296            0 :                                         if (nspname && *nspname)
     297            0 :                                                 appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     298            0 :                                         if (name && *name)
     299            0 :                                                 appendPQExpBuffer(thissql, "%s ", name);
     300            0 :                                         appendPQExpBufferStr(thissql, "TO ");
     301            0 :                                         if (grantee->len == 0)
     302            0 :                                                 appendPQExpBufferStr(thissql, "PUBLIC;\n");
     303              :                                         else
     304            0 :                                                 appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
     305            0 :                                 }
     306            0 :                                 if (privswgo->len > 0)
     307              :                                 {
     308            0 :                                         appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
     309            0 :                                                                           prefix, privswgo->data, type);
     310            0 :                                         if (nspname && *nspname)
     311            0 :                                                 appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
     312            0 :                                         if (name && *name)
     313            0 :                                                 appendPQExpBuffer(thissql, "%s ", name);
     314            0 :                                         appendPQExpBufferStr(thissql, "TO ");
     315            0 :                                         if (grantee->len == 0)
     316            0 :                                                 appendPQExpBufferStr(thissql, "PUBLIC");
     317              :                                         else
     318            0 :                                                 appendPQExpBufferStr(thissql, fmtId(grantee->data));
     319            0 :                                         appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
     320            0 :                                 }
     321              : 
     322            0 :                                 if (grantor->len > 0
     323            0 :                                         && (!owner || strcmp(owner, grantor->data) != 0))
     324            0 :                                         appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n");
     325            0 :                         }
     326            0 :                 }
     327              :                 else
     328              :                 {
     329              :                         /* parseAclItem failed, give up */
     330            0 :                         ok = false;
     331            0 :                         break;
     332              :                 }
     333            0 :         }
     334              : 
     335            0 :         destroyPQExpBuffer(grantee);
     336            0 :         destroyPQExpBuffer(grantor);
     337            0 :         destroyPQExpBuffer(privs);
     338            0 :         destroyPQExpBuffer(privswgo);
     339              : 
     340            0 :         appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
     341            0 :         destroyPQExpBuffer(firstsql);
     342            0 :         destroyPQExpBuffer(secondsql);
     343              : 
     344            0 :         free(aclitems);
     345            0 :         free(baseitems);
     346            0 :         free(grantitems);
     347            0 :         free(revokeitems);
     348              : 
     349            0 :         return ok;
     350            0 : }
     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            0 : 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            0 :         PQExpBuffer prefix;
     373              : 
     374            0 :         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            0 :         appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
     383            0 :                                           fmtId(owner));
     384            0 :         if (nspname)
     385            0 :                 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            0 :         if (!buildACLCommands("", NULL, NULL, type,
     392            0 :                                                   acls, acldefault, owner,
     393            0 :                                                   prefix->data, remoteVersion, sql))
     394              :         {
     395            0 :                 destroyPQExpBuffer(prefix);
     396            0 :                 return false;
     397              :         }
     398              : 
     399            0 :         destroyPQExpBuffer(prefix);
     400              : 
     401            0 :         return true;
     402            0 : }
     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            0 : 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            0 :         char       *buf;
     429            0 :         bool            all_with_go = true;
     430            0 :         bool            all_without_go = true;
     431            0 :         char       *eqpos;
     432            0 :         char       *slpos;
     433            0 :         char       *pos;
     434              : 
     435            0 :         buf = pg_strdup(item);
     436              : 
     437              :         /* user or group name is string up to = */
     438            0 :         eqpos = dequoteAclUserName(grantee, buf);
     439            0 :         if (*eqpos != '=')
     440              :         {
     441            0 :                 pg_free(buf);
     442            0 :                 return false;
     443              :         }
     444              : 
     445              :         /* grantor should appear after / */
     446            0 :         slpos = strchr(eqpos + 1, '/');
     447            0 :         if (slpos)
     448              :         {
     449            0 :                 *slpos++ = '\0';
     450            0 :                 slpos = dequoteAclUserName(grantor, slpos);
     451            0 :                 if (*slpos != '\0')
     452              :                 {
     453            0 :                         pg_free(buf);
     454            0 :                         return false;
     455              :                 }
     456            0 :         }
     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            0 :         resetPQExpBuffer(privs);
     484            0 :         resetPQExpBuffer(privswgo);
     485              : 
     486            0 :         if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
     487            0 :                 strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
     488              :         {
     489            0 :                 CONVERT_PRIV('r', "SELECT");
     490              : 
     491            0 :                 if (strcmp(type, "SEQUENCE") == 0 ||
     492            0 :                         strcmp(type, "SEQUENCES") == 0)
     493              :                         /* sequence only */
     494            0 :                         CONVERT_PRIV('U', "USAGE");
     495              :                 else
     496              :                 {
     497              :                         /* table only */
     498            0 :                         CONVERT_PRIV('a', "INSERT");
     499            0 :                         CONVERT_PRIV('x', "REFERENCES");
     500              :                         /* rest are not applicable to columns */
     501            0 :                         if (subname == NULL)
     502              :                         {
     503            0 :                                 CONVERT_PRIV('d', "DELETE");
     504            0 :                                 CONVERT_PRIV('t', "TRIGGER");
     505            0 :                                 CONVERT_PRIV('D', "TRUNCATE");
     506            0 :                                 CONVERT_PRIV('m', "MAINTAIN");
     507            0 :                         }
     508              :                 }
     509              : 
     510              :                 /* UPDATE */
     511            0 :                 CONVERT_PRIV('w', "UPDATE");
     512            0 :         }
     513            0 :         else if (strcmp(type, "FUNCTION") == 0 ||
     514            0 :                          strcmp(type, "FUNCTIONS") == 0)
     515            0 :                 CONVERT_PRIV('X', "EXECUTE");
     516            0 :         else if (strcmp(type, "PROCEDURE") == 0 ||
     517            0 :                          strcmp(type, "PROCEDURES") == 0)
     518            0 :                 CONVERT_PRIV('X', "EXECUTE");
     519            0 :         else if (strcmp(type, "LANGUAGE") == 0)
     520            0 :                 CONVERT_PRIV('U', "USAGE");
     521            0 :         else if (strcmp(type, "SCHEMA") == 0 ||
     522            0 :                          strcmp(type, "SCHEMAS") == 0)
     523              :         {
     524            0 :                 CONVERT_PRIV('C', "CREATE");
     525            0 :                 CONVERT_PRIV('U', "USAGE");
     526            0 :         }
     527            0 :         else if (strcmp(type, "DATABASE") == 0)
     528              :         {
     529            0 :                 CONVERT_PRIV('C', "CREATE");
     530            0 :                 CONVERT_PRIV('c', "CONNECT");
     531            0 :                 CONVERT_PRIV('T', "TEMPORARY");
     532            0 :         }
     533            0 :         else if (strcmp(type, "TABLESPACE") == 0)
     534            0 :                 CONVERT_PRIV('C', "CREATE");
     535            0 :         else if (strcmp(type, "TYPE") == 0 ||
     536            0 :                          strcmp(type, "TYPES") == 0)
     537            0 :                 CONVERT_PRIV('U', "USAGE");
     538            0 :         else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
     539            0 :                 CONVERT_PRIV('U', "USAGE");
     540            0 :         else if (strcmp(type, "FOREIGN SERVER") == 0)
     541            0 :                 CONVERT_PRIV('U', "USAGE");
     542            0 :         else if (strcmp(type, "FOREIGN TABLE") == 0)
     543            0 :                 CONVERT_PRIV('r', "SELECT");
     544            0 :         else if (strcmp(type, "PARAMETER") == 0)
     545              :         {
     546            0 :                 CONVERT_PRIV('s', "SET");
     547            0 :                 CONVERT_PRIV('A', "ALTER SYSTEM");
     548            0 :         }
     549            0 :         else if (strcmp(type, "LARGE OBJECT") == 0 ||
     550            0 :                          strcmp(type, "LARGE OBJECTS") == 0)
     551              :         {
     552            0 :                 CONVERT_PRIV('r', "SELECT");
     553            0 :                 CONVERT_PRIV('w', "UPDATE");
     554            0 :         }
     555              :         else
     556            0 :                 abort();
     557              : 
     558              : #undef CONVERT_PRIV
     559              : 
     560            0 :         if (all_with_go)
     561              :         {
     562            0 :                 resetPQExpBuffer(privs);
     563            0 :                 printfPQExpBuffer(privswgo, "ALL");
     564            0 :                 if (subname)
     565            0 :                         appendPQExpBuffer(privswgo, "(%s)", subname);
     566            0 :         }
     567            0 :         else if (all_without_go)
     568              :         {
     569            0 :                 resetPQExpBuffer(privswgo);
     570            0 :                 printfPQExpBuffer(privs, "ALL");
     571            0 :                 if (subname)
     572            0 :                         appendPQExpBuffer(privs, "(%s)", subname);
     573            0 :         }
     574              : 
     575            0 :         pg_free(buf);
     576              : 
     577            0 :         return true;
     578            0 : }
     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            0 : quoteAclUserName(PQExpBuffer output, const char *input)
     586              : {
     587            0 :         const char *src;
     588            0 :         bool            safe = true;
     589              : 
     590            0 :         for (src = input; *src; src++)
     591              :         {
     592              :                 /* This test had better match what putid() does */
     593            0 :                 if (!isalnum((unsigned char) *src) && *src != '_')
     594              :                 {
     595            0 :                         safe = false;
     596            0 :                         break;
     597              :                 }
     598            0 :         }
     599            0 :         if (!safe)
     600            0 :                 appendPQExpBufferChar(output, '"');
     601            0 :         for (src = input; *src; src++)
     602              :         {
     603              :                 /* A double quote character in a username is encoded as "" */
     604            0 :                 if (*src == '"')
     605            0 :                         appendPQExpBufferChar(output, '"');
     606            0 :                 appendPQExpBufferChar(output, *src);
     607            0 :         }
     608            0 :         if (!safe)
     609            0 :                 appendPQExpBufferChar(output, '"');
     610            0 : }
     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            0 : dequoteAclUserName(PQExpBuffer output, char *input)
     620              : {
     621            0 :         resetPQExpBuffer(output);
     622              : 
     623            0 :         while (*input && *input != '=')
     624              :         {
     625              :                 /*
     626              :                  * If user name isn't quoted, then just add it to the output buffer
     627              :                  */
     628            0 :                 if (*input != '"')
     629            0 :                         appendPQExpBufferChar(output, *input++);
     630              :                 else
     631              :                 {
     632              :                         /* Otherwise, it's a quoted username */
     633            0 :                         input++;
     634              :                         /* Loop until we come across an unescaped quote */
     635            0 :                         while (!(*input == '"' && *(input + 1) != '"'))
     636              :                         {
     637            0 :                                 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            0 :                                 if (*input == '"' && *(input + 1) == '"')
     645            0 :                                         input++;
     646            0 :                                 appendPQExpBufferChar(output, *input++);
     647              :                         }
     648            0 :                         input++;
     649              :                 }
     650              :         }
     651            0 :         return input;
     652            0 : }
     653              : 
     654              : /*
     655              :  * Append a privilege keyword to a keyword list, inserting comma if needed.
     656              :  */
     657              : static void
     658            0 : AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
     659              : {
     660            0 :         if (aclbuf->len > 0)
     661            0 :                 appendPQExpBufferChar(aclbuf, ',');
     662            0 :         appendPQExpBufferStr(aclbuf, keyword);
     663            0 :         if (subname)
     664            0 :                 appendPQExpBuffer(aclbuf, "(%s)", subname);
     665            0 : }
     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            0 : buildShSecLabelQuery(const char *catalog_name, Oid objectId,
     679              :                                          PQExpBuffer sql)
     680              : {
     681            0 :         appendPQExpBuffer(sql,
     682              :                                           "SELECT provider, label FROM pg_catalog.pg_shseclabel "
     683              :                                           "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
     684            0 :                                           "AND objoid = '%u'", catalog_name, objectId);
     685            0 : }
     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            0 : emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
     697              :                                 const char *objtype, const char *objname)
     698              : {
     699            0 :         int                     i;
     700              : 
     701            0 :         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            0 :                                                   fmtId(provider), objtype);
     710            0 :                 appendPQExpBuffer(buffer,
     711              :                                                   " %s IS ",
     712            0 :                                                   fmtId(objname));
     713            0 :                 appendStringLiteralConn(buffer, label, conn);
     714            0 :                 appendPQExpBufferStr(buffer, ";\n");
     715            0 :         }
     716            0 : }
     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            0 : variable_is_guc_list_quote(const char *name)
     731              : {
     732            0 :         if (pg_strcasecmp(name, "local_preload_libraries") == 0 ||
     733            0 :                 pg_strcasecmp(name, "oauth_validator_libraries") == 0 ||
     734            0 :                 pg_strcasecmp(name, "search_path") == 0 ||
     735            0 :                 pg_strcasecmp(name, "session_preload_libraries") == 0 ||
     736            0 :                 pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
     737            0 :                 pg_strcasecmp(name, "temp_tablespaces") == 0 ||
     738            0 :                 pg_strcasecmp(name, "unix_socket_directories") == 0)
     739            0 :                 return true;
     740              :         else
     741            0 :                 return false;
     742            0 : }
     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            0 : SplitGUCList(char *rawstring, char separator,
     766              :                          char ***namelist)
     767              : {
     768            0 :         char       *nextp = rawstring;
     769            0 :         bool            done = false;
     770            0 :         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            0 :         *namelist = nextptr = (char **)
     778            0 :                 pg_malloc((strlen(rawstring) / 2 + 2) * sizeof(char *));
     779            0 :         *nextptr = NULL;
     780              : 
     781            0 :         while (isspace((unsigned char) *nextp))
     782            0 :                 nextp++;                                /* skip leading whitespace */
     783              : 
     784            0 :         if (*nextp == '\0')
     785            0 :                 return true;                    /* empty string represents empty list */
     786              : 
     787              :         /* At the top of the loop, we are at start of a new identifier. */
     788            0 :         do
     789              :         {
     790            0 :                 char       *curname;
     791            0 :                 char       *endp;
     792              : 
     793            0 :                 if (*nextp == '"')
     794              :                 {
     795              :                         /* Quoted name --- collapse quote-quote pairs */
     796            0 :                         curname = nextp + 1;
     797            0 :                         for (;;)
     798              :                         {
     799            0 :                                 endp = strchr(nextp + 1, '"');
     800            0 :                                 if (endp == NULL)
     801            0 :                                         return false;   /* mismatched quotes */
     802            0 :                                 if (endp[1] != '"')
     803            0 :                                         break;          /* found end of quoted name */
     804              :                                 /* Collapse adjacent quotes into one quote, and look again */
     805            0 :                                 memmove(endp, endp + 1, strlen(endp));
     806            0 :                                 nextp = endp;
     807              :                         }
     808              :                         /* endp now points at the terminating quote */
     809            0 :                         nextp = endp + 1;
     810            0 :                 }
     811              :                 else
     812              :                 {
     813              :                         /* Unquoted name --- extends to separator or whitespace */
     814            0 :                         curname = nextp;
     815            0 :                         while (*nextp && *nextp != separator &&
     816            0 :                                    !isspace((unsigned char) *nextp))
     817            0 :                                 nextp++;
     818            0 :                         endp = nextp;
     819            0 :                         if (curname == nextp)
     820            0 :                                 return false;   /* empty unquoted name not allowed */
     821              :                 }
     822              : 
     823            0 :                 while (isspace((unsigned char) *nextp))
     824            0 :                         nextp++;                        /* skip trailing whitespace */
     825              : 
     826            0 :                 if (*nextp == separator)
     827              :                 {
     828            0 :                         nextp++;
     829            0 :                         while (isspace((unsigned char) *nextp))
     830            0 :                                 nextp++;                /* skip leading whitespace for next */
     831              :                         /* we expect another name, so done remains false */
     832            0 :                 }
     833            0 :                 else if (*nextp == '\0')
     834            0 :                         done = true;
     835              :                 else
     836            0 :                         return false;           /* invalid syntax */
     837              : 
     838              :                 /* Now safe to overwrite separator with a null */
     839            0 :                 *endp = '\0';
     840              : 
     841              :                 /*
     842              :                  * Finished isolating current name --- add it to output array
     843              :                  */
     844            0 :                 *nextptr++ = curname;
     845              : 
     846              :                 /* Loop back if we didn't reach end of string */
     847            0 :         } while (!done);
     848              : 
     849            0 :         *nextptr = NULL;
     850            0 :         return true;
     851            0 : }
     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            0 : 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            0 :         char       *mine;
     871            0 :         char       *pos;
     872              : 
     873              :         /* Parse the configitem.  If we can't find an "=", silently do nothing. */
     874            0 :         mine = pg_strdup(configitem);
     875            0 :         pos = strchr(mine, '=');
     876            0 :         if (pos == NULL)
     877              :         {
     878            0 :                 pg_free(mine);
     879            0 :                 return;
     880              :         }
     881            0 :         *pos++ = '\0';
     882              : 
     883              :         /* Build the command, with suitable quoting for everything. */
     884            0 :         appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
     885            0 :         if (type2 != NULL && name2 != NULL)
     886            0 :                 appendPQExpBuffer(buf, "IN %s %s ", type2, fmtId(name2));
     887            0 :         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            0 :         if (variable_is_guc_list_quote(mine))
     905              :         {
     906            0 :                 char      **namelist;
     907            0 :                 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            0 :                         }
     922            0 :                 }
     923            0 :                 pg_free(namelist);
     924            0 :         }
     925              :         else
     926            0 :                 appendStringLiteralConn(buf, pos, conn);
     927              : 
     928            0 :         appendPQExpBufferStr(buf, ";\n");
     929              : 
     930            0 :         pg_free(mine);
     931            0 : }
     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            0 : create_or_open_dir(const char *dirname)
     941              : {
     942            0 :         int                     ret;
     943              : 
     944            0 :         switch ((ret = pg_check_dir(dirname)))
     945              :         {
     946              :                 case -1:
     947              :                         /* opendir failed but not with ENOENT */
     948            0 :                         pg_fatal("could not open directory \"%s\": %m", dirname);
     949            0 :                         break;
     950              :                 case 0:
     951              :                         /* directory does not exist */
     952            0 :                         if (mkdir(dirname, pg_dir_create_mode) < 0)
     953            0 :                                 pg_fatal("could not create directory \"%s\": %m", dirname);
     954            0 :                         break;
     955              :                 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              :                 default:
     962              :                         /* exists and is not empty */
     963            0 :                         pg_fatal("directory \"%s\" is not empty", dirname);
     964            0 :         }
     965            0 : }
     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            0 : generate_restrict_key(void)
     974              : {
     975            0 :         uint8           buf[64];
     976            0 :         char       *ret = palloc(sizeof(buf));
     977              : 
     978            0 :         if (!pg_strong_random(buf, sizeof(buf)))
     979            0 :                 return NULL;
     980              : 
     981            0 :         for (int i = 0; i < sizeof(buf) - 1; i++)
     982              :         {
     983            0 :                 uint8           idx = buf[i] % strlen(restrict_chars);
     984              : 
     985            0 :                 ret[i] = restrict_chars[idx];
     986            0 :         }
     987            0 :         ret[sizeof(buf) - 1] = '\0';
     988              : 
     989            0 :         return ret;
     990            0 : }
     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            0 : valid_restrict_key(const char *restrict_key)
     998              : {
     999            0 :         return restrict_key != NULL &&
    1000            0 :                 restrict_key[0] != '\0' &&
    1001            0 :                 strspn(restrict_key, restrict_chars) == strlen(restrict_key);
    1002              : }
        

Generated by: LCOV version 2.3.2-1