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)
        {