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.
|
* Convert backend's version string into a number.
|
||||||
*/
|
*/
|
||||||
|
@ -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,
|
||||||
|
@ -1252,20 +1252,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 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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#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 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…
x
Reference in New Issue
Block a user