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.
This commit is contained in:
parent
9de3f2843c
commit
2a1ef10115
@ -323,6 +323,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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -1252,20 +1252,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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "pg_backup_archiver.h"
|
||||
#include "dumputils.h"
|
||||
|
||||
#include <unistd.h> /* 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 lowrite(0, '%s');\n", str);
|
||||
ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);
|
||||
|
||||
free(str);
|
||||
destroyPQExpBuffer(buf);
|
||||
}
|
||||
return dLen;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user