Skip to content

Commit 67e02cd

Browse files
committed
Fix mishandling of quoted-list GUC values in pg_dump and ruleutils.c.
Code that prints out the contents of setconfig or proconfig arrays in SQL format needs to handle GUC_LIST_QUOTE variables differently from other ones, because for those variables, flatten_set_variable_args() already applied a layer of quoting. The value can therefore safely be printed as-is, and indeed must be, or flatten_set_variable_args() will muck it up completely on reload. For all other GUC variables, it's necessary and sufficient to quote the value as a SQL literal. We'd recognized the need for this long ago, but mis-analyzed the need slightly, thinking that all GUC_LIST_INPUT variables needed the special treatment. That's actually wrong, since a valid value of a LIST variable might include characters that need quoting, although no existing variables accept such values. More to the point, we hadn't made any particular effort to keep the various places that deal with this up-to-date with the set of variables that actually need special treatment, meaning that we'd do the wrong thing with, for example, temp_tablespaces values. This affects dumping of SET clauses attached to functions, as well as ALTER DATABASE/ROLE SET commands. In ruleutils.c we can fix it reasonably honestly by exporting a guc.c function that allows discovering the flags for a given GUC variable. But pg_dump doesn't have easy access to that, so continue the old method of having a hard-wired list of affected variable names. At least we can fix it to have just one list not two, and update the list to match current reality. A remaining problem with this is that it only works for built-in GUC variables. pg_dump's list obvious knows nothing of third-party extensions, and even the "ask guc.c" method isn't bulletproof since the relevant extension might not be loaded. There's no obvious solution to that, so for now, we'll just have to discourage extension authors from inventing custom GUCs that need GUC_LIST_QUOTE. This has been busted for a long time, so back-patch to all supported branches. Michael Paquier and Tom Lane, reviewed by Kyotaro Horiguchi and Pavel Stehule Discussion: https://postgr.es/m/20180111064900.GA51030@paquier.xyz
1 parent 9312fcf commit 67e02cd

File tree

9 files changed

+114
-13
lines changed

9 files changed

+114
-13
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "utils/array.h"
5555
#include "utils/builtins.h"
5656
#include "utils/fmgroids.h"
57+
#include "utils/guc.h"
5758
#include "utils/hsearch.h"
5859
#include "utils/lsyscache.h"
5960
#include "utils/rel.h"
@@ -2052,11 +2053,15 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
20522053
quote_identifier(configitem));
20532054

20542055
/*
2055-
* Some GUC variable names are 'LIST' type and hence must not
2056-
* be quoted.
2056+
* Variables that are marked GUC_LIST_QUOTE were already fully
2057+
* quoted by flatten_set_variable_args() before they were put
2058+
* into the proconfig array; we mustn't re-quote them or we'll
2059+
* make a mess. Variables that are not so marked should just
2060+
* be emitted as simple string literals. If the variable is
2061+
* not known to guc.c, we'll do the latter; this makes it
2062+
* unsafe to use GUC_LIST_QUOTE for extension variables.
20572063
*/
2058-
if (pg_strcasecmp(configitem, "DateStyle") == 0
2059-
|| pg_strcasecmp(configitem, "search_path") == 0)
2064+
if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
20602065
appendStringInfoString(&buf, pos);
20612066
else
20622067
simple_quote_literal(&buf, pos);

src/backend/utils/misc/guc.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,8 @@ const char *const config_type_names[] =
674674
*
675675
* 6. Don't forget to document the option (at least in config.sgml).
676676
*
677-
* 7. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure
678-
* it is not single quoted at dump time.
677+
* 7. If it's a new GUC_LIST_QUOTE option, you must add it to
678+
* variable_is_guc_list_quote() in src/bin/pg_dump/dumputils.c.
679679
*/
680680

681681

@@ -6370,6 +6370,30 @@ GetConfigOptionResetString(const char *name)
63706370
return NULL;
63716371
}
63726372

6373+
/*
6374+
* Get the GUC flags associated with the given option.
6375+
*
6376+
* If the option doesn't exist, return 0 if missing_ok is true,
6377+
* otherwise throw an ereport and don't return.
6378+
*/
6379+
int
6380+
GetConfigOptionFlags(const char *name, bool missing_ok)
6381+
{
6382+
struct config_generic *record;
6383+
6384+
record = find_option(name, false, WARNING);
6385+
if (record == NULL)
6386+
{
6387+
if (missing_ok)
6388+
return 0;
6389+
ereport(ERROR,
6390+
(errcode(ERRCODE_UNDEFINED_OBJECT),
6391+
errmsg("unrecognized configuration parameter \"%s\"",
6392+
name)));
6393+
}
6394+
return record->flags;
6395+
}
6396+
63736397

63746398
/*
63756399
* flatten_set_variable_args

src/bin/pg_dump/dumputils.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,3 +1522,26 @@ simple_string_list_member(SimpleStringList *list, const char *val)
15221522
}
15231523
return false;
15241524
}
1525+
1526+
/*
1527+
* Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
1528+
*
1529+
* It'd be better if we could inquire this directly from the backend; but even
1530+
* if there were a function for that, it could only tell us about variables
1531+
* currently known to guc.c, so that it'd be unsafe for extensions to declare
1532+
* GUC_LIST_QUOTE variables anyway. Lacking a solution for that, it doesn't
1533+
* seem worth the work to do more than have this list, which must be kept in
1534+
* sync with the variables actually marked GUC_LIST_QUOTE in guc.c.
1535+
*/
1536+
bool
1537+
variable_is_guc_list_quote(const char *name)
1538+
{
1539+
if (pg_strcasecmp(name, "temp_tablespaces") == 0 ||
1540+
pg_strcasecmp(name, "session_preload_libraries") == 0 ||
1541+
pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
1542+
pg_strcasecmp(name, "local_preload_libraries") == 0 ||
1543+
pg_strcasecmp(name, "search_path") == 0)
1544+
return true;
1545+
else
1546+
return false;
1547+
}

src/bin/pg_dump/dumputils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,6 @@ extern void set_dump_section(const char *arg, int *dumpSections);
7575
extern void simple_string_list_append(SimpleStringList *list, const char *val);
7676
extern bool simple_string_list_member(SimpleStringList *list, const char *val);
7777

78+
extern bool variable_is_guc_list_quote(const char *name);
79+
7880
#endif /* DUMPUTILS_H */

src/bin/pg_dump/pg_dump.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10149,11 +10149,15 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
1014910149
appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
1015010150

1015110151
/*
10152-
* Some GUC variable names are 'LIST' type and hence must not be
10153-
* quoted.
10152+
* Variables that are marked GUC_LIST_QUOTE were already fully quoted
10153+
* by flatten_set_variable_args() before they were put into the
10154+
* proconfig array; we mustn't re-quote them or we'll make a mess.
10155+
* Variables that are not so marked should just be emitted as simple
10156+
* string literals. If the variable is not known to
10157+
* variable_is_guc_list_quote(), we'll do the latter; this makes it
10158+
* unsafe to use GUC_LIST_QUOTE for extension variables.
1015410159
*/
10155-
if (pg_strcasecmp(configitem, "DateStyle") == 0
10156-
|| pg_strcasecmp(configitem, "search_path") == 0)
10160+
if (variable_is_guc_list_quote(configitem))
1015710161
appendPQExpBufferStr(q, pos);
1015810162
else
1015910163
appendStringLiteralAH(q, pos, fout);

src/bin/pg_dump/pg_dumpall.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,10 +1610,15 @@ makeAlterConfigCommand(PGconn *conn, const char *arrayitem,
16101610
appendPQExpBuffer(buf, "SET %s TO ", fmtId(mine));
16111611

16121612
/*
1613-
* Some GUC variable names are 'LIST' type and hence must not be quoted.
1613+
* Variables that are marked GUC_LIST_QUOTE were already fully quoted by
1614+
* flatten_set_variable_args() before they were put into the setconfig
1615+
* array; we mustn't re-quote them or we'll make a mess. Variables that
1616+
* are not so marked should just be emitted as simple string literals. If
1617+
* the variable is not known to variable_is_guc_list_quote(), we'll do the
1618+
* latter; this makes it unsafe to use GUC_LIST_QUOTE for extension
1619+
* variables.
16141620
*/
1615-
if (pg_strcasecmp(mine, "DateStyle") == 0
1616-
|| pg_strcasecmp(mine, "search_path") == 0)
1621+
if (variable_is_guc_list_quote(mine))
16171622
appendPQExpBufferStr(buf, pos + 1);
16181623
else
16191624
appendStringLiteralConn(buf, pos + 1, conn);

src/include/utils/guc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ extern void EmitWarningsOnPlaceholders(const char *className);
323323
extern const char *GetConfigOption(const char *name, bool missing_ok,
324324
bool restrict_superuser);
325325
extern const char *GetConfigOptionResetString(const char *name);
326+
extern int GetConfigOptionFlags(const char *name, bool missing_ok);
326327
extern void ProcessConfigFile(GucContext context);
327328
extern void InitializeGUCOptions(void);
328329
extern bool SelectConfigFiles(const char *userDoption, const char *progname);

src/test/regress/expected/rules.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,3 +2706,28 @@ View definition:
27062706
FROM ( VALUES (1,2)) v(q, w);
27072707

27082708
drop view rule_v1;
2709+
-- test for pg_get_functiondef properly regurgitating SET parameters
2710+
-- Note that the function is kept around to stress pg_dump.
2711+
CREATE FUNCTION func_with_set_params() RETURNS integer
2712+
AS 'select 1;'
2713+
LANGUAGE SQL
2714+
SET extra_float_digits TO 2
2715+
SET work_mem TO '4MB'
2716+
SET datestyle to iso, mdy
2717+
SET search_path TO PG_CATALOG, "Mixed/Case", 'c:/"a"/path'
2718+
IMMUTABLE STRICT;
2719+
SELECT pg_get_functiondef('func_with_set_params()'::regprocedure);
2720+
pg_get_functiondef
2721+
---------------------------------------------------------------
2722+
CREATE OR REPLACE FUNCTION public.func_with_set_params() +
2723+
RETURNS integer +
2724+
LANGUAGE sql +
2725+
IMMUTABLE STRICT +
2726+
SET extra_float_digits TO '2' +
2727+
SET work_mem TO '4MB' +
2728+
SET "DateStyle" TO 'iso, mdy' +
2729+
SET search_path TO pg_catalog, "Mixed/Case", "c:/""a""/path"+
2730+
AS $function$select 1;$function$ +
2731+
2732+
(1 row)
2733+

src/test/regress/sql/rules.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,3 +1023,15 @@ drop view rule_v1;
10231023
create view rule_v1(x) as select * from (values(1,2)) v(q,w);
10241024
\d+ rule_v1
10251025
drop view rule_v1;
1026+
1027+
-- test for pg_get_functiondef properly regurgitating SET parameters
1028+
-- Note that the function is kept around to stress pg_dump.
1029+
CREATE FUNCTION func_with_set_params() RETURNS integer
1030+
AS 'select 1;'
1031+
LANGUAGE SQL
1032+
SET extra_float_digits TO 2
1033+
SET work_mem TO '4MB'
1034+
SET datestyle to iso, mdy
1035+
SET search_path TO PG_CATALOG, "Mixed/Case", 'c:/"a"/path'
1036+
IMMUTABLE STRICT;
1037+
SELECT pg_get_functiondef('func_with_set_params()'::regprocedure);

0 commit comments

Comments
 (0)