LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 82.7 % 364 301
Test Date: 2026-07-26 10:15:44 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 62.4 % 452 282

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

Generated by: LCOV version 2.0-1