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 set at backend start time might be listed. Such settings
will be applied during backend start (after parsing the will be applied during backend start (after parsing the
command-line options if any). The values will act as 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -4083,8 +4083,7 @@ BackendRun(Port *port)
/* /*
* Pass any backend switches specified with -o on the postmaster's own * Pass any backend switches specified with -o on the postmaster's own
* command line. We assume these are secure. (It's OK to mangle * command line. We assume these are secure.
* ExtraOptions now, since we're safely inside a subprocess.)
*/ */
pg_split_opts(av, &ac, ExtraOptions); 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 * 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 * The caller is responsible for ensuring the argv array is large enough. The
* for ensuring the argv array is large enough. The maximum possible number * maximum possible number of arguments added by this routine is
* of arguments added by this routine is (strlen(optstr) + 1) / 2. * (strlen(optstr) + 1) / 2.
* *
* Since no current POSTGRES arguments require any quoting characters, * Because some option values can contain spaces we allow escaping using
* we can use the simple-minded tactic of assuming each set of space- * backslashes, with \\ representing a literal backslash.
* 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...
*/ */
void void
pg_split_opts(char **argv, int *argcp, char *optstr) pg_split_opts(char **argv, int *argcp, char *optstr)
{ {
StringInfoData s;
initStringInfo(&s);
while (*optstr) while (*optstr)
{ {
bool last_was_escape = false;
resetStringInfo(&s);
/* skip over leading space */
while (isspace((unsigned char) *optstr)) while (isspace((unsigned char) *optstr))
optstr++; optstr++;
if (*optstr == '\0') if (*optstr == '\0')
break; 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++; 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"; av[ac++] = "postgres";
/* Note this mangles port->cmdline_options */
pg_split_opts(av, &ac, port->cmdline_options); pg_split_opts(av, &ac, port->cmdline_options);
av[ac] = NULL; av[ac] = NULL;