summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-01-21 16:22:27 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-01-21 16:22:27 -0500
commit1a1167a172ebc390437ffc5547a9f755fa56b731 (patch)
treed9eb2d21fd0e944529382edbfdb4a83ec85c8139
parentb414dde44cb91f891d1006f22b7196df54e055ea (diff)
downloadpostgresql-1a1167a172ebc390437ffc5547a9f755fa56b731.tar.gz
Fix pg_restore to do the right thing when escaping large objects.
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.
-rw-r--r--src/bin/pg_dump/dumputils.c78
-rw-r--r--src/bin/pg_dump/dumputils.h3
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.c15
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.h3
-rw-r--r--src/bin/pg_dump/pg_backup_null.c15
5 files changed, 99 insertions, 15 deletions
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index b750d1e900..83c600a340 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -275,6 +275,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.
*/
int
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 93f2729207..ec6382b274 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -26,6 +26,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 *type,
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 880fbf1c6c..c8db8f3dea 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1090,20 +1090,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;
}
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index d8520ec9d6..e1e63c06b6 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -323,6 +323,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
*/
diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c
index f31a173ce1..84bbf3ac6b 100644
--- a/src/bin/pg_dump/pg_backup_null.c
+++ b/src/bin/pg_dump/pg_backup_null.c
@@ -23,6 +23,7 @@
*/
#include "pg_backup_archiver.h"
+#include "dumputils.h"
#include <unistd.h> /* for dup */
@@ -98,16 +99,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;
}