Re: Quoting in recovery.conf - Mailing list pgsql-hackers

From Heikki Linnakangas
Subject Re: Quoting in recovery.conf
Date
Msg-id 4BBB65CB.7080806@enterprisedb.com
Whole thread Raw
In response to Re: Quoting in recovery.conf  (Simon Riggs <simon@2ndQuadrant.com>)
Responses Re: Quoting in recovery.conf  (Fujii Masao <masao.fujii@gmail.com>)
List pgsql-hackers
Simon Riggs wrote:
> On Tue, 2010-04-06 at 16:07 +0900, Fujii Masao wrote:
>> On Tue, Apr 6, 2010 at 3:47 PM, Heikki Linnakangas
>> <heikki.linnakangas@enterprisedb.com> wrote:
>>> To follow up on the discussion here:
>>>
>>> http://archives.postgresql.org/pgsql-docs/2010-02/msg00039.php
>>>
>>> It seems like a big oversight that there's no way to insert quotes in
>>> strings in recovery.conf. In the long run, the parsing should be done
>>> the same way as postgresql.conf, or the two files be merged altogether,
>>> but right now I think we should just add support for escaping quotes. I
>>> propose two quotes '' to mean a quote mark in the string, like in
>>> strings in SQL queries.
>> Agreed. This would be useful for users to specify the application_name
>> containing a space in the primary_conninfo, for example.
>
> +1

Ok, here's what I came up with.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index abdf4d8..73ef0f9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4894,6 +4894,100 @@ str_time(pg_time_t tnow)
 }

 /*
+ * 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)
         {

pgsql-hackers by date:

Previous
From: Magnus Hagander
Date:
Subject: Re: walreceiver is uninterruptible on win32
Next
From: Tom Lane
Date:
Subject: Re: Remaining Streaming Replication Open Items