Allow quotes to be escaped in recovery.conf, by doubling them. This patch
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 7 Apr 2010 10:58:49 +0000 (10:58 +0000)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 7 Apr 2010 10:58:49 +0000 (10:58 +0000)
also makes the parsing a little bit stricter, rejecting garbage after the
parameter value and values with missing ending quotes, for example.

doc/src/sgml/recovery-config.sgml
src/backend/access/transam/xlog.c

index 3a6a31cb53dcf47264c4c5113f2d83eaa9012576..85d3b7eb3fb6d142ba98923da63bdd7e4b591c06 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/recovery-config.sgml,v 2.3 2010/03/18 09:17:18 heikki Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/recovery-config.sgml,v 2.4 2010/04/07 10:58:49 heikki Exp $ -->
 
 <chapter Id="recovery-config">
   <title>Recovery Configuration</title>
       be changed once recovery has begun.
    </para>
 
+   <para>
+     Settings in <filename>recovery.conf</> are specified in the format
+     <literal>name = 'value'</>. One parameter is specified per line.
+     Hash marks (<literal>#</literal>) designate the rest of the
+     line as a comment.  To embed a single quote in a parameter
+     value, write two quotes (<literal>''</>).
+   </para>
+
   <sect1 id="archive-recovery-settings">
 
     <title>Archive recovery settings</title>
index 592419d356741a1371d1c7ecf25129ba55a24e2d..12392f8cfc010eab77a0db5e1af884b9b6c301ab 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.390 2010/04/07 06:12:52 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.391 2010/04/07 10:58:49 heikki Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4893,6 +4893,100 @@ str_time(pg_time_t tnow)
        return buf;
 }
 
+/*
+ * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+ * file. If the line is parsed successfully, returns true, false indicates
+ * syntax error. On success, *key_p and *value_p are set to the parameter
+ * name and value on the line, respectively. If the line is an empty line,
+ * consisting entirely of whitespace and comments, function returns true
+ * and *keyp_p and *value_p are set to NULL.
+ *
+ * The pointers returned in *key_p and *value_p point to an internal buffer
+ * that is valid only until the next call of parseRecoveryCommandFile().
+ */
+static bool
+parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
+{
+       char       *ptr;
+       char       *bufp;
+       char       *key;
+       char       *value;
+       static char *buf = NULL;
+
+       *key_p = *value_p = NULL;
+
+       /*
+        * Allocate the buffer on first use. It's used to hold both the
+        * parameter name and value.
+        */
+       if (buf == NULL)
+               buf = malloc(MAXPGPATH + 1);
+       bufp = buf;
+
+       /* Skip any whitespace at the beginning of line */
+       for (ptr = cmdline; *ptr; ptr++)
+       {
+               if (!isspace((unsigned char) *ptr))
+                       break;
+       }
+       /* Ignore empty lines */
+       if (*ptr == '\0' || *ptr == '#')
+               return true;
+
+       /* Read the parameter name */
+       key = bufp;
+       while (*ptr && !isspace((unsigned char) *ptr) &&
+                  *ptr != '=' && *ptr != '\'')
+               *(bufp++) = *(ptr++);
+       *(bufp++) = '\0';
+
+       /* Skip to the beginning quote of the parameter value */
+       ptr = strchr(ptr, '\'');
+       if (!ptr)
+               return false;
+       ptr++;
+
+       /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+       value = bufp;
+       for (;;)
+       {
+               if (*ptr == '\'')
+               {
+                       ptr++;
+                       if (*ptr == '\'')
+                               *(bufp++) = '\'';
+                       else
+                       {
+                               /* end of parameter */
+                               *bufp = '\0';
+                               break;
+                       }
+               }
+               else if (*ptr == '\0')
+                       return false;   /* unterminated quoted string */
+               else
+                       *(bufp++) = *ptr;
+
+               ptr++;
+       }
+       *(bufp++) = '\0';
+
+       /* Check that there's no garbage after the value */
+       while (*ptr)
+       {
+               if (*ptr == '#')
+                       break;
+               if (!isspace((unsigned char) *ptr))
+                       return false;
+               ptr++;
+       }
+
+       /* Success! */
+       *key_p = key;
+       *value_p = value;
+       return true;
+}
+
 /*
  * See if there is a recovery command file (recovery.conf), and if so
  * read in parameters for archive recovery and XLOG streaming.
@@ -4926,39 +5020,16 @@ readRecoveryCommandFile(void)
         */
        while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
        {
-               /* skip leading whitespace and check for # comment */
-               char       *ptr;
                char       *tok1;
                char       *tok2;
 
-               for (ptr = cmdline; *ptr; ptr++)
-               {
-                       if (!isspace((unsigned char) *ptr))
-                               break;
-               }
-               if (*ptr == '\0' || *ptr == '#')
-                       continue;
-
-               /* identify the quoted parameter value */
-               tok1 = strtok(ptr, "'");
-               if (!tok1)
-               {
-                       syntaxError = true;
-                       break;
-               }
-               tok2 = strtok(NULL, "'");
-               if (!tok2)
-               {
-                       syntaxError = true;
-                       break;
-               }
-               /* reparse to get just the parameter name */
-               tok1 = strtok(ptr, " \t=");
-               if (!tok1)
+               if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
                {
                        syntaxError = true;
                        break;
                }
+               if (tok1 == NULL)
+                       continue;
 
                if (strcmp(tok1, "restore_command") == 0)
                {