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.
This commit is contained in:
Tom Lane 2009-08-04 21:56:09 +00:00
parent 50d08346f3
commit b1732111f2
5 changed files with 75 additions and 20 deletions

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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. * Convert backend's version string into a number.
*/ */

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); PGconn *conn);
extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str, extern void appendStringLiteralDQ(PQExpBuffer buf, const char *str,
const char *dqprefix); 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 int parse_version(const char *versionString);
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems); extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
extern bool buildACLCommands(const char *name, const char *subname, extern bool buildACLCommands(const char *name, const char *subname,

View File

@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * 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 else
{ {
unsigned char *str; PQExpBuffer buf = createPQExpBuffer();
size_t len;
str = PQescapeBytea((const unsigned char *) AH->lo_buf, appendByteaLiteralAHX(buf,
AH->lo_buf_used, &len); (const unsigned char *) AH->lo_buf,
if (!str) AH->lo_buf_used,
die_horribly(AH, modulename, "out of memory\n"); AH);
/* Hack: turn off writingBlob so ahwrite doesn't recurse to here */ /* Hack: turn off writingBlob so ahwrite doesn't recurse to here */
AH->writingBlob = 0; 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; AH->writingBlob = 1;
free(str); destroyPQExpBuffer(buf);
} }
AH->lo_buf_used = 0; AH->lo_buf_used = 0;
} }

View File

@ -17,7 +17,7 @@
* *
* *
* IDENTIFICATION * 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) \ #define appendStringLiteralAHX(buf,str,AH) \
appendStringLiteral(buf, str, (AH)->public.encoding, (AH)->public.std_strings) 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 * Mandatory routines for each supported format
*/ */

View File

@ -17,12 +17,13 @@
* *
* *
* IDENTIFICATION * 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 "pg_backup_archiver.h"
#include "dumputils.h"
#include <unistd.h> /* for dup */ #include <unistd.h> /* for dup */
@ -101,16 +102,16 @@ _WriteBlobData(ArchiveHandle *AH, const void *data, size_t dLen)
{ {
if (dLen > 0) if (dLen > 0)
{ {
unsigned char *str; PQExpBuffer buf = createPQExpBuffer();
size_t len;
str = PQescapeBytea((const unsigned char *) data, dLen, &len); appendByteaLiteralAHX(buf,
if (!str) (const unsigned char *) data,
die_horribly(AH, NULL, "out of memory\n"); 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; return dLen;
} }