Fix pg_restore to do the right thing when escaping large objects.
authorTom Lane <[email protected]>
Fri, 21 Jan 2011 21:22:21 +0000 (16:22 -0500)
committerTom Lane <[email protected]>
Fri, 21 Jan 2011 21:22:21 +0000 (16:22 -0500)
Specifically, this makes the workflow pg_dump -Fc -> pg_restore -> file
produce correct output for BLOBs when the source database has
standard_conforming_strings turned on.  It was already okay when that was
off, or if pg_restore was told to restore directly into a database.

This is a back-port of commit b1732111f233bbb72788e92a627242ec28a85631 of
2009-08-04, with additional changes to emit old-style escaped bytea data
instead of hex-style.  At the time, we had not heard of anyone encountering
the problem in the field, so I judged it not worth the risk of changing
back branches.  Now we do have a report, from Bosco Rama, so back-patch
into 8.2 through 8.4.  9.0 and up are okay already.

src/bin/pg_dump/dumputils.c
src/bin/pg_dump/dumputils.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_backup_archiver.h
src/bin/pg_dump/pg_backup_null.c

index e50e3e87fe26b8485bb235a6e510132c29bf96f7..345d6cf3c883b386e1635f8323da9f3c9be7dbbe 100644 (file)
@@ -323,6 +323,84 @@ appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
 }
 
 
+/*
+ * Convert a bytea value (presented as raw bytes) to an SQL string literal
+ * and append it to the given buffer.  We assume the specified
+ * standard_conforming_strings setting.
+ *
+ * This is needed in situations where we do not have a PGconn available.
+ * Where we do, PQescapeByteaConn is a better choice.
+ */
+void
+appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
+                                  bool std_strings)
+{
+       const unsigned char *vp;
+       unsigned char *rp;
+       size_t          i;
+       size_t          len;
+       size_t          bslash_len = (std_strings ? 1 : 2);
+
+       len = 2;                                        /* for the quote marks */
+       vp = str;
+       for (i = length; i > 0; i--, vp++)
+       {
+               if (*vp < 0x20 || *vp > 0x7e)
+                       len += bslash_len + 3;
+               else if (*vp == '\'')
+                       len += 2;
+               else if (*vp == '\\')
+                       len += bslash_len + bslash_len;
+               else
+                       len++;
+       }
+
+       if (!enlargePQExpBuffer(buf, len))
+               return;
+
+       rp = (unsigned char *) (buf->data + buf->len);
+       *rp++ = '\'';
+
+       vp = str;
+       for (i = length; i > 0; i--, vp++)
+       {
+               if (*vp < 0x20 || *vp > 0x7e)
+               {
+                       int                     val = *vp;
+
+                       if (!std_strings)
+                               *rp++ = '\\';
+                       *rp++ = '\\';
+                       *rp++ = (val >> 6) + '0';
+                       *rp++ = ((val >> 3) & 07) + '0';
+                       *rp++ = (val & 07) + '0';
+               }
+               else if (*vp == '\'')
+               {
+                       *rp++ = '\'';
+                       *rp++ = '\'';
+               }
+               else if (*vp == '\\')
+               {
+                       if (!std_strings)
+                       {
+                               *rp++ = '\\';
+                               *rp++ = '\\';
+                       }
+                       *rp++ = '\\';
+                       *rp++ = '\\';
+               }
+               else
+                       *rp++ = *vp;
+       }
+
+       *rp++ = '\'';
+       *rp = '\0';
+
+       buf->len = ((char *) rp) - buf->data;
+}
+
+
 /*
  * Convert backend's version string into a number.
  */
index 952c8b3653e653909117a23fa39fa9700ea4bfe9..e6c3d451a0bf5d8bb465234334a2735dea3fee31 100644 (file)
@@ -27,6 +27,9 @@ extern void appendStringLiteralConn(PQExpBuffer buf, const char *str,
                                                PGconn *conn);
 extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str,
                                          const char *dqprefix);
+extern void appendByteaLiteral(PQExpBuffer buf,
+                                                          const unsigned char *str, size_t length,
+                                                          bool std_strings);
 extern int     parse_version(const char *versionString);
 extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
 extern bool buildACLCommands(const char *name, const char *subname,
index c1d1eae2100b2be7b4263fc622efeacbf6ba83ca..98945310ce6f0a956b78b24e2a070a5eea711c3d 100644 (file)
@@ -1252,20 +1252,19 @@ dump_lo_buf(ArchiveHandle *AH)
        }
        else
        {
-               unsigned char *str;
-               size_t          len;
+               PQExpBuffer buf = createPQExpBuffer();
 
-               str = PQescapeBytea((const unsigned char *) AH->lo_buf,
-                                                       AH->lo_buf_used, &len);
-               if (!str)
-                       die_horribly(AH, modulename, "out of memory\n");
+               appendByteaLiteralAHX(buf,
+                                                         (const unsigned char *) AH->lo_buf,
+                                                         AH->lo_buf_used,
+                                                         AH);
 
                /* Hack: turn off writingBlob so ahwrite doesn't recurse to here */
                AH->writingBlob = 0;
-               ahprintf(AH, "SELECT lowrite(0, '%s');\n", str);
+               ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);
                AH->writingBlob = 1;
 
-               free(str);
+               destroyPQExpBuffer(buf);
        }
        AH->lo_buf_used = 0;
 }
index 2cde2dbda34c97eede0284b457c5c0ec07ad9734..0f440a1555665b010cbaeee87403c40bdbddb163 100644 (file)
@@ -342,6 +342,9 @@ extern bool checkSeek(FILE *fp);
 #define appendStringLiteralAHX(buf,str,AH) \
        appendStringLiteral(buf, str, (AH)->public.encoding, (AH)->public.std_strings)
 
+#define appendByteaLiteralAHX(buf,str,len,AH) \
+       appendByteaLiteral(buf, str, len, (AH)->public.std_strings)
+
 /*
  * Mandatory routines for each supported format
  */
index abff1138fc2e9a38d72a9962fe0905897f71cf64..634aa6f1759cd1dd42ceeb7e69412a96cbdced4c 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 #include "pg_backup_archiver.h"
+#include "dumputils.h"
 
 #include <unistd.h>                            /* for dup */
 
@@ -101,16 +102,16 @@ _WriteBlobData(ArchiveHandle *AH, const void *data, size_t dLen)
 {
        if (dLen > 0)
        {
-               unsigned char *str;
-               size_t          len;
+               PQExpBuffer buf = createPQExpBuffer();
 
-               str = PQescapeBytea((const unsigned char *) data, dLen, &len);
-               if (!str)
-                       die_horribly(AH, NULL, "out of memory\n");
+               appendByteaLiteralAHX(buf,
+                                                         (const unsigned char *) data,
+                                                         dLen,
+                                                         AH);
 
-               ahprintf(AH, "SELECT lowrite(0, '%s');\n", str);
+               ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);
 
-               free(str);
+               destroyPQExpBuffer(buf);
        }
        return dLen;
 }