From b1732111f233bbb72788e92a627242ec28a85631 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 4 Aug 2009 21:56:09 +0000 Subject: [PATCH] Fix pg_dump to do the right thing when escaping the contents of large objects. 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 | 51 +++++++++++++++++++++++++++- src/bin/pg_dump/dumputils.h | 5 ++- src/bin/pg_dump/pg_backup_archiver.c | 17 +++++----- src/bin/pg_dump/pg_backup_archiver.h | 5 ++- src/bin/pg_dump/pg_backup_null.c | 17 +++++----- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index 404e1d3ed3..8037c1d7e1 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.47 2009/07/14 20:24:10 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.48 2009/08/04 21:56:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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. */ diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index 952c8b3653..3c56c3f9a4 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.24 2009/03/11 03:33:29 adunstan Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.25 2009/08/04 21:56:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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, diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index c5a75d3488..2ff282faa8 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.173 2009/07/21 21:46:10 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.174 2009/08/04 21:56:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; } diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index 4f7884862d..710dec019a 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.80 2009/07/21 21:46:10 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.81 2009/08/04 21:56:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 */ diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c index a89b012e6c..d9cb02446b 100644 --- a/src/bin/pg_dump/pg_backup_null.c +++ b/src/bin/pg_dump/pg_backup_null.c @@ -17,12 +17,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.21 2009/07/21 21:46:10 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.22 2009/08/04 21:56:09 tgl Exp $ * *------------------------------------------------------------------------- */ #include "pg_backup_archiver.h" +#include "dumputils.h" #include /* 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; } -- 2.40.0