LCOV - code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 276 337 81.9 %
Date: 2025-04-24 12:15:10 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14