LCOV - differential code coverage report
Current view: top level - src/bin/pg_dump - dumputils.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 83.9 % 323 271 4 3 17 28 4 65 15 187 16 72 4 9
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 11 11 2 2 7 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a