diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-01-21 16:22:27 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-01-21 16:22:27 -0500 |
commit | 1a1167a172ebc390437ffc5547a9f755fa56b731 (patch) | |
tree | d9eb2d21fd0e944529382edbfdb4a83ec85c8139 | |
parent | b414dde44cb91f891d1006f22b7196df54e055ea (diff) | |
download | postgresql-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.c | 78 | ||||
-rw-r--r-- | src/bin/pg_dump/dumputils.h | 3 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_backup_archiver.c | 15 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_backup_archiver.h | 3 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_backup_null.c | 15 |
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; } |