pgsql-fix-pg_dump-blob-privs.5.patch
application/octect-stream
Filename: pgsql-fix-pg_dump-blob-privs.5.patch
Type: application/octect-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/bin/pg_dump/pg_backup_archiver.c | 31 | 0 |
| src/bin/pg_dump/pg_backup_archiver.h | 2 | 0 |
| src/bin/pg_dump/pg_backup_custom.c | 5 | 0 |
| src/bin/pg_dump/pg_backup_db.c | 13 | 0 |
| src/bin/pg_dump/pg_backup_files.c | 24 | 0 |
| src/bin/pg_dump/pg_backup_null.c | 8 | 0 |
| src/bin/pg_dump/pg_backup_tar.c | 40 | 0 |
| src/bin/pg_dump/pg_dump.c | 124 | 0 |
| src/bin/pg_dump/pg_dump.h | 9 | 0 |
| src/bin/pg_dump/pg_dump_sort.c | 5 | 0 |
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
***************
*** 329,335 **** RestoreArchive(Archive *AHX, RestoreOptions *ropt)
AH->currentTE = te;
reqs = _tocEntryRequired(te, ropt, false /* needn't drop ACLs */ );
! if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt)
{
/* We want the schema */
ahlog(AH, 1, "dropping %s %s\n", te->desc, te->tag);
--- 329,336 ----
AH->currentTE = te;
reqs = _tocEntryRequired(te, ropt, false /* needn't drop ACLs */ );
! if (((reqs & REQ_SCHEMA) != 0 ||
! strcmp(te->desc, "LARGE OBJECT") == 0) && te->dropStmt)
{
/* We want the schema */
ahlog(AH, 1, "dropping %s %s\n", te->desc, te->tag);
***************
*** 448,454 **** restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
defnDumped = false;
! if ((reqs & REQ_SCHEMA) != 0) /* We want the schema */
{
ahlog(AH, 1, "creating %s %s\n", te->desc, te->tag);
--- 449,456 ----
defnDumped = false;
! if ((reqs & REQ_SCHEMA) != 0 && /* We want the schema */
! strcmp(te->desc, "LARGE OBJECT") != 0) /* or large object */
{
ahlog(AH, 1, "creating %s %s\n", te->desc, te->tag);
***************
*** 519,525 **** restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
{
_printTocEntry(AH, te, ropt, true, false);
! if (strcmp(te->desc, "BLOBS") == 0 ||
strcmp(te->desc, "BLOB COMMENTS") == 0)
{
ahlog(AH, 1, "restoring %s\n", te->desc);
--- 521,528 ----
{
_printTocEntry(AH, te, ropt, true, false);
! if (strcmp(te->desc, "LARGE OBJECT") == 0 ||
! strcmp(te->desc, "BLOBS") == 0 ||
strcmp(te->desc, "BLOB COMMENTS") == 0)
{
ahlog(AH, 1, "restoring %s\n", te->desc);
***************
*** 903,909 **** EndRestoreBlobs(ArchiveHandle *AH)
* Called by a format handler to initiate restoration of a blob
*/
void
! StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
{
Oid loOid;
--- 906,912 ----
* Called by a format handler to initiate restoration of a blob
*/
void
! StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool cleanup, bool compat)
{
Oid loOid;
***************
*** 914,938 **** StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
ahlog(AH, 2, "restoring large object with OID %u\n", oid);
! if (drop)
! DropBlobIfExists(AH, oid);
if (AH->connection)
{
! loOid = lo_create(AH->connection, oid);
! if (loOid == 0 || loOid != oid)
! die_horribly(AH, modulename, "could not create large object %u\n",
! oid);
!
AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
if (AH->loFd == -1)
die_horribly(AH, modulename, "could not open large object\n");
}
! else
{
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
oid, INV_WRITE);
}
AH->writingBlob = 1;
}
--- 917,947 ----
ahlog(AH, 2, "restoring large object with OID %u\n", oid);
! if (cleanup)
! CleanupBlobIfExists(AH, oid, compat);
if (AH->connection)
{
! if (compat)
! {
! loOid = lo_create(AH->connection, oid);
! if (loOid == 0 || loOid != oid)
! die_horribly(AH, modulename, "could not create large object %u\n", oid);
! }
AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
if (AH->loFd == -1)
die_horribly(AH, modulename, "could not open large object\n");
}
! else if (compat)
{
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
oid, INV_WRITE);
}
+ else
+ {
+ ahprintf(AH, "SELECT pg_catalog.lo_open('%u', %d);\n",
+ oid, INV_WRITE);
+ }
AH->writingBlob = 1;
}
***************
*** 1940,1946 **** WriteDataChunks(ArchiveHandle *AH)
AH->currToc = te;
/* printf("Writing data for %d (%x)\n", te->id, te); */
! if (strcmp(te->desc, "BLOBS") == 0)
{
startPtr = AH->StartBlobsPtr;
endPtr = AH->EndBlobsPtr;
--- 1949,1956 ----
AH->currToc = te;
/* printf("Writing data for %d (%x)\n", te->id, te); */
! if (strcmp(te->desc, "BLOBS") == 0 ||
! strcmp(te->desc, "LARGE OBJECT") == 0)
{
startPtr = AH->StartBlobsPtr;
endPtr = AH->EndBlobsPtr;
***************
*** 2685,2690 **** _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
--- 2695,2707 ----
return;
}
+ /* objects named by just an identifier */
+ if (strcmp(type, "LARGE OBJECT") == 0)
+ {
+ appendPQExpBuffer(buf, "%s %s", type, te->tag);
+ return;
+ }
+
/*
* These object types require additional decoration. Fortunately, the
* information needed is exactly what's in the DROP command.
***************
*** 2828,2833 **** _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
--- 2845,2851 ----
strcmp(te->desc, "DATABASE") == 0 ||
strcmp(te->desc, "DOMAIN") == 0 ||
strcmp(te->desc, "FUNCTION") == 0 ||
+ strcmp(te->desc, "LARGE OBJECT") == 0 ||
strcmp(te->desc, "OPERATOR") == 0 ||
strcmp(te->desc, "OPERATOR CLASS") == 0 ||
strcmp(te->desc, "OPERATOR FAMILY") == 0 ||
*** a/src/bin/pg_dump/pg_backup_archiver.h
--- b/src/bin/pg_dump/pg_backup_archiver.h
***************
*** 359,365 **** int ReadOffset(ArchiveHandle *, pgoff_t *);
size_t WriteOffset(ArchiveHandle *, pgoff_t, int);
extern void StartRestoreBlobs(ArchiveHandle *AH);
! extern void StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop);
extern void EndRestoreBlob(ArchiveHandle *AH, Oid oid);
extern void EndRestoreBlobs(ArchiveHandle *AH);
--- 359,365 ----
size_t WriteOffset(ArchiveHandle *, pgoff_t, int);
extern void StartRestoreBlobs(ArchiveHandle *AH);
! extern void StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool cleanup, bool compat);
extern void EndRestoreBlob(ArchiveHandle *AH, Oid oid);
extern void EndRestoreBlobs(ArchiveHandle *AH);
***************
*** 371,377 **** extern void InitArchiveFmt_Tar(ArchiveHandle *AH);
extern bool isValidTarHeader(char *header);
extern int ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *newUser);
! extern void DropBlobIfExists(ArchiveHandle *AH, Oid oid);
int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH);
int ahprintf(ArchiveHandle *AH, const char *fmt,...) __attribute__((format(printf, 2, 3)));
--- 371,377 ----
extern bool isValidTarHeader(char *header);
extern int ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *newUser);
! extern void CleanupBlobIfExists(ArchiveHandle *AH, Oid oid, bool compat);
int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH);
int ahprintf(ArchiveHandle *AH, const char *fmt,...) __attribute__((format(printf, 2, 3)));
*** a/src/bin/pg_dump/pg_backup_custom.c
--- b/src/bin/pg_dump/pg_backup_custom.c
***************
*** 54,60 **** static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
! static void _LoadBlobs(ArchiveHandle *AH, bool drop);
static void _Clone(ArchiveHandle *AH);
static void _DeClone(ArchiveHandle *AH);
--- 54,60 ----
static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
! static void _LoadBlobs(ArchiveHandle *AH, bool cleanup, bool compat);
static void _Clone(ArchiveHandle *AH);
static void _DeClone(ArchiveHandle *AH);
***************
*** 498,504 **** _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
break;
case BLK_BLOBS:
! _LoadBlobs(AH, ropt->dropSchema);
break;
default: /* Always have a default */
--- 498,505 ----
break;
case BLK_BLOBS:
! _LoadBlobs(AH, ropt->dropSchema,
! strcmp(te->desc, "BLOBS") == 0 ? true : false);
break;
default: /* Always have a default */
***************
*** 619,625 **** _PrintData(ArchiveHandle *AH)
}
static void
! _LoadBlobs(ArchiveHandle *AH, bool drop)
{
Oid oid;
--- 620,626 ----
}
static void
! _LoadBlobs(ArchiveHandle *AH, bool cleanup, bool compat)
{
Oid oid;
***************
*** 628,634 **** _LoadBlobs(ArchiveHandle *AH, bool drop)
oid = ReadInt(AH);
while (oid != 0)
{
! StartRestoreBlob(AH, oid, drop);
_PrintData(AH);
EndRestoreBlob(AH, oid);
oid = ReadInt(AH);
--- 629,635 ----
oid = ReadInt(AH);
while (oid != 0)
{
! StartRestoreBlob(AH, oid, cleanup, compat);
_PrintData(AH);
EndRestoreBlob(AH, oid);
oid = ReadInt(AH);
*** a/src/bin/pg_dump/pg_backup_db.c
--- b/src/bin/pg_dump/pg_backup_db.c
***************
*** 12,17 ****
--- 12,18 ----
#include "pg_backup_db.h"
#include "dumputils.h"
+ #include "libpq/libpq-fs.h"
#include <unistd.h>
***************
*** 653,672 **** CommitTransaction(ArchiveHandle *AH)
}
void
! DropBlobIfExists(ArchiveHandle *AH, Oid oid)
{
! /* Call lo_unlink only if exists to avoid not-found error. */
! if (PQserverVersion(AH->connection) >= 80500)
! {
ahprintf(AH, "SELECT pg_catalog.lo_unlink(oid) "
! "FROM pg_catalog.pg_largeobject_metadata "
! "WHERE oid = %u;\n", oid);
! }
else
! {
! ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n",
! oid, oid);
! }
}
static bool
--- 654,674 ----
}
void
! CleanupBlobIfExists(ArchiveHandle *AH, Oid oid, bool compat)
{
! if (AH->connection &&
! PQserverVersion(AH->connection) < 80500)
! die_horribly(AH, NULL,
! "could not restore large object into older server");
!
! if (compat)
ahprintf(AH, "SELECT pg_catalog.lo_unlink(oid) "
! "FROM pg_catalog.pg_largeobject_metadata "
! "WHERE oid = %u;\n", oid);
else
! ahprintf(AH, "SELECT pg_catalog.lo_truncate(pg_catalog.lo_open(oid, %d), 0) "
! "FROM pg_catalog.pg_largeobject_metadata "
! "WHERE oid = %u;\n", INV_READ, oid);
}
static bool
*** a/src/bin/pg_dump/pg_backup_files.c
--- b/src/bin/pg_dump/pg_backup_files.c
***************
*** 41,50 **** static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
- static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
- static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
#define K_STD_BUF_SIZE 1024
--- 41,48 ----
***************
*** 67,72 **** typedef struct
--- 65,72 ----
static const char *modulename = gettext_noop("file archiver");
static void _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt);
+ static void _LoadLargeObject(ArchiveHandle *AH, TocEntry *te,
+ RestoreOptions *ropt);
static void _getBlobTocEntry(ArchiveHandle *AH, Oid *oid, char *fname);
/*
***************
*** 93,102 **** InitArchiveFmt_Files(ArchiveHandle *AH)
AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc;
! AH->StartBlobsPtr = _StartBlobs;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
! AH->EndBlobsPtr = _EndBlobs;
AH->ClonePtr = NULL;
AH->DeClonePtr = NULL;
--- 93,102 ----
AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc;
! AH->StartBlobsPtr = NULL;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
! AH->EndBlobsPtr = NULL;
AH->ClonePtr = NULL;
AH->DeClonePtr = NULL;
***************
*** 331,336 **** _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
--- 331,338 ----
if (strcmp(te->desc, "BLOBS") == 0)
_LoadBlobs(AH, ropt);
+ else if (strcmp(te->desc, "LARGE OBJECT") == 0)
+ _LoadLargeObject(AH, te, ropt);
else
_PrintFileData(AH, tctx->filename, ropt);
}
***************
*** 382,388 **** _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
while (oid != 0)
{
! StartRestoreBlob(AH, oid, ropt->dropSchema);
_PrintFileData(AH, fname, ropt);
EndRestoreBlob(AH, oid);
_getBlobTocEntry(AH, &oid, fname);
--- 384,390 ----
while (oid != 0)
{
! StartRestoreBlob(AH, oid, ropt->dropSchema, true);
_PrintFileData(AH, fname, ropt);
EndRestoreBlob(AH, oid);
_getBlobTocEntry(AH, &oid, fname);
***************
*** 394,399 **** _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
--- 396,418 ----
EndRestoreBlobs(AH);
}
+ static void
+ _LoadLargeObject(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
+ {
+ Oid oid = atooid(te->tag);
+ char fname[K_STD_BUF_SIZE];
+
+ snprintf(fname, sizeof(fname), "blob_%u.dat%s",
+ oid, (AH->compression ? ".gz" : ""));
+
+ StartRestoreBlobs(AH);
+
+ StartRestoreBlob(AH, oid, ropt->dropSchema, false);
+ _PrintFileData(AH, fname, ropt);
+ EndRestoreBlob(AH, oid);
+
+ EndRestoreBlobs(AH);
+ }
static int
_WriteByte(ArchiveHandle *AH, const int i)
***************
*** 468,496 **** _CloseArchive(ArchiveHandle *AH)
*/
/*
- * Called by the archiver when starting to save all BLOB DATA (not schema).
- * This routine should save whatever format-specific information is needed
- * to read the BLOBs back into memory.
- *
- * It is called just prior to the dumper's DataDumper routine.
- *
- * Optional, but strongly recommended.
- */
- static void
- _StartBlobs(ArchiveHandle *AH, TocEntry *te)
- {
- lclContext *ctx = (lclContext *) AH->formatData;
- char fname[K_STD_BUF_SIZE];
-
- sprintf(fname, "blobs.toc");
- ctx->blobToc = fopen(fname, PG_BINARY_W);
-
- if (ctx->blobToc == NULL)
- die_horribly(AH, modulename,
- "could not open large object TOC for output: %s\n", strerror(errno));
- }
-
- /*
* Called by the archiver when the dumper calls StartBlob.
*
* Mandatory.
--- 487,492 ----
***************
*** 517,524 **** _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
sprintf(fmode, "wb%d", AH->compression);
sprintf(fname, "blob_%u.dat%s", oid, sfx);
- fprintf(ctx->blobToc, "%u %s\n", oid, fname);
-
#ifdef HAVE_LIBZ
tctx->FH = gzopen(fname, fmode);
#else
--- 513,518 ----
***************
*** 543,562 **** _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
if (GZCLOSE(tctx->FH) != 0)
die_horribly(AH, modulename, "could not close large object file\n");
}
-
- /*
- * Called by the archiver when finishing saving all BLOB DATA.
- *
- * Optional.
- */
- static void
- _EndBlobs(ArchiveHandle *AH, TocEntry *te)
- {
- lclContext *ctx = (lclContext *) AH->formatData;
-
- /* Write out a fake zero OID to mark end-of-blobs. */
- /* WriteInt(AH, 0); */
-
- if (fclose(ctx->blobToc) != 0)
- die_horribly(AH, modulename, "could not close large object TOC file: %s\n", strerror(errno));
- }
--- 537,539 ----
*** a/src/bin/pg_dump/pg_backup_null.c
--- b/src/bin/pg_dump/pg_backup_null.c
***************
*** 147,160 **** _StartBlobs(ArchiveHandle *AH, TocEntry *te)
static void
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
{
if (oid == 0)
die_horribly(AH, NULL, "invalid OID for large object\n");
if (AH->ropt->dropSchema)
! DropBlobIfExists(AH, oid);
! ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
! oid, INV_WRITE);
AH->WriteDataPtr = _WriteBlobData;
}
--- 147,165 ----
static void
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
{
+ bool compat = (strcmp(te->desc, "BLOBS") == 0 ? true : false);
+
if (oid == 0)
die_horribly(AH, NULL, "invalid OID for large object\n");
if (AH->ropt->dropSchema)
! CleanupBlobIfExists(AH, oid, compat);
! if (compat)
! ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
! oid, INV_WRITE);
! else
! ahprintf(AH, "SELECT pg_catalog.lo_open(%u, %d);\n", oid, INV_WRITE);
AH->WriteDataPtr = _WriteBlobData;
}
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
***************
*** 44,53 **** static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
- static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
- static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
#define K_STD_BUF_SIZE 1024
--- 44,51 ----
***************
*** 101,106 **** typedef struct
--- 99,106 ----
static const char *modulename = gettext_noop("tar archiver");
static void _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt);
+ static void _LoadLargeObject(ArchiveHandle *AH, TocEntry *te,
+ RestoreOptions *ropt);
static TAR_MEMBER *tarOpen(ArchiveHandle *AH, const char *filename, char mode);
static void tarClose(ArchiveHandle *AH, TAR_MEMBER *TH);
***************
*** 145,154 **** InitArchiveFmt_Tar(ArchiveHandle *AH)
AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc;
! AH->StartBlobsPtr = _StartBlobs;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
! AH->EndBlobsPtr = _EndBlobs;
AH->ClonePtr = NULL;
AH->DeClonePtr = NULL;
--- 145,154 ----
AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc;
! AH->StartBlobsPtr = NULL;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
! AH->EndBlobsPtr = NULL;
AH->ClonePtr = NULL;
AH->DeClonePtr = NULL;
***************
*** 697,702 **** _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
--- 697,704 ----
if (strcmp(te->desc, "BLOBS") == 0)
_LoadBlobs(AH, ropt);
+ else if (strcmp(te->desc, "LARGE OBJECT") == 0)
+ _LoadLargeObject(AH, te, ropt);
else
_PrintFileData(AH, tctx->filename, ropt);
}
***************
*** 725,731 **** _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
{
ahlog(AH, 1, "restoring large object OID %u\n", oid);
! StartRestoreBlob(AH, oid, ropt->dropSchema);
while ((cnt = tarRead(buf, 4095, th)) > 0)
{
--- 727,733 ----
{
ahlog(AH, 1, "restoring large object OID %u\n", oid);
! StartRestoreBlob(AH, oid, ropt->dropSchema, true);
while ((cnt = tarRead(buf, 4095, th)) > 0)
{
***************
*** 756,761 **** _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
--- 758,796 ----
EndRestoreBlobs(AH);
}
+ static void
+ _LoadLargeObject(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
+ {
+ Oid oid = atooid(te->tag);
+ char fname[K_STD_BUF_SIZE];
+ char buf[4096];
+ TAR_MEMBER *th;
+ size_t cnt;
+
+ StartRestoreBlobs(AH);
+
+ snprintf(fname, sizeof(fname), "blob_%u.dat", oid);
+
+ th = tarOpen(AH, fname, 'r');
+ if (th == NULL)
+ die_horribly(AH, modulename, "could not open input file \"%s\": %s\n",
+ fname, strerror(errno));
+
+ ahlog(AH, 1, "restoring large object OID %u\n", oid);
+
+ StartRestoreBlob(AH, oid, ropt->dropSchema, false);
+
+ while ((cnt = tarRead(buf, 4095, th)) > 0)
+ {
+ buf[cnt] = '\0';
+ ahwrite(buf, 1, cnt, AH);
+ }
+ EndRestoreBlob(AH, oid);
+
+ tarClose(AH, th);
+
+ EndRestoreBlobs(AH);
+ }
static int
_WriteByte(ArchiveHandle *AH, const int i)
***************
*** 894,919 **** _scriptOut(ArchiveHandle *AH, const void *buf, size_t len)
*/
/*
- * Called by the archiver when starting to save all BLOB DATA (not schema).
- * This routine should save whatever format-specific information is needed
- * to read the BLOBs back into memory.
- *
- * It is called just prior to the dumper's DataDumper routine.
- *
- * Optional, but strongly recommended.
- *
- */
- static void
- _StartBlobs(ArchiveHandle *AH, TocEntry *te)
- {
- lclContext *ctx = (lclContext *) AH->formatData;
- char fname[K_STD_BUF_SIZE];
-
- sprintf(fname, "blobs.toc");
- ctx->blobToc = tarOpen(AH, fname, 'w');
- }
-
- /*
* Called by the archiver when the dumper calls StartBlob.
*
* Mandatory.
--- 929,934 ----
***************
*** 923,929 **** _StartBlobs(ArchiveHandle *AH, TocEntry *te)
static void
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
{
- lclContext *ctx = (lclContext *) AH->formatData;
lclTocEntry *tctx = (lclTocEntry *) te->formatData;
char fname[255];
char *sfx;
--- 938,943 ----
***************
*** 938,945 **** _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
sprintf(fname, "blob_%u.dat%s", oid, sfx);
- tarPrintf(AH, ctx->blobToc, "%u %s\n", oid, fname);
-
tctx->TH = tarOpen(AH, fname, 'w');
}
--- 952,957 ----
***************
*** 957,981 **** _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
tarClose(AH, tctx->TH);
}
- /*
- * Called by the archiver when finishing saving all BLOB DATA.
- *
- * Optional.
- *
- */
- static void
- _EndBlobs(ArchiveHandle *AH, TocEntry *te)
- {
- lclContext *ctx = (lclContext *) AH->formatData;
-
- /* Write out a fake zero OID to mark end-of-blobs. */
- /* WriteInt(AH, 0); */
-
- tarClose(AH, ctx->blobToc);
- }
-
-
-
/*------------
* TAR Support
*------------
--- 969,974 ----
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 190,198 **** static void selectSourceSchema(const char *schemaName);
static char *getFormattedTypeName(Oid oid, OidOptions opts);
static char *myFormatType(const char *typname, int32 typmod);
static const char *fmtQualifiedId(const char *schema, const char *id);
! static bool hasBlobs(Archive *AH);
! static int dumpBlobs(Archive *AH, void *arg);
! static int dumpBlobComments(Archive *AH, void *arg);
static void dumpDatabase(Archive *AH);
static void dumpEncoding(Archive *AH);
static void dumpStdStrings(Archive *AH);
--- 190,198 ----
static char *getFormattedTypeName(Oid oid, OidOptions opts);
static char *myFormatType(const char *typname, int32 typmod);
static const char *fmtQualifiedId(const char *schema, const char *id);
! static void getLargeObjects(Archive *AH);
! static void dumpLargeObject(Archive *AH, LargeObjectInfo *loinfo);
! static int dumpLargeObjectData(Archive *AH, void *arg);
static void dumpDatabase(Archive *AH);
static void dumpEncoding(Archive *AH);
static void dumpStdStrings(Archive *AH);
***************
*** 701,725 **** main(int argc, char **argv)
getTableDataFKConstraints();
}
! if (outputBlobs && hasBlobs(g_fout))
! {
! /* Add placeholders to allow correct sorting of blobs */
! DumpableObject *blobobj;
! DumpableObject *blobcobj;
!
! blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
! blobobj->objType = DO_BLOBS;
! blobobj->catId = nilCatalogId;
! AssignDumpId(blobobj);
! blobobj->name = strdup("BLOBS");
!
! blobcobj = (DumpableObject *) malloc(sizeof(DumpableObject));
! blobcobj->objType = DO_BLOB_COMMENTS;
! blobcobj->catId = nilCatalogId;
! AssignDumpId(blobcobj);
! blobcobj->name = strdup("BLOB COMMENTS");
! addObjectDependency(blobcobj, blobobj->dumpId);
! }
/*
* Collect dependency data to assist in ordering the objects.
--- 701,708 ----
getTableDataFKConstraints();
}
! if (outputBlobs)
! getLargeObjects(g_fout);
/*
* Collect dependency data to assist in ordering the objects.
***************
*** 1936,2183 **** dumpStdStrings(Archive *AH)
destroyPQExpBuffer(qry);
}
-
/*
! * hasBlobs:
! * Test whether database contains any large objects
*/
! static bool
! hasBlobs(Archive *AH)
{
! bool result;
! const char *blobQry;
! PGresult *res;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! /* Check for BLOB OIDs */
if (AH->remoteVersion >= 80500)
! blobQry = "SELECT oid FROM pg_largeobject_metadata LIMIT 1";
else if (AH->remoteVersion >= 70100)
! blobQry = "SELECT loid FROM pg_largeobject LIMIT 1";
else
! blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1";
! res = PQexec(g_conn, blobQry);
! check_sql_result(res, g_conn, blobQry, PGRES_TUPLES_OK);
! result = PQntuples(res) > 0;
PQclear(res);
! return result;
}
/*
! * dumpBlobs:
! * dump all blobs
*/
! static int
! dumpBlobs(Archive *AH, void *arg)
{
! const char *blobQry;
! const char *blobFetchQry;
! PGresult *res;
! char buf[LOBBUFSIZE];
! int i;
! int cnt;
!
! if (g_verbose)
! write_msg(NULL, "saving large objects\n");
! /* Make sure we are in proper schema */
! selectSourceSchema("pg_catalog");
!
! /* Cursor to get all BLOB OIDs */
! if (AH->remoteVersion >= 80500)
! blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata";
! else if (AH->remoteVersion >= 70100)
! blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
! else
! blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'";
! res = PQexec(g_conn, blobQry);
! check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
!
! /* Command to fetch from cursor */
! blobFetchQry = "FETCH 1000 IN bloboid";
!
! do
{
! PQclear(res);
!
! /* Do a fetch */
! res = PQexec(g_conn, blobFetchQry);
! check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
!
! /* Process the tuples, if any */
! for (i = 0; i < PQntuples(res); i++)
! {
! Oid blobOid;
! int loFd;
!
! blobOid = atooid(PQgetvalue(res, i, 0));
! /* Open the BLOB */
! loFd = lo_open(g_conn, blobOid, INV_READ);
! if (loFd == -1)
! {
! write_msg(NULL, "dumpBlobs(): could not open large object: %s",
! PQerrorMessage(g_conn));
! exit_nicely();
! }
!
! StartBlob(AH, blobOid);
!
! /* Now read it in chunks, sending data to archive */
! do
! {
! cnt = lo_read(g_conn, loFd, buf, LOBBUFSIZE);
! if (cnt < 0)
! {
! write_msg(NULL, "dumpBlobs(): error reading large object: %s",
! PQerrorMessage(g_conn));
! exit_nicely();
! }
!
! WriteData(AH, buf, cnt);
! } while (cnt > 0);
!
! lo_close(g_conn, loFd);
!
! EndBlob(AH, blobOid);
! }
! } while (PQntuples(res) > 0);
! PQclear(res);
! return 1;
}
- /*
- * dumpBlobComments
- * dump all blob properties.
- * It has "BLOB COMMENTS" tag due to the historical reason, but note
- * that it is the routine to dump all the properties of blobs.
- *
- * Since we don't provide any way to be selective about dumping blobs,
- * there's no need to be selective about their comments either. We put
- * all the comments into one big TOC entry.
- */
static int
! dumpBlobComments(Archive *AH, void *arg)
{
! const char *blobQry;
! const char *blobFetchQry;
! PQExpBuffer cmdQry = createPQExpBuffer();
! PGresult *res;
! int i;
!
! if (g_verbose)
! write_msg(NULL, "saving large object properties\n");
!
! /* Make sure we are in proper schema */
! selectSourceSchema("pg_catalog");
! /* Cursor to get all BLOB comments */
! if (AH->remoteVersion >= 80500)
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
! "obj_description(oid, 'pg_largeobject'), "
! "pg_get_userbyid(lomowner), lomacl "
! "FROM pg_largeobject_metadata";
! else if (AH->remoteVersion >= 70300)
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
! "obj_description(loid, 'pg_largeobject'), NULL, NULL "
! "FROM (SELECT DISTINCT loid FROM "
! "pg_description d JOIN pg_largeobject l ON (objoid = loid) "
! "WHERE classoid = 'pg_largeobject'::regclass) ss";
! else if (AH->remoteVersion >= 70200)
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
! "obj_description(loid, 'pg_largeobject'), NULL, NULL "
! "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
! else if (AH->remoteVersion >= 70100)
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
! "obj_description(loid), NULL, NULL "
! "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
! else
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
! " ( "
! " SELECT description "
! " FROM pg_description pd "
! " WHERE pd.objoid=pc.oid "
! " ), NULL, NULL "
! "FROM pg_class pc WHERE relkind = 'l'";
!
! res = PQexec(g_conn, blobQry);
! check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
! /* Command to fetch from cursor */
! blobFetchQry = "FETCH 100 IN blobcmt";
do
{
! PQclear(res);
!
! /* Do a fetch */
! res = PQexec(g_conn, blobFetchQry);
! check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
!
! /* Process the tuples, if any */
! for (i = 0; i < PQntuples(res); i++)
{
! Oid blobOid = atooid(PQgetvalue(res, i, 0));
! char *lo_comment = PQgetvalue(res, i, 1);
! char *lo_owner = PQgetvalue(res, i, 2);
! char *lo_acl = PQgetvalue(res, i, 3);
! char lo_name[32];
!
! resetPQExpBuffer(cmdQry);
!
! /* comment on the blob */
! if (!PQgetisnull(res, i, 1))
! {
! appendPQExpBuffer(cmdQry,
! "COMMENT ON LARGE OBJECT %u IS ", blobOid);
! appendStringLiteralAH(cmdQry, lo_comment, AH);
! appendPQExpBuffer(cmdQry, ";\n");
! }
!
! /* dump blob ownership, if necessary */
! if (!PQgetisnull(res, i, 2))
! {
! appendPQExpBuffer(cmdQry,
! "ALTER LARGE OBJECT %u OWNER TO %s;\n",
! blobOid, lo_owner);
! }
!
! /* dump blob privileges, if necessary */
! if (!PQgetisnull(res, i, 3) &&
! !dataOnly && !aclsSkip)
! {
! snprintf(lo_name, sizeof(lo_name), "%u", blobOid);
! if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT",
! lo_acl, lo_owner, "",
! AH->remoteVersion, cmdQry))
! {
! write_msg(NULL, "could not parse ACL (%s) for "
! "large object %u", lo_acl, blobOid);
! exit_nicely();
! }
! }
!
! if (cmdQry->len > 0)
! {
! appendPQExpBuffer(cmdQry, "\n");
! archputs(cmdQry->data, AH);
! }
}
- } while (PQntuples(res) > 0);
! PQclear(res);
! archputs("\n", AH);
! destroyPQExpBuffer(cmdQry);
return 1;
}
--- 1919,2077 ----
destroyPQExpBuffer(qry);
}
/*
! * getLargeObjects
! * Gather the information of large obejcts.
*/
! static void
! getLargeObjects(Archive *AH)
{
! PQExpBuffer loQry = createPQExpBuffer();
! LargeObjectInfo *loinfo;
! PGresult *res;
! int i;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
! /* Collect large object metadata */
if (AH->remoteVersion >= 80500)
! appendPQExpBuffer(loQry,
! "SELECT oid, (%s lomowner), lomacl,"
! " obj_description(oid, 'pg_largeobject')"
! " FROM pg_largeobject_metadata",
! username_subquery);
! else if (AH->remoteVersion >= 70200)
! appendPQExpBuffer(loQry,
! "SELECT DISTINCT loid, NULL, NULL, "
! " obj_description(loid, 'pg_largeobject')"
! " FROM pg_largeobject");
else if (AH->remoteVersion >= 70100)
! appendPQExpBuffer(loQry,
! "SELECT DISTINCT loid, NULL, NULL, "
! " obj_description(loid)"
! " FROM pg_largeobject");
else
! appendPQExpBuffer(loQry,
! "SELECT DISTINCT oid, NULL, NULL, "
! " obj_description(oid)"
! " FROM pg_class WHERE relkind = 'l'");
! res = PQexec(g_conn, loQry->data);
! check_sql_result(res, g_conn, loQry->data, PGRES_TUPLES_OK);
! for (i = 0; i < PQntuples(res); i++)
! {
! loinfo = (LargeObjectInfo *) malloc(sizeof(LargeObjectInfo));
! loinfo->dobj.objType = DO_LARGE_OBJECT;
! loinfo->dobj.catId = nilCatalogId;
! AssignDumpId(&loinfo->dobj);
+ loinfo->dobj.name = strdup(PQgetvalue(res, i, 0));
+ loinfo->rolname = strdup(PQgetvalue(res, i, 1));
+ loinfo->loacl = strdup(PQgetvalue(res, i, 2));
+ loinfo->locomm = strdup(PQgetvalue(res, i, 3));
+ }
PQclear(res);
! destroyPQExpBuffer(loQry);
}
/*
! * dumpLargeObject
! * dump a large object metadata
*/
! static void
! dumpLargeObject(Archive *AH, LargeObjectInfo *loinfo)
{
! PQExpBuffer cquery;
! PQExpBuffer dquery;
! cquery = createPQExpBuffer();
! dquery = createPQExpBuffer();
! /*
! * create an empty large object
! */
! appendPQExpBuffer(cquery, "SELECT lo_create(%s);\n",
! loinfo->dobj.name);
! /*
! * COMMENT ON, if necessary. Note that we cannot use dumpComment()
! * because it will be deployed on SECTION_NONE.
! */
! if (loinfo->locomm && strlen(loinfo->locomm) > 0)
{
! appendPQExpBuffer(cquery,
! "\nCOMMENT ON LARGE OBJECT %s IS ",
! loinfo->dobj.name);
! appendStringLiteralAH(cquery, loinfo->locomm, AH);
! appendPQExpBuffer(cquery, ";\n");
! }
! /*
! * cleanup a large object
! */
! appendPQExpBuffer(dquery, "SELECT lo_unlink(%s);\n",
! loinfo->dobj.name);
! ArchiveEntry(AH, loinfo->dobj.catId, loinfo->dobj.dumpId,
! loinfo->dobj.name,
! NULL, NULL,
! loinfo->rolname, false,
! "LARGE OBJECT", SECTION_DATA,
! cquery->data, dquery->data, NULL,
! loinfo->dobj.dependencies, loinfo->dobj.nDeps,
! dumpLargeObjectData, loinfo);
! /*
! * Dump access privileges, if necessary
! */
! dumpACL(AH, loinfo->dobj.catId, loinfo->dobj.dumpId,
! "LARGE OBJECT",
! loinfo->dobj.name, NULL,
! loinfo->dobj.name, NULL,
! loinfo->rolname, loinfo->loacl);
}
static int
! dumpLargeObjectData(Archive *AH, void *arg)
{
! LargeObjectInfo *loinfo = (LargeObjectInfo *)arg;
! Oid blobOid;
! int blobFd;
! int cnt;
! char buf[LOBBUFSIZE];
! blobOid = atooid(loinfo->dobj.name);
! /* open the large obejct */
! blobFd = lo_open(g_conn, blobOid, INV_READ);
! if (blobFd == -1)
! {
! write_msg(NULL, "%s: could not open large object: %s",
! __FUNCTION__,
! PQerrorMessage(g_conn));
! exit_nicely();
! }
+ StartBlob(AH, blobOid);
+ /* Now read it in chunks, sending data to archive */
do
{
! cnt = lo_read(g_conn, blobFd, buf, LOBBUFSIZE);
! if (cnt < 0)
{
! write_msg(NULL, "%s(): error reading large object: %s",
! __FUNCTION__,
! PQerrorMessage(g_conn));
! exit_nicely();
}
! WriteData(AH, buf, cnt);
! } while (cnt > 0);
! lo_close(g_conn, blobFd);
! EndBlob(AH, blobOid);
return 1;
}
***************
*** 6524,6544 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_DEFAULT_ACL:
dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
break;
! case DO_BLOBS:
! ArchiveEntry(fout, dobj->catId, dobj->dumpId,
! dobj->name, NULL, NULL, "",
! false, "BLOBS", SECTION_DATA,
! "", "", NULL,
! dobj->dependencies, dobj->nDeps,
! dumpBlobs, NULL);
! break;
! case DO_BLOB_COMMENTS:
! ArchiveEntry(fout, dobj->catId, dobj->dumpId,
! dobj->name, NULL, NULL, "",
! false, "BLOB COMMENTS", SECTION_DATA,
! "", "", NULL,
! dobj->dependencies, dobj->nDeps,
! dumpBlobComments, NULL);
break;
}
}
--- 6418,6425 ----
case DO_DEFAULT_ACL:
dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
break;
! case DO_LARGE_OBJECT:
! dumpLargeObject(fout, (LargeObjectInfo *) dobj);
break;
}
}
***************
*** 10395,10401 **** dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
PQExpBuffer sql;
/* Do nothing if ACL dump is not enabled */
! if (dataOnly || aclsSkip)
return;
sql = createPQExpBuffer();
--- 10276,10284 ----
PQExpBuffer sql;
/* Do nothing if ACL dump is not enabled */
! /* Large object is an exception of --data-only */
! if (aclsSkip ||
! (dataOnly && strcmp(type, "LARGE OBJECT") != 0))
return;
sql = createPQExpBuffer();
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 115,122 **** typedef enum
DO_FDW,
DO_FOREIGN_SERVER,
DO_DEFAULT_ACL,
! DO_BLOBS,
! DO_BLOB_COMMENTS
} DumpableObjectType;
typedef struct _dumpableObject
--- 115,121 ----
DO_FDW,
DO_FOREIGN_SERVER,
DO_DEFAULT_ACL,
! DO_LARGE_OBJECT,
} DumpableObjectType;
typedef struct _dumpableObject
***************
*** 443,448 **** typedef struct _defaultACLInfo
--- 442,455 ----
char *defaclacl;
} DefaultACLInfo;
+ typedef struct _largeObjectInfo
+ {
+ DumpableObject dobj;
+ char *rolname;
+ char *loacl;
+ char *locomm;
+ } LargeObjectInfo;
+
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
extern bool g_verbose; /* verbose flag */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 55,62 **** static const int oldObjectTypePriority[] =
3, /* DO_FDW */
4, /* DO_FOREIGN_SERVER */
17, /* DO_DEFAULT_ACL */
! 10, /* DO_BLOBS */
! 11 /* DO_BLOB_COMMENTS */
};
/*
--- 55,61 ----
3, /* DO_FDW */
4, /* DO_FOREIGN_SERVER */
17, /* DO_DEFAULT_ACL */
! 10, /* DO_LARGE_OBJECT */
};
/*
***************
*** 92,99 **** static const int newObjectTypePriority[] =
14, /* DO_FDW */
15, /* DO_FOREIGN_SERVER */
27, /* DO_DEFAULT_ACL */
! 20, /* DO_BLOBS */
! 21 /* DO_BLOB_COMMENTS */
};
--- 91,97 ----
14, /* DO_FDW */
15, /* DO_FOREIGN_SERVER */
27, /* DO_DEFAULT_ACL */
! 20, /* DO_LARGE_OBJECT */
};
***************
*** 1146,1160 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"DEFAULT ACL %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
! case DO_BLOBS:
snprintf(buf, bufsize,
! "BLOBS (ID %d)",
! obj->dumpId);
! return;
! case DO_BLOB_COMMENTS:
! snprintf(buf, bufsize,
! "BLOB COMMENTS (ID %d)",
! obj->dumpId);
return;
}
/* shouldn't get here */
--- 1144,1153 ----
"DEFAULT ACL %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
! case DO_LARGE_OBJECT:
snprintf(buf, bufsize,
! "LARGE OBJECT (OID %s)",
! obj->name);
return;
}
/* shouldn't get here */