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.
This commit is contained in:
Andres Freund 2014-08-28 13:59:29 +02:00
parent e23014f3d4
commit 11a020eb6e
3 changed files with 43 additions and 17 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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))
optstr++;
if (*optstr)
*optstr++ = '\0';
/*
* 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++;
}
/* 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;