]> granicus.if.org Git - postgresql/commitdiff
Allow escaping of option values for options passed at connection start.
authorAndres Freund <andres@anarazel.de>
Thu, 28 Aug 2014 11:59:29 +0000 (13:59 +0200)
committerAndres Freund <andres@anarazel.de>
Thu, 28 Aug 2014 11:59:29 +0000 (13:59 +0200)
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
src/backend/postmaster/postmaster.c
src/backend/utils/init/postinit.c

index e519ff96b90a4bee5c1476df2af46917bfeaa8d7..a8a09e4f0ec63f5fe586ccfc1f4caaa996e54baa 100644 (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>
index b190cf51136f4a9c753695c5c988ceb9946fb286..14535c8b35a6cf07011e41531ebd238aa41de5fe 100644 (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);
 
index a5b98217739e7bc1a7d331aa0169829ac1d93e14..304be047892c663f665477bbb3f69e71a22a124a 100644 (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))
+
+               /*
+                * 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;