Fix pg_dump to do the right thing when escaping the contents of large objects.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Aug 2009 21:56:09 +0000 (21:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Aug 2009 21:56:09 +0000 (21:56 +0000)
The previous implementation got it right in most cases but failed in one:
if you pg_dump into an archive with standard_conforming_strings enabled, then
pg_restore to a script file (not directly to a database), the script will set
standard_conforming_strings = on but then emit large object data as
nonstandardly-escaped strings.

At the moment the code is made to emit hex-format bytea strings when dumping
to a script file.  We might want to change to old-style escaping for backwards
compatibility, but that would be slower and bulkier.  If we do, it's just a
matter of reimplementing appendByteaLiteral().

This has been broken for a long time, but given the lack of field complaints
I'm not going to worry about back-patching.

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 178eb7c8d57095eb94baed792df18b062db7b623..5a3f9c34cef31cb7e536bc341e2b4f76091eff9b 100644 (file)
@@ -325,6 +325,55 @@ 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 *source = str;
+       char       *target;
+
+       static const char hextbl[] = "0123456789abcdef";
+
+       /*
+        * This implementation is hard-wired to produce hex-format output.
+        * We do not know the server version the output will be loaded into,
+        * so making an intelligent format choice is impossible.  It might be
+        * better to always use the old escaped format.
+        */
+       if (!enlargePQExpBuffer(buf, 2 * length + 5))
+               return;
+
+       target = buf->data + buf->len;
+       *target++ = '\'';
+       if (!std_strings)
+               *target++ = '\\';
+       *target++ = '\\';
+       *target++ = 'x';
+
+       while (length-- > 0)
+       {
+               unsigned char c = *source++;
+
+               *target++ = hextbl[(c >> 4) & 0xF];
+               *target++ = hextbl[c & 0xF];
+       }
+
+       /* Write the terminating quote and NUL character. */
+       *target++ = '\'';
+       *target = '\0';
+
+       buf->len = target - buf->data;
+}
+
+
 /*
  * Convert backend's version string into a number.
  */
index f493a41576974c80a054951d4f387c3c0e49fc40..f693922cf468990c304af03b7192be4987fe98e1 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 fe4841a94e355e22ff3420be8abb04fb886dc10b..012fb936b70ffceda9d18ca98d670c42b658827b 100644 (file)
@@ -1249,20 +1249,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 pg_catalog.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 5c779bdcef0dc63e5f6dc75e57154205c8c6fbec..21ba8c031d979c54dd0ab8f619aa17ac4ee1bc64 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 2ae7351cc156cf3ee7f403819daf96d161888f5d..fc48f1ba8d9ff31abba5f7d1b9055da561432b78 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 pg_catalog.lowrite(0, '%s');\n", str);
+               ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);
 
-               free(str);
+               destroyPQExpBuffer(buf);
        }
        return dLen;
 }