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:
parent
50d08346f3
commit
b1732111f2
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue