From 11a020eb6e4023a1570a2788ba22dd6aafbd02dc Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 28 Aug 2014 13:59:29 +0200 Subject: [PATCH] Allow escaping of option values for options passed at connection start. This is useful to allow to set GUCs to values that include spaces; something that wasn't previously possible. The primary case motivating this is the desire to set default_transaction_isolation to 'repeatable read' on a per connection basis, but other usecases like seach_path do also exist. This introduces a slight backward incompatibility: Previously a \ in an option value would have been passed on literally, now it'll be taken as an escape. The relevant mailing list discussion starts with 20140204125823.GJ12016@awork2.anarazel.de. --- doc/src/sgml/protocol.sgml | 5 ++- src/backend/postmaster/postmaster.c | 3 +- src/backend/utils/init/postinit.c | 52 +++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index e519ff96b9..a8a09e4f0e 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -4734,7 +4734,10 @@ StartupMessage (F) set at backend start time might be listed. Such settings will be applied during backend start (after parsing the command-line options if any). The values will act as - session defaults. + session defaults. Spaces in option values need to be escaped + with a backslash (\). A literal backslash can be + passed by escaping it with another backslash + (i.e \\). diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b190cf5113..14535c8b35 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -4083,8 +4083,7 @@ BackendRun(Port *port) /* * Pass any backend switches specified with -o on the postmaster's own - * command line. We assume these are secure. (It's OK to mangle - * ExtraOptions now, since we're safely inside a subprocess.) + * command line. We assume these are secure. */ pg_split_opts(av, &ac, ExtraOptions); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index a5b9821773..304be04789 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -409,32 +409,57 @@ InitCommunication(void) /* * pg_split_opts -- split a string of options and append it to an argv array * - * NB: the input string is destructively modified! Also, caller is responsible - * for ensuring the argv array is large enough. The maximum possible number - * of arguments added by this routine is (strlen(optstr) + 1) / 2. + * The caller is responsible for ensuring the argv array is large enough. The + * maximum possible number of arguments added by this routine is + * (strlen(optstr) + 1) / 2. * - * Since no current POSTGRES arguments require any quoting characters, - * we can use the simple-minded tactic of assuming each set of space- - * delimited characters is a separate argv element. - * - * If you don't like that, well, we *used* to pass the whole option string - * as ONE argument to execl(), which was even less intelligent... + * Because some option values can contain spaces we allow escaping using + * backslashes, with \\ representing a literal backslash. */ void pg_split_opts(char **argv, int *argcp, char *optstr) { + StringInfoData s; + + initStringInfo(&s); + while (*optstr) { + bool last_was_escape = false; + + resetStringInfo(&s); + + /* skip over leading space */ while (isspace((unsigned char) *optstr)) optstr++; + if (*optstr == '\0') break; - argv[(*argcp)++] = optstr; - while (*optstr && !isspace((unsigned char) *optstr)) + + /* + * Parse a single option + value, stopping at the first space, unless + * it's escaped. + */ + while (*optstr) + { + if (isspace(*optstr) && !last_was_escape) + break; + + if (!last_was_escape && *optstr == '\\') + last_was_escape = true; + else + { + last_was_escape = false; + appendStringInfoChar(&s, *optstr); + } + optstr++; - if (*optstr) - *optstr++ = '\0'; + } + + /* now store the option */ + argv[(*argcp)++] = pstrdup(s.data); } + resetStringInfo(&s); } /* @@ -981,7 +1006,6 @@ process_startup_options(Port *port, bool am_superuser) av[ac++] = "postgres"; - /* Note this mangles port->cmdline_options */ pg_split_opts(av, &ac, port->cmdline_options); av[ac] = NULL; -- 2.40.0