v3-allow-SET-to-an-empty-list.patch
text/x-diff
Filename: v3-allow-SET-to-an-empty-list.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/utils/adt/ruleutils.c | 7 | 3 |
| src/backend/utils/adt/varlena.c | 9 | 3 |
| src/backend/utils/misc/guc_funcs.c | 36 | 7 |
| src/bin/pg_dump/dumputils.c | 9 | 3 |
| src/bin/pg_dump/pg_dump.c | 6 | 2 |
| src/test/regress/expected/guc.out | 18 | 0 |
| src/test/regress/expected/rules.out | 15 | 13 |
| src/test/regress/sql/guc.sql | 8 | 0 |
| src/test/regress/sql/rules.sql | 2 | 1 |
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3d6e6bdbfd2..27ba1920e68 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3086,9 +3086,10 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
* rules used there aren't exactly like SQL's, we have to
* break the list value apart and then quote the elements as
* string literals. (The elements may be double-quoted as-is,
- * but we can't just feed them to the SQL parser; it would do
- * the wrong thing with elements that are zero-length or
- * longer than NAMEDATALEN.)
+ * but we can't just feed them to the SQL parser that way; it
+ * would truncate elements that are longer than NAMEDATALEN,
+ * which would be wrong if they're paths.) Also, we need a
+ * special case for empty lists.
*
* Variables that are not so marked should just be emitted as
* simple string literals. If the variable is not known to
@@ -3106,6 +3107,9 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
/* this shouldn't fail really */
elog(ERROR, "invalid list syntax in proconfig item");
}
+ /* Special case: represent an empty list as '' */
+ if (namelist == NIL)
+ appendStringInfoString(&buf, "''");
foreach(lc, namelist)
{
char *curname = (char *) lfirst(lc);
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 2c398cd9e5c..d5ef492ee41 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2753,7 +2753,7 @@ SplitIdentifierString(char *rawstring, char separator,
nextp++; /* skip leading whitespace */
if (*nextp == '\0')
- return true; /* allow empty string */
+ return true; /* empty string represents empty list */
/* At the top of the loop, we are at start of a new identifier. */
do
@@ -2777,6 +2777,8 @@ SplitIdentifierString(char *rawstring, char separator,
nextp = endp;
}
/* endp now points at the terminating quote */
+ if (curname == endp)
+ return false; /* empty quoted name not allowed */
nextp = endp + 1;
}
else
@@ -2880,7 +2882,7 @@ SplitDirectoriesString(char *rawstring, char separator,
nextp++; /* skip leading whitespace */
if (*nextp == '\0')
- return true; /* allow empty string */
+ return true; /* empty string represents empty list */
/* At the top of the loop, we are at start of a new directory. */
do
@@ -2904,6 +2906,8 @@ SplitDirectoriesString(char *rawstring, char separator,
nextp = endp;
}
/* endp now points at the terminating quote */
+ if (curname == endp)
+ return false; /* empty quoted name not allowed */
nextp = endp + 1;
}
else
@@ -3001,7 +3005,7 @@ SplitGUCList(char *rawstring, char separator,
nextp++; /* skip leading whitespace */
if (*nextp == '\0')
- return true; /* allow empty string */
+ return true; /* empty string represents empty list */
/* At the top of the loop, we are at start of a new identifier. */
do
@@ -3025,6 +3029,8 @@ SplitGUCList(char *rawstring, char separator,
nextp = endp;
}
/* endp now points at the terminating quote */
+ if (curname == endp)
+ return false; /* empty quoted name not allowed */
nextp = endp + 1;
}
else
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c
index b9e26982abd..a0557d164d8 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -210,12 +210,30 @@ flatten_set_variable_args(const char *name, List *args)
else
flags = 0;
- /* Complain if list input and non-list variable */
- if ((flags & GUC_LIST_INPUT) == 0 &&
- list_length(args) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("SET %s takes only one argument", name)));
+ /*
+ * Handle special cases for list input.
+ */
+ if (flags & GUC_LIST_INPUT)
+ {
+ /* A single empty-string item is treated as an empty list. */
+ if (list_length(args) == 1)
+ {
+ Node *arg = (Node *) linitial(args);
+
+ if (IsA(arg, A_Const) &&
+ nodeTag(&((A_Const *) arg)->val) == T_String &&
+ *strVal(&((A_Const *) arg)->val) == '\0')
+ return pstrdup("");
+ }
+ }
+ else
+ {
+ /* Complain if list input and non-list variable. */
+ if (list_length(args) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("SET %s takes only one argument", name)));
+ }
initStringInfo(&buf);
@@ -269,6 +287,9 @@ flatten_set_variable_args(const char *name, List *args)
Datum interval;
char *intervalout;
+ /* gram.y ensures this is only reachable for TIME ZONE */
+ Assert(!(flags & GUC_LIST_QUOTE));
+
typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
Assert(typoid == INTERVALOID);
@@ -286,9 +307,17 @@ flatten_set_variable_args(const char *name, List *args)
else
{
/*
- * Plain string literal or identifier. For quote mode,
+ * Plain string literal or identifier. In a list GUC,
+ * disallow empty-string elements (so that the preceding
+ * hack for empty lists is unambiguous). For quote mode,
* quote it if it's not a vanilla identifier.
*/
+ if ((flags & GUC_LIST_INPUT) && *val == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("SET %s does not accept empty-string elements",
+ name)));
+
if (flags & GUC_LIST_QUOTE)
appendStringInfoString(&buf, quote_identifier(val));
else
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 05b84c0d6e7..00a369c8861 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -781,7 +781,7 @@ SplitGUCList(char *rawstring, char separator,
nextp++; /* skip leading whitespace */
if (*nextp == '\0')
- return true; /* allow empty string */
+ return true; /* empty string represents empty list */
/* At the top of the loop, we are at start of a new identifier. */
do
@@ -805,6 +805,8 @@ SplitGUCList(char *rawstring, char separator,
nextp = endp;
}
/* endp now points at the terminating quote */
+ if (curname == endp)
+ return false; /* empty quoted name not allowed */
nextp = endp + 1;
}
else
@@ -891,8 +893,9 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
* array. However, because the quoting rules used there aren't exactly
* like SQL's, we have to break the list value apart and then quote the
* elements as string literals. (The elements may be double-quoted as-is,
- * but we can't just feed them to the SQL parser; it would do the wrong
- * thing with elements that are zero-length or longer than NAMEDATALEN.)
+ * but we can't just feed them to the SQL parser that way; it would
+ * truncate elements that are longer than NAMEDATALEN, which would be
+ * wrong if they're paths.) Also, we need a special case for empty lists.
*
* Variables that are not so marked should just be emitted as simple
* string literals. If the variable is not known to
@@ -908,6 +911,9 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
/* this shouldn't fail really */
if (SplitGUCList(pos, ',', &namelist))
{
+ /* Special case: represent an empty list as '' */
+ if (*namelist == NULL)
+ appendPQExpBufferStr(buf, "''");
for (nameptr = namelist; *nameptr; nameptr++)
{
if (nameptr != namelist)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bea793456f9..9d36a6a5aaf 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13699,8 +13699,9 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
* aren't exactly like SQL's, we have to break the list value apart
* and then quote the elements as string literals. (The elements may
* be double-quoted as-is, but we can't just feed them to the SQL
- * parser; it would do the wrong thing with elements that are
- * zero-length or longer than NAMEDATALEN.)
+ * parser that way; it would truncate elements that are longer than
+ * NAMEDATALEN, which would be wrong if they're paths.) Also, we need
+ * a special case for empty lists.
*
* Variables that are not so marked should just be emitted as simple
* string literals. If the variable is not known to
@@ -13716,6 +13717,9 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
/* this shouldn't fail really */
if (SplitGUCList(pos, ',', &namelist))
{
+ /* Special case: represent an empty list as '' */
+ if (*namelist == NULL)
+ appendPQExpBufferStr(q, "''");
for (nameptr = namelist; *nameptr; nameptr++)
{
if (nameptr != namelist)
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765c..e49e609415b 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -31,6 +31,24 @@ SELECT '2006-08-13 12:34:56'::timestamptz;
2006-08-13 12:34:56-07
(1 row)
+-- Check handling of list GUCs
+SET search_path = 'pg_catalog', Foo, 'Bar';
+SHOW search_path;
+ search_path
+------------------------
+ pg_catalog, foo, "Bar"
+(1 row)
+
+SET search_path = ''; -- means empty list
+SHOW search_path;
+ search_path
+-------------
+
+(1 row)
+
+SET search_path = '', 'foo'; -- error, empty list elements not OK
+ERROR: SET search_path does not accept empty-string elements
+RESET search_path;
-- SET LOCAL has no effect outside of a transaction
SET LOCAL vacuum_cost_delay TO 50;
WARNING: SET LOCAL can only be used in transaction blocks
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 35e8aad7701..43d5cf10266 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -3549,21 +3549,23 @@ CREATE FUNCTION func_with_set_params() RETURNS integer
SET extra_float_digits TO 2
SET work_mem TO '4MB'
SET datestyle to iso, mdy
- SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
+ SET temp_tablespaces to ''
+ SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
IMMUTABLE STRICT;
SELECT pg_get_functiondef('func_with_set_params()'::regprocedure);
- pg_get_functiondef
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- CREATE OR REPLACE FUNCTION public.func_with_set_params() +
- RETURNS integer +
- LANGUAGE sql +
- IMMUTABLE STRICT +
- SET search_path TO 'pg_catalog' +
- SET extra_float_digits TO '2' +
- SET work_mem TO '4MB' +
- SET "DateStyle" TO 'iso, mdy' +
- SET local_preload_libraries TO 'Mixed/Case', 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'+
- AS $function$select 1;$function$ +
+ pg_get_functiondef
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.func_with_set_params() +
+ RETURNS integer +
+ LANGUAGE sql +
+ IMMUTABLE STRICT +
+ SET search_path TO 'pg_catalog' +
+ SET extra_float_digits TO '2' +
+ SET work_mem TO '4MB' +
+ SET "DateStyle" TO 'iso, mdy' +
+ SET temp_tablespaces TO '' +
+ SET local_preload_libraries TO 'Mixed/Case', 'c:/''a"/path', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'+
+ AS $function$select 1;$function$ +
(1 row)
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..65630135f18 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -12,6 +12,14 @@ SHOW vacuum_cost_delay;
SHOW datestyle;
SELECT '2006-08-13 12:34:56'::timestamptz;
+-- Check handling of list GUCs
+SET search_path = 'pg_catalog', Foo, 'Bar';
+SHOW search_path;
+SET search_path = ''; -- means empty list
+SHOW search_path;
+SET search_path = '', 'foo'; -- error, empty list elements not OK
+RESET search_path;
+
-- SET LOCAL has no effect outside of a transaction
SET LOCAL vacuum_cost_delay TO 50;
SHOW vacuum_cost_delay;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index fdd3ff1d161..5c5ff5e9cca 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -1217,7 +1217,8 @@ CREATE FUNCTION func_with_set_params() RETURNS integer
SET extra_float_digits TO 2
SET work_mem TO '4MB'
SET datestyle to iso, mdy
- SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
+ SET temp_tablespaces to ''
+ SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
IMMUTABLE STRICT;
SELECT pg_get_functiondef('func_with_set_params()'::regprocedure);