postgres.git / summary / log / commit / refs
commit 4fcccea972bcdce66976da86535e970dea9b999f
Author: Jacob Champion <jchampion@postgresql.org>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon Aug 10 13:38:18 2026 +0000
First released in: 17.11, 18.6
Move SplitGUCList() to fe_utils
This is needed so that all versions of pg_upgrade that migrate logical
replication slots can parse the new output_plugin_libraries GUC.
Backpatch-through: 17
Security: CVE-2026-6471
src/backend/utils/adt/varlena.c | 2 +-
src/bin/pg_dump/dumputils.c | 109 -----------------------------------
src/bin/pg_dump/dumputils.h | 3 -
src/fe_utils/string_utils.c | 110 ++++++++++++++++++++++++++++++++++++
src/include/fe_utils/string_utils.h | 2 +
5 files changed, 113 insertions(+), 113 deletions(-)
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index d3587359187..ed55cc20258 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -3731,7 +3731,7 @@ SplitDirectoriesString(char *rawstring, char separator,
* However, it's not clear that having one function with a bunch of option
* flags would be much better.
*
- * XXX there is a version of this function in src/bin/pg_dump/dumputils.c.
+ * XXX there is a version of this function in src/fe_utils/string_utils.c.
* Be sure to update that if you have to change this.
*
* Inputs:
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 47522d05429..a32da911ffa 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -737,115 +737,6 @@ variable_is_guc_list_quote(const char *name)
return false;
}
-/*
- * SplitGUCList --- parse a string containing identifiers or file names
- *
- * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
- * presuming whether the elements will be taken as identifiers or file names.
- * See comparable code in src/backend/utils/adt/varlena.c.
- *
- * Inputs:
- * rawstring: the input string; must be overwritable! On return, it's
- * been modified to contain the separated identifiers.
- * separator: the separator punctuation expected between identifiers
- * (typically '.' or ','). Whitespace may also appear around
- * identifiers.
- * Outputs:
- * namelist: receives a malloc'd, null-terminated array of pointers to
- * identifiers within rawstring. Caller should free this
- * even on error return.
- *
- * Returns true if okay, false if there is a syntax error in the string.
- */
-bool
-SplitGUCList(char *rawstring, char separator,
- char ***namelist)
-{
- char *nextp = rawstring;
- bool done = false;
- char **nextptr;
-
- /*
- * Since we disallow empty identifiers, this is a conservative
- * overestimate of the number of pointers we could need. Allow one for
- * list terminator.
- */
- *namelist = nextptr = (char **)
- pg_malloc((strlen(rawstring) / 2 + 2) * sizeof(char *));
- *nextptr = NULL;
-
- while (isspace((unsigned char) *nextp))
- nextp++; /* skip leading whitespace */
-
- if (*nextp == '\0')
- return true; /* allow empty string */
-
- /* At the top of the loop, we are at start of a new identifier. */
- do
- {
- char *curname;
- char *endp;
-
- if (*nextp == '"')
- {
- /* Quoted name --- collapse quote-quote pairs */
- curname = nextp + 1;
- for (;;)
- {
- endp = strchr(nextp + 1, '"');
- if (endp == NULL)
- return false; /* mismatched quotes */
- if (endp[1] != '"')
- break; /* found end of quoted name */
- /* Collapse adjacent quotes into one quote, and look again */
- memmove(endp, endp + 1, strlen(endp));
- nextp = endp;
- }
- /* endp now points at the terminating quote */
- nextp = endp + 1;
- }
- else
- {
- /* Unquoted name --- extends to separator or whitespace */
- curname = nextp;
- while (*nextp && *nextp != separator &&
- !isspace((unsigned char) *nextp))
- nextp++;
- endp = nextp;
- if (curname == nextp)
- return false; /* empty unquoted name not allowed */
- }
-
- while (isspace((unsigned char) *nextp))
- nextp++; /* skip trailing whitespace */
-
- if (*nextp == separator)
- {
- nextp++;
- while (isspace((unsigned char) *nextp))
- nextp++; /* skip leading whitespace for next */
- /* we expect another name, so done remains false */
- }
- else if (*nextp == '\0')
- done = true;
- else
- return false; /* invalid syntax */
-
- /* Now safe to overwrite separator with a null */
- *endp = '\0';
-
- /*
- * Finished isolating current name --- add it to output array
- */
- *nextptr++ = curname;
-
- /* Loop back if we didn't reach end of string */
- } while (!done);
-
- *nextptr = NULL;
- return true;
-}
-
/*
* Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
*
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index f685c616c64..c00f58cc9d9 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -56,9 +56,6 @@ extern void emitShSecLabels(PGconn *conn, PGresult *res,
extern bool variable_is_guc_list_quote(const char *name);
-extern bool SplitGUCList(char *rawstring, char separator,
- char ***namelist);
-
extern void makeAlterConfigCommand(PGconn *conn, const char *configitem,
const char *type, const char *name,
const char *type2, const char *name2,
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 8e062c70379..4439c683108 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -805,6 +805,116 @@ appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
}
+/*
+ * SplitGUCList --- parse a string containing identifiers or file names
+ *
+ * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
+ * presuming whether the elements will be taken as identifiers or file names.
+ * See comparable code in src/backend/utils/adt/varlena.c.
+ *
+ * Inputs:
+ * rawstring: the input string; must be overwritable! On return, it's
+ * been modified to contain the separated identifiers.
+ * separator: the separator punctuation expected between identifiers
+ * (typically '.' or ','). Whitespace may also appear around
+ * identifiers.
+ * Outputs:
+ * namelist: receives a malloc'd, null-terminated array of pointers to
+ * identifiers within rawstring. Caller should free this
+ * even on error return.
+ *
+ * Returns true if okay, false if there is a syntax error in the string.
+ */
+bool
+SplitGUCList(char *rawstring, char separator,
+ char ***namelist)
+{
+ char *nextp = rawstring;
+ bool done = false;
+ char **nextptr;
+
+ /*
+ * Since we disallow empty identifiers, this is a conservative
+ * overestimate of the number of pointers we could need. Allow one for
+ * list terminator.
+ */
+ *namelist = nextptr =
+ pg_malloc_array(char *, (strlen(rawstring) / 2 + 2));
+ *nextptr = NULL;
+
+ while (isspace((unsigned char) *nextp))
+ nextp++; /* skip leading whitespace */
+
+ if (*nextp == '\0')
+ return true; /* allow empty string */
+
+ /* At the top of the loop, we are at start of a new identifier. */
+ do
+ {
+ char *curname;
+ char *endp;
+
+ if (*nextp == '"')
+ {
+ /* Quoted name --- collapse quote-quote pairs */
+ curname = nextp + 1;
+ for (;;)
+ {
+ endp = strchr(nextp + 1, '"');
+ if (endp == NULL)
+ return false; /* mismatched quotes */
+ if (endp[1] != '"')
+ break; /* found end of quoted name */
+ /* Collapse adjacent quotes into one quote, and look again */
+ memmove(endp, endp + 1, strlen(endp));
+ nextp = endp;
+ }
+ /* endp now points at the terminating quote */
+ nextp = endp + 1;
+ }
+ else
+ {
+ /* Unquoted name --- extends to separator or whitespace */
+ curname = nextp;
+ while (*nextp && *nextp != separator &&
+ !isspace((unsigned char) *nextp))
+ nextp++;
+ endp = nextp;
+ if (curname == nextp)
+ return false; /* empty unquoted name not allowed */
+ }
+
+ while (isspace((unsigned char) *nextp))
+ nextp++; /* skip trailing whitespace */
+
+ if (*nextp == separator)
+ {
+ nextp++;
+ while (isspace((unsigned char) *nextp))
+ nextp++; /* skip leading whitespace for next */
+ /* we expect another name, so done remains false */
+ }
+ else if (*nextp == '\0')
+ done = true;
+ else
+ return false; /* invalid syntax */
+
+ /* Now safe to overwrite separator with a null */
+ *endp = '\0';
+
+ /*
+ * Finished isolating current name --- add it to output array
+ */
+ *nextptr++ = curname;
+
+ /* Loop back if we didn't reach end of string */
+ } while (!done);
+
+ *nextptr = NULL;
+ return true;
+}
+
+
/*
* Deconstruct the text representation of a 1-dimensional Postgres array
* into individual items.
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h
index 500f585a4a5..ee99daeb6f7 100644
--- a/src/include/fe_utils/string_utils.h
+++ b/src/include/fe_utils/string_utils.h
@@ -48,6 +48,8 @@ extern bool appendShellStringNoError(PQExpBuffer buf, const char *str);
extern void appendConnStrVal(PQExpBuffer buf, const char *str);
extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
+extern bool SplitGUCList(char *rawstring, char separator, char ***namelist);
+
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
extern void appendPGArray(PQExpBuffer buffer, const char *value);
[parent: e41c72aff039]