diff options
author | Andres Freund <andres@anarazel.de> | 2014-08-28 13:59:29 +0200 |
---|---|---|
committer | Andres Freund <andres@anarazel.de> | 2014-08-28 13:59:29 +0200 |
commit | 11a020eb6e4023a1570a2788ba22dd6aafbd02dc (patch) | |
tree | 9a33f530bc058929f6e823250111c238c6c1d267 | |
parent | e23014f3d40f7d2c23bc97207fd28efbe5ba102b (diff) | |
download | postgresql-11a020eb6e4023a1570a2788ba22dd6aafbd02dc.tar.gz postgresql-11a020eb6e4023a1570a2788ba22dd6aafbd02dc.zip |
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.
-rw-r--r-- | doc/src/sgml/protocol.sgml | 5 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 3 | ||||
-rw-r--r-- | 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 e519ff96b90..a8a09e4f0ec 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 (<literal>\</>). A literal backslash can be + passed by escaping it with another backslash + (i.e <literal>\\</>). </para> </listitem> </varlistentry> diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b190cf51136..14535c8b35a 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 a5b98217739..304be047892 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; |