Commit e8f69be0 authored by Philip Warner's avatar Philip Warner

- Support for BLOB output from pg_dump and input via pg_restore

- Support for direct DB connection in pg_restore
- Fixes in support for --insert flag
- pg_dump now outputs in modified OID order
- various other bug fixes
parent 0143d391
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# #
# Copyright (c) 1994, Regents of the University of California # Copyright (c) 1994, Regents of the University of California
# #
# $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.19 2000/07/04 19:52:00 petere Exp $ # $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.20 2000/07/21 11:40:08 pjw Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,8 +12,8 @@ subdir = src/bin/pg_dump ...@@ -12,8 +12,8 @@ subdir = src/bin/pg_dump
top_builddir = ../../.. top_builddir = ../../..
include ../../Makefile.global include ../../Makefile.global
OBJS= pg_backup_archiver.o pg_backup_custom.o pg_backup_files.o \ OBJS= pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o pg_backup_files.o \
pg_backup_plain_text.o $(STRDUP) pg_backup_null.o pg_backup_tar.o $(STRDUP)
CFLAGS+= -I$(LIBPQDIR) CFLAGS+= -I$(LIBPQDIR)
LIBS+= -lz LIBS+= -lz
......
Notes on pg_dump Notes on pg_dump
================ ================
pg_dump, by default, still outputs text files. 1. pg_dump, by default, still outputs text files.
pg_dumpall forces all pg_dump output to be text, since it also outputs text into the same output stream. 2. pg_dumpall forces all pg_dump output to be text, since it also outputs text into the same output stream.
The plain text output format can not be used as input into pg_restore. 3. The plain text output format can not be used as input into pg_restore.
4. pg_dump now dumps the items in a modified OID order to try to improve relaibility of default restores.
To dump a database into the next custom format, type: To dump a database into the next custom format, type:
pg_dump <db-name> -Fc > <backup-file> pg_dump <db-name> -Fc > <backup-file>
or, in TAR format
pg_dump <db-name> -Ft > <backup-file>
To restore, try To restore, try
To list contents: To list contents:
...@@ -53,7 +59,37 @@ or, simply: ...@@ -53,7 +59,37 @@ or, simply:
pg_restore backup.bck --use=toc.lis | psql newdbname pg_restore backup.bck --use=toc.lis | psql newdbname
Philip Warner, 3-Jul-2000 BLOBs
=====
To dump blobs you must use the custom archive format (-Fc) or TAR format (-Ft), and specify the
--blobs qualifier to the pg_dump command.
To restore blobs you must use a direct database connection (--db=db-to-restore-to).
eg.
pg_dump --blob -Fc db-to-backup -f backup.bck
pg_restore backup.bck --db=db-to-restore-into
TAR
===
The TAR archive that pg_dump creates currently has a blank username & group for the files,
but should be otherwise valid. It also includes a 'restore.sql' script which is there for
the benefit of humans. It is never used by pg_restore.
Note: the TAR format archive can only be used as input into pg_restore if it is in TAR form.
(ie. you should not extract the files then expect pg_restore to work).
You can extract, edit, and tar the files again, and it should work, but the 'toc'
file should go at the start, the data files be in the order they are used, and
the BLOB files at the end.
Philip Warner, 16-Jul-2000
pjw@rhyme.com.au pjw@rhyme.com.au
......
...@@ -30,12 +30,15 @@ ...@@ -30,12 +30,15 @@
#define PG_BACKUP__ #define PG_BACKUP__
#include "postgres.h"
#include "libpq-fe.h"
typedef enum _archiveFormat { typedef enum _archiveFormat {
archUnknown = 0, archUnknown = 0,
archCustom = 1, archCustom = 1,
archFiles = 2, archFiles = 2,
archTar = 3, archTar = 3,
archPlainText = 4 archNull = 4
} ArchiveFormat; } ArchiveFormat;
/* /*
...@@ -43,7 +46,8 @@ typedef enum _archiveFormat { ...@@ -43,7 +46,8 @@ typedef enum _archiveFormat {
* time this gives us some abstraction and type checking. * time this gives us some abstraction and type checking.
*/ */
typedef struct _Archive { typedef struct _Archive {
/* Nothing here */ int verbose;
/* The rest is private */
} Archive; } Archive;
typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg); typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);
...@@ -73,6 +77,13 @@ typedef struct _restoreOptions { ...@@ -73,6 +77,13 @@ typedef struct _restoreOptions {
char *tableNames; char *tableNames;
char *triggerNames; char *triggerNames;
int useDB;
char *dbname;
char *pgport;
char *pghost;
int ignoreVersion;
int requirePassword;
int *idWanted; int *idWanted;
int limitToList; int limitToList;
int compression; int compression;
...@@ -83,24 +94,42 @@ typedef struct _restoreOptions { ...@@ -83,24 +94,42 @@ typedef struct _restoreOptions {
* Main archiver interface. * Main archiver interface.
*/ */
extern void exit_horribly(Archive *AH, const char *fmt, ...);
/* Lets the archibe know we have a DB connection to shutdown if it dies */
PGconn* ConnectDatabase(Archive *AH,
const char* dbname,
const char* pghost,
const char* pgport,
const int reqPwd,
const int ignoreVersion);
/* Called to add a TOC entry */ /* Called to add a TOC entry */
extern void ArchiveEntry(Archive* AH, const char* oid, const char* name, extern void ArchiveEntry(Archive* AH, const char* oid, const char* name,
const char* desc, const char* (deps[]), const char* defn, const char* desc, const char* (deps[]), const char* defn,
const char* dropStmt, const char* owner, const char* dropStmt, const char* copyStmt, const char* owner,
DataDumperPtr dumpFn, void* dumpArg); DataDumperPtr dumpFn, void* dumpArg);
/* Called to write *data* to the archive */ /* Called to write *data* to the archive */
extern int WriteData(Archive* AH, const void* data, int dLen); extern int WriteData(Archive* AH, const void* data, int dLen);
//extern int StartBlobs(Archive* AH);
//extern int EndBlobs(Archive* AH);
extern int StartBlob(Archive* AH, int oid);
extern int EndBlob(Archive* AH, int oid);
extern void CloseArchive(Archive* AH); extern void CloseArchive(Archive* AH);
extern void RestoreArchive(Archive* AH, RestoreOptions *ropt); extern void RestoreArchive(Archive* AH, RestoreOptions *ropt);
/* Open an existing archive */ /* Open an existing archive */
extern Archive* OpenArchive(const char* FileSpec, ArchiveFormat fmt); extern Archive* OpenArchive(const char* FileSpec, const ArchiveFormat fmt);
/* Create a new archive */ /* Create a new archive */
extern Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression); extern Archive* CreateArchive(const char* FileSpec, const ArchiveFormat fmt,
const int compression);
/* The --list option */ /* The --list option */
extern void PrintTOCSummary(Archive* AH, RestoreOptions *ropt); extern void PrintTOCSummary(Archive* AH, RestoreOptions *ropt);
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include "pg_backup.h" #include "pg_backup.h"
#include "pg_backup_archiver.h" #include "pg_backup_archiver.h"
#include "pg_backup_db.h"
#include <string.h> #include <string.h>
#include <unistd.h> /* for dup */ #include <unistd.h> /* for dup */
...@@ -32,10 +34,13 @@ ...@@ -32,10 +34,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include "pqexpbuffer.h"
#include "libpq/libpq-fs.h"
static void _SortToc(ArchiveHandle* AH, TocSortCompareFn fn); static void _SortToc(ArchiveHandle* AH, TocSortCompareFn fn);
static int _tocSortCompareByOIDNum(const void *p1, const void *p2); static int _tocSortCompareByOIDNum(const void *p1, const void *p2);
static int _tocSortCompareByIDNum(const void *p1, const void *p2); static int _tocSortCompareByIDNum(const void *p1, const void *p2);
static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt, static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt,
int compression, ArchiveMode mode); int compression, ArchiveMode mode);
static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt); static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt); static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt);
...@@ -45,8 +50,14 @@ static TocEntry* _getTocEntry(ArchiveHandle* AH, int id); ...@@ -45,8 +50,14 @@ static TocEntry* _getTocEntry(ArchiveHandle* AH, int id);
static void _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te); static void _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te);
static void _moveBefore(ArchiveHandle* AH, TocEntry* pos, TocEntry* te); static void _moveBefore(ArchiveHandle* AH, TocEntry* pos, TocEntry* te);
static int _discoverArchiveFormat(ArchiveHandle* AH); static int _discoverArchiveFormat(ArchiveHandle* AH);
static char *progname = "Archiver"; static char *progname = "Archiver";
static void _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap);
static int _canRestoreBlobs(ArchiveHandle *AH);
/* /*
* Wrapper functions. * Wrapper functions.
* *
...@@ -58,7 +69,9 @@ static char *progname = "Archiver"; ...@@ -58,7 +69,9 @@ static char *progname = "Archiver";
/* Create a new archive */ /* Create a new archive */
/* Public */ /* Public */
Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression) Archive* CreateArchive(const char* FileSpec, const ArchiveFormat fmt,
const int compression)
{ {
ArchiveHandle* AH = _allocAH(FileSpec, fmt, compression, archModeWrite); ArchiveHandle* AH = _allocAH(FileSpec, fmt, compression, archModeWrite);
return (Archive*)AH; return (Archive*)AH;
...@@ -66,7 +79,7 @@ Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression) ...@@ -66,7 +79,7 @@ Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression)
/* Open an existing archive */ /* Open an existing archive */
/* Public */ /* Public */
Archive* OpenArchive(const char* FileSpec, ArchiveFormat fmt) Archive* OpenArchive(const char* FileSpec, const ArchiveFormat fmt)
{ {
ArchiveHandle* AH = _allocAH(FileSpec, fmt, 0, archModeRead); ArchiveHandle* AH = _allocAH(FileSpec, fmt, 0, archModeRead);
return (Archive*)AH; return (Archive*)AH;
...@@ -93,47 +106,155 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt) ...@@ -93,47 +106,155 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
int reqs; int reqs;
OutputContext sav; OutputContext sav;
AH->ropt = ropt;
/*
* If we're using a DB connection, then connect it.
*/
if (ropt->useDB)
{
ahlog(AH, 1, "Connecting to database for restore\n");
if (AH->version < K_VERS_1_3)
die_horribly(AH, "Direct database connections are not supported in pre-1.3 archives");
ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport,
ropt->requirePassword, ropt->ignoreVersion);
}
/*
* Setup the output file if necessary.
*/
if (ropt->filename || ropt->compression) if (ropt->filename || ropt->compression)
sav = SetOutput(AH, ropt->filename, ropt->compression); sav = SetOutput(AH, ropt->filename, ropt->compression);
ahprintf(AH, "--\n-- Selected TOC Entries:\n--\n"); ahprintf(AH, "--\n-- Selected TOC Entries:\n--\n");
/* Drop the items at the start, in reverse order */ /*
* Drop the items at the start, in reverse order
*/
if (ropt->dropSchema) { if (ropt->dropSchema) {
te = AH->toc->prev; te = AH->toc->prev;
while (te != AH->toc) { while (te != AH->toc) {
reqs = _tocEntryRequired(te, ropt); reqs = _tocEntryRequired(te, ropt);
if ( (reqs & 1) && te->dropStmt) { /* We want the schema */ if ( ( (reqs & 1) != 0) && te->dropStmt) { /* We want the schema */
ahlog(AH, 1, "Dropping %s %s\n", te->desc, te->name);
ahprintf(AH, "%s", te->dropStmt); ahprintf(AH, "%s", te->dropStmt);
} }
te = te->prev; te = te->prev;
} }
} }
/*
* Now process each TOC entry
*/
te = AH->toc->next; te = AH->toc->next;
while (te != AH->toc) { while (te != AH->toc) {
/* Work out what, if anything, we want from this entry */
reqs = _tocEntryRequired(te, ropt); reqs = _tocEntryRequired(te, ropt);
if (reqs & 1) /* We want the schema */ if ( (reqs & 1) != 0) /* We want the schema */
{
ahlog(AH, 1, "Creating %s %s\n", te->desc, te->name);
_printTocEntry(AH, te, ropt); _printTocEntry(AH, te, ropt);
}
/*
* If we want data, and it has data, then restore that too
*/
if (AH->PrintTocDataPtr != NULL && (reqs & 2) != 0) { if (AH->PrintTocDataPtr != NULL && (reqs & 2) != 0) {
#ifndef HAVE_LIBZ #ifndef HAVE_LIBZ
if (AH->compression != 0) if (AH->compression != 0)
die_horribly("%s: Unable to restore data from a compressed archive\n", progname); die_horribly(AH, "%s: Unable to restore data from a compressed archive\n", progname);
#endif #endif
ahlog(AH, 1, "Restoring data for %s \n", te->name);
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",
te->id, te->oid, te->desc, te->name);
/*
* Maybe we can't do BLOBS, so check if this node is for BLOBS
*/
if ((strcmp(te->desc,"BLOBS") == 0) && !_canRestoreBlobs(AH))
{
ahprintf(AH, "--\n-- SKIPPED \n--\n\n");
/*
* This is a bit nasty - we assume, for the moment, that if a custom
* output is used, then we don't want warnings.
*/
if (!AH->CustomOutPtr)
fprintf(stderr, "%s: WARNING - skipping BLOB restoration\n", progname);
} else {
_disableTriggers(AH, te, ropt); _disableTriggers(AH, te, ropt);
/* If we have a copy statement, use it. As of V1.3, these are separate
* to allow easy import from withing a database connection. Pre 1.3
* archives can not use DB connections and are sent to output only.
*
* For V1.3+, the table data MUST have a copy statement so that
* we can go into appropriate mode with libpq.
*/
if (te->copyStmt && strlen(te->copyStmt) > 0)
ahprintf(AH, te->copyStmt);
(*AH->PrintTocDataPtr)(AH, te, ropt); (*AH->PrintTocDataPtr)(AH, te, ropt);
_enableTriggers(AH, te, ropt); _enableTriggers(AH, te, ropt);
} }
}
te = te->next; te = te->next;
} }
/*
* Now use blobs_xref (if used) to fixup any refs for tables that we loaded
*/
if (_canRestoreBlobs(AH) && AH->createdBlobXref)
{
te = AH->toc->next;
while (te != AH->toc) {
/* Is it table data? */
if (strcmp(te->desc, "TABLE DATA") == 0) {
ahlog(AH, 2, "Checking if we loaded %s\n", te->name);
reqs = _tocEntryRequired(te, ropt);
if ( (reqs & 2) != 0) /* We loaded the data */
{
ahlog(AH, 1, "Fixing up BLOB ref for %s\n", te->name);
FixupBlobRefs(AH, te->name);
}
}
else
{
ahlog(AH, 2, "Ignoring BLOB xrefs for %s %s\n", te->desc, te->name);
}
te = te->next;
}
}
/*
* Clean up & we're done.
*/
if (ropt->filename) if (ropt->filename)
ResetOutput(AH, sav); ResetOutput(AH, sav);
if (ropt->useDB)
{
PQfinish(AH->connection);
AH->connection = NULL;
}
} }
/*
* Allocate a new RestoreOptions block.
* This is mainly so we can initialize it, but also for future expansion,
*/
RestoreOptions* NewRestoreOptions(void) RestoreOptions* NewRestoreOptions(void)
{ {
RestoreOptions* opts; RestoreOptions* opts;
...@@ -145,6 +266,11 @@ RestoreOptions* NewRestoreOptions(void) ...@@ -145,6 +266,11 @@ RestoreOptions* NewRestoreOptions(void)
return opts; return opts;
} }
static int _canRestoreBlobs(ArchiveHandle *AH)
{
return (AH->ropt->useDB && AH->connection);
}
static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt) static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
{ {
ahprintf(AH, "-- Disable triggers\n"); ahprintf(AH, "-- Disable triggers\n");
...@@ -168,7 +294,7 @@ static void _enableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *rop ...@@ -168,7 +294,7 @@ static void _enableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *rop
/* /*
* This is a routine that is available to pg_dump, hence the 'Archive*' parameter. * This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
*/ */
/* Public */ /* Public */
...@@ -176,6 +302,10 @@ int WriteData(Archive* AHX, const void* data, int dLen) ...@@ -176,6 +302,10 @@ int WriteData(Archive* AHX, const void* data, int dLen)
{ {
ArchiveHandle* AH = (ArchiveHandle*)AHX; ArchiveHandle* AH = (ArchiveHandle*)AHX;
if (!AH->currToc)
die_horribly(AH, "%s: WriteData can not be called outside the context of "
"a DataDumper routine\n", progname);
return (*AH->WriteDataPtr)(AH, data, dLen); return (*AH->WriteDataPtr)(AH, data, dLen);
} }
...@@ -187,7 +317,7 @@ int WriteData(Archive* AHX, const void* data, int dLen) ...@@ -187,7 +317,7 @@ int WriteData(Archive* AHX, const void* data, int dLen)
/* Public */ /* Public */
void ArchiveEntry(Archive* AHX, const char* oid, const char* name, void ArchiveEntry(Archive* AHX, const char* oid, const char* name,
const char* desc, const char* (deps[]), const char* defn, const char* desc, const char* (deps[]), const char* defn,
const char* dropStmt, const char* owner, const char* dropStmt, const char* copyStmt, const char* owner,
DataDumperPtr dumpFn, void* dumpArg) DataDumperPtr dumpFn, void* dumpArg)
{ {
ArchiveHandle* AH = (ArchiveHandle*)AHX; ArchiveHandle* AH = (ArchiveHandle*)AHX;
...@@ -196,9 +326,9 @@ void ArchiveEntry(Archive* AHX, const char* oid, const char* name, ...@@ -196,9 +326,9 @@ void ArchiveEntry(Archive* AHX, const char* oid, const char* name,
AH->lastID++; AH->lastID++;
AH->tocCount++; AH->tocCount++;
newToc = (TocEntry*)malloc(sizeof(TocEntry)); newToc = (TocEntry*)calloc(1, sizeof(TocEntry));
if (!newToc) if (!newToc)
die_horribly("Archiver: unable to allocate memory for TOC entry\n"); die_horribly(AH, "Archiver: unable to allocate memory for TOC entry\n");
newToc->prev = AH->toc->prev; newToc->prev = AH->toc->prev;
newToc->next = AH->toc; newToc->next = AH->toc;
...@@ -212,6 +342,7 @@ void ArchiveEntry(Archive* AHX, const char* oid, const char* name, ...@@ -212,6 +342,7 @@ void ArchiveEntry(Archive* AHX, const char* oid, const char* name,
newToc->desc = strdup(desc); newToc->desc = strdup(desc);
newToc->defn = strdup(defn); newToc->defn = strdup(defn);
newToc->dropStmt = strdup(dropStmt); newToc->dropStmt = strdup(dropStmt);
newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
newToc->owner = strdup(owner); newToc->owner = strdup(owner);
newToc->printed = 0; newToc->printed = 0;
newToc->formatData = NULL; newToc->formatData = NULL;
...@@ -233,10 +364,30 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt) ...@@ -233,10 +364,30 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
ArchiveHandle* AH = (ArchiveHandle*) AHX; ArchiveHandle* AH = (ArchiveHandle*) AHX;
TocEntry *te = AH->toc->next; TocEntry *te = AH->toc->next;
OutputContext sav; OutputContext sav;
char *fmtName;
if (ropt->filename) if (ropt->filename)
sav = SetOutput(AH, ropt->filename, ropt->compression); sav = SetOutput(AH, ropt->filename, ropt->compression);
ahprintf(AH, ";\n; Archive created at %s", ctime(&AH->createDate));
ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %d\n",
AH->archdbname, AH->tocCount, AH->compression);
switch (AH->format) {
case archFiles:
fmtName = "FILES";
break;
case archCustom:
fmtName = "CUSTOM";
break;
case archTar:
fmtName = "TAR";
break;
default:
fmtName = "UNKNOWN";
}
ahprintf(AH, "; Format: %s\n;\n", fmtName);
ahprintf(AH, ";\n; Selected TOC Entries:\n;\n"); ahprintf(AH, ";\n; Selected TOC Entries:\n;\n");
while (te != AH->toc) { while (te != AH->toc) {
...@@ -249,6 +400,79 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt) ...@@ -249,6 +400,79 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
ResetOutput(AH, sav); ResetOutput(AH, sav);
} }
/***********
* BLOB Archival
***********/
/* Called by a dumper to signal start of a BLOB */
int StartBlob(Archive* AHX, int oid)
{
ArchiveHandle* AH = (ArchiveHandle*)AHX;
if (!AH->StartBlobPtr)
die_horribly(AH, "%s: BLOB output not supported in chosen format\n", progname);
(*AH->StartBlobPtr)(AH, AH->currToc, oid);
return 1;
}
/* Called by a dumper to signal end of a BLOB */
int EndBlob(Archive* AHX, int oid)
{
ArchiveHandle* AH = (ArchiveHandle*)AHX;
if (AH->EndBlobPtr)
(*AH->EndBlobPtr)(AH, AH->currToc, oid);
return 1;
}
/**********
* BLOB Restoration
**********/
/*
* Called by a format handler to initiate restoration of a blob
*/
void StartRestoreBlob(ArchiveHandle* AH, int oid)
{
int loOid;
if (!AH->createdBlobXref)
{
if (!AH->connection)
die_horribly(AH, "%s: can not restore BLOBs without a database connection", progname);
CreateBlobXrefTable(AH);
AH->createdBlobXref = 1;
}
loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
if (loOid == 0)
die_horribly(AH, "%s: unable to create BLOB\n", progname);
ahlog(AH, 1, "Restoring BLOB oid %d as %d\n", oid, loOid);
StartTransaction(AH);
InsertBlobXref(AH, oid, loOid);
AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
if (AH->loFd == -1)
die_horribly(AH, "%s: unable to open BLOB\n", progname);
AH->writingBlob = 1;
}
void EndRestoreBlob(ArchiveHandle* AH, int oid)
{
lo_close(AH->connection, AH->loFd);
AH->writingBlob = 0;
CommitTransaction(AH);
}
/*********** /***********
* Sorting and Reordering * Sorting and Reordering
***********/ ***********/
...@@ -256,6 +480,7 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt) ...@@ -256,6 +480,7 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
/* /*
* Move TOC entries of the specified type to the START of the TOC. * Move TOC entries of the specified type to the START of the TOC.
*/ */
/* Public */ /* Public */
void MoveToStart(Archive* AHX, char *oType) void MoveToStart(Archive* AHX, char *oType)
{ {
...@@ -356,7 +581,7 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt) ...@@ -356,7 +581,7 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt)
/* Setup the file */ /* Setup the file */
fh = fopen(ropt->tocFile, PG_BINARY_R); fh = fopen(ropt->tocFile, PG_BINARY_R);
if (!fh) if (!fh)
die_horribly("%s: could not open TOC file\n", progname); die_horribly(AH, "%s: could not open TOC file\n", progname);
while (fgets(buf, 1024, fh) != NULL) while (fgets(buf, 1024, fh) != NULL)
{ {
...@@ -377,14 +602,14 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt) ...@@ -377,14 +602,14 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt)
id = strtol(buf, &endptr, 10); id = strtol(buf, &endptr, 10);
if (endptr == buf) if (endptr == buf)
{ {
fprintf(stderr, "%s: warning - line ignored: %s\n", progname, buf); fprintf(stderr, "%s: WARNING - line ignored: %s\n", progname, buf);
continue; continue;
} }
/* Find TOC entry */ /* Find TOC entry */
te = _getTocEntry(AH, id); te = _getTocEntry(AH, id);
if (!te) if (!te)
die_horribly("%s: could not find entry for id %d\n",progname, id); die_horribly(AH, "%s: could not find entry for id %d\n",progname, id);
ropt->idWanted[id-1] = 1; ropt->idWanted[id-1] = 1;
...@@ -428,7 +653,7 @@ int archprintf(Archive* AH, const char *fmt, ...) ...@@ -428,7 +653,7 @@ int archprintf(Archive* AH, const char *fmt, ...)
if ((p = malloc(bSize)) == NULL) if ((p = malloc(bSize)) == NULL)
{ {
va_end(ap); va_end(ap);
die_horribly("%s: could not allocate buffer for archprintf\n", progname); exit_horribly(AH, "%s: could not allocate buffer for archprintf\n", progname);
} }
cnt = vsnprintf(p, bSize, fmt, ap); cnt = vsnprintf(p, bSize, fmt, ap);
} }
...@@ -525,7 +750,7 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...) ...@@ -525,7 +750,7 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...)
if (p == NULL) if (p == NULL)
{ {
va_end(ap); va_end(ap);
die_horribly("%s: could not allocate buffer for ahprintf\n", progname); die_horribly(AH, "%s: could not allocate buffer for ahprintf\n", progname);
} }
cnt = vsnprintf(p, bSize, fmt, ap); cnt = vsnprintf(p, bSize, fmt, ap);
} }
...@@ -535,28 +760,82 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...) ...@@ -535,28 +760,82 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...)
return cnt; return cnt;
} }
void ahlog(ArchiveHandle* AH, int level, const char *fmt, ...)
{
va_list ap;
if (AH->debugLevel < level && (!AH->public.verbose || level > 1))
return;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
/* /*
* Write buffer to the output file (usually stdout). * Write buffer to the output file (usually stdout). This is user for
* outputting 'restore' scripts etc. It is even possible for an archive
* format to create a custom output routine to 'fake' a restore if it
* wants to generate a script (see TAR output).
*/ */
int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH) int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH)
{ {
if (AH->gzOut) int res;
if (AH->writingBlob)
{
res = lo_write(AH->connection, AH->loFd, (void*)ptr, size * nmemb);
ahlog(AH, 5, "Wrote %d bytes of BLOB data (result = %d)\n", size * nmemb, res);
return res;
}
else if (AH->gzOut)
return GZWRITE((void*)ptr, size, nmemb, AH->OF); return GZWRITE((void*)ptr, size, nmemb, AH->OF);
else if (AH->CustomOutPtr)
return AH->CustomOutPtr(AH, ptr, size * nmemb);
else
{
/*
* If we're doing a restore, and it's direct to DB, and we're connected
* then send it to the DB.
*/
if (AH->ropt && AH->ropt->useDB && AH->connection)
return ExecuteSqlCommandBuf(AH, (void*)ptr, size*nmemb);
else else
return fwrite((void*)ptr, size, nmemb, AH->OF); return fwrite((void*)ptr, size, nmemb, AH->OF);
}
} }
/* Common exit code */
static void _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap)
{
vfprintf(stderr, fmt, ap);
void die_horribly(const char *fmt, ...) if (AH)
if (AH->connection)
PQfinish(AH->connection);
exit(1);
}
/* External use */
void exit_horribly(Archive *AH, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); _die_horribly((ArchiveHandle*)AH, fmt, ap);
va_end(ap);
exit(1);
} }
/* Archiver use (just different arg declaration) */
void die_horribly(ArchiveHandle *AH, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_die_horribly(AH, fmt, ap);
}
static void _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te) static void _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te)
{ {
te->prev->next = te->next; te->prev->next = te->next;
...@@ -638,18 +917,19 @@ int WriteInt(ArchiveHandle* AH, int i) ...@@ -638,18 +917,19 @@ int WriteInt(ArchiveHandle* AH, int i)
int ReadInt(ArchiveHandle* AH) int ReadInt(ArchiveHandle* AH)
{ {
int res = 0; int res = 0;
int shft = 1;
int bv, b; int bv, b;
int sign = 0; /* Default positive */ int sign = 0; /* Default positive */
int bitShift = 0;
if (AH->version > K_VERS_1_0) if (AH->version > K_VERS_1_0)
/* Read a sign byte */ /* Read a sign byte */
sign = (*AH->ReadBytePtr)(AH); sign = (*AH->ReadBytePtr)(AH);
for( b = 0 ; b < AH->intSize ; b++) { for( b = 0 ; b < AH->intSize ; b++) {
bv = (*AH->ReadBytePtr)(AH); bv = (*AH->ReadBytePtr)(AH) & 0xFF;
res = res + shft * bv; if (bv != 0)
shft *= 256; res = res + (bv << bitShift);
bitShift += 8;
} }
if (sign) if (sign)
...@@ -660,8 +940,17 @@ int ReadInt(ArchiveHandle* AH) ...@@ -660,8 +940,17 @@ int ReadInt(ArchiveHandle* AH)
int WriteStr(ArchiveHandle* AH, char* c) int WriteStr(ArchiveHandle* AH, char* c)
{ {
int l = WriteInt(AH, strlen(c)); int res;
return (*AH->WriteBufPtr)(AH, c, strlen(c)) + l;
if (c)
{
res = WriteInt(AH, strlen(c));
res += (*AH->WriteBufPtr)(AH, c, strlen(c));
}
else
res = WriteInt(AH, -1);
return res;
} }
char* ReadStr(ArchiveHandle* AH) char* ReadStr(ArchiveHandle* AH)
...@@ -670,12 +959,18 @@ char* ReadStr(ArchiveHandle* AH) ...@@ -670,12 +959,18 @@ char* ReadStr(ArchiveHandle* AH)
int l; int l;
l = ReadInt(AH); l = ReadInt(AH);
if (l == -1)
buf = NULL;
else
{
buf = (char*)malloc(l+1); buf = (char*)malloc(l+1);
if (!buf) if (!buf)
die_horribly("Archiver: Unable to allocate sufficient memory in ReadStr\n"); die_horribly(AH, "%s: Unable to allocate sufficient memory in ReadStr - " "requested %d (0x%x) bytes\n", progname, l, l);
(*AH->ReadBufPtr)(AH, (void*)buf, l); (*AH->ReadBufPtr)(AH, (void*)buf, l);
buf[l] = '\0'; buf[l] = '\0';
}
return buf; return buf;
} }
...@@ -686,6 +981,17 @@ int _discoverArchiveFormat(ArchiveHandle* AH) ...@@ -686,6 +981,17 @@ int _discoverArchiveFormat(ArchiveHandle* AH)
int cnt; int cnt;
int wantClose = 0; int wantClose = 0;
/*
* fprintf(stderr, "%s: Attempting to ascertain archive format\n", progname);
*/
if (AH->lookahead)
free(AH->lookahead);
AH->lookaheadSize = 512;
AH->lookahead = calloc(1, 512);
AH->lookaheadLen = 0;
AH->lookaheadPos = 0;
if (AH->fSpec) { if (AH->fSpec) {
wantClose = 1; wantClose = 1;
...@@ -695,114 +1001,149 @@ int _discoverArchiveFormat(ArchiveHandle* AH) ...@@ -695,114 +1001,149 @@ int _discoverArchiveFormat(ArchiveHandle* AH)
} }
if (!fh) if (!fh)
die_horribly("Archiver: could not open input file\n"); die_horribly(AH, "Archiver: could not open input file\n");
cnt = fread(sig, 1, 5, fh); cnt = fread(sig, 1, 5, fh);
if (cnt != 5) if (cnt != 5)
die_horribly("%s: input file is too short, or is unreadable\n", progname); die_horribly(AH, "%s: input file is too short, or is unreadable\n", progname);
if (strncmp(sig, "PGDMP", 5) != 0) /* Save it, just in case we need it later*/
die_horribly("%s: input file does not appear to be a valid archive\n", progname); strncpy(&AH->lookahead[0], sig, 5);
AH->lookaheadLen = 5;
if (strncmp(sig, "PGDMP", 5) == 0)
{
AH->vmaj = fgetc(fh); AH->vmaj = fgetc(fh);
AH->vmin = fgetc(fh); AH->vmin = fgetc(fh);
/* Save these too... */
AH->lookahead[AH->lookaheadLen++] = AH->vmaj;
AH->lookahead[AH->lookaheadLen++] = AH->vmin;
/* Check header version; varies from V1.0 */ /* Check header version; varies from V1.0 */
if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */ if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */
{
AH->vrev = fgetc(fh); AH->vrev = fgetc(fh);
AH->lookahead[AH->lookaheadLen++] = AH->vrev;
}
else else
AH->vrev = 0; AH->vrev = 0;
AH->intSize = fgetc(fh); AH->intSize = fgetc(fh);
AH->lookahead[AH->lookaheadLen++] = AH->intSize;
AH->format = fgetc(fh); AH->format = fgetc(fh);
AH->lookahead[AH->lookaheadLen++] = AH->format;
/* Make a convenient integer <maj><min><rev>00 */ /* Make a convenient integer <maj><min><rev>00 */
AH->version = ( (AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev ) * 256 + 0; AH->version = ( (AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev ) * 256 + 0;
} else {
/*
* *Maybe* we have a tar archive format file...
* So, read first 512 byte header...
*/
cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
AH->lookaheadLen += cnt;
if (AH->lookaheadLen != 512)
die_horribly(AH, "%s: input file does not appear to be a valid archive (too short?)\n",
progname);
if (!isValidTarHeader(AH->lookahead))
die_horribly(AH, "%s: input file does not appear to be a valid archive\n", progname);
AH->format = archTar;
}
/* If we can't seek, then mark the header as read */ /* If we can't seek, then mark the header as read */
if (fseek(fh, 0, SEEK_SET) != 0) if (fseek(fh, 0, SEEK_SET) != 0)
{
/*
* NOTE: Formats that use the looahead buffer can unset this in their Init routine.
*/
AH->readHeader = 1; AH->readHeader = 1;
}
else
AH->lookaheadLen = 0; /* Don't bother since we've reset the file */
/*
*fprintf(stderr, "%s: read %d bytes into lookahead buffer\n", progname, AH->lookaheadLen);
*/
/* Close the file */ /* Close the file */
if (wantClose) if (wantClose)
fclose(fh); fclose(fh);
return AH->format; return AH->format;
} }
/* /*
* Allocate an archive handle * Allocate an archive handle
*/ */
static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt, static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt,
int compression, ArchiveMode mode) { const int compression, ArchiveMode mode)
{
ArchiveHandle* AH; ArchiveHandle* AH;
/*
*fprintf(stderr, "%s: allocating AH for %s, format %d\n", progname, FileSpec, fmt);
*/
AH = (ArchiveHandle*)calloc(1, sizeof(ArchiveHandle)); AH = (ArchiveHandle*)calloc(1, sizeof(ArchiveHandle));
if (!AH) if (!AH)
die_horribly("Archiver: Could not allocate archive handle\n"); die_horribly(AH, "Archiver: Could not allocate archive handle\n");
AH->vmaj = K_VERS_MAJOR; AH->vmaj = K_VERS_MAJOR;
AH->vmin = K_VERS_MINOR; AH->vmin = K_VERS_MINOR;
AH->createDate = time(NULL);
AH->intSize = sizeof(int); AH->intSize = sizeof(int);
AH->lastID = 0; AH->lastID = 0;
if (FileSpec) { if (FileSpec) {
AH->fSpec = strdup(FileSpec); AH->fSpec = strdup(FileSpec);
/*
* Not used; maybe later....
*
* AH->workDir = strdup(FileSpec);
* for(i=strlen(FileSpec) ; i > 0 ; i--)
* if (AH->workDir[i-1] == '/')
*/
} else { } else {
AH->fSpec = NULL; AH->fSpec = NULL;
} }
AH->FH = NULL;
AH->formatData = NULL;
AH->currToc = NULL;
AH->currUser = ""; AH->currUser = "";
AH->toc = (TocEntry*)calloc(1, sizeof(TocEntry)); AH->toc = (TocEntry*)calloc(1, sizeof(TocEntry));
if (!AH->toc) if (!AH->toc)
die_horribly("Archiver: Could not allocate TOC header\n"); die_horribly(AH, "Archiver: Could not allocate TOC header\n");
AH->tocCount = 0;
AH->toc->next = AH->toc; AH->toc->next = AH->toc;
AH->toc->prev = AH->toc; AH->toc->prev = AH->toc;
AH->toc->id = 0;
AH->toc->oid = NULL;
AH->toc->name = NULL; /* eg. MY_SPECIAL_FUNCTION */
AH->toc->desc = NULL; /* eg. FUNCTION */
AH->toc->defn = NULL; /* ie. sql to define it */
AH->toc->depOid = NULL;
AH->mode = mode; AH->mode = mode;
AH->format = fmt;
AH->compression = compression; AH->compression = compression;
AH->ArchiveEntryPtr = NULL; AH->pgCopyBuf = createPQExpBuffer();
AH->sqlBuf = createPQExpBuffer();
AH->StartDataPtr = NULL;
AH->WriteDataPtr = NULL;
AH->EndDataPtr = NULL;
AH->WriteBytePtr = NULL;
AH->ReadBytePtr = NULL;
AH->WriteBufPtr = NULL;
AH->ReadBufPtr = NULL;
AH->ClosePtr = NULL;
AH->WriteExtraTocPtr = NULL;
AH->ReadExtraTocPtr = NULL;
AH->PrintExtraTocPtr = NULL;
AH->readHeader = 0;
/* Open stdout with no compression for AH output handle */ /* Open stdout with no compression for AH output handle */
AH->gzOut = 0; AH->gzOut = 0;
AH->OF = stdout; AH->OF = stdout;
/*
*fprintf(stderr, "%s: archive format is %d\n", progname, fmt);
*/
if (fmt == archUnknown) if (fmt == archUnknown)
fmt = _discoverArchiveFormat(AH); AH->format = _discoverArchiveFormat(AH);
else
AH->format = fmt;
switch (fmt) { switch (AH->format) {
case archCustom: case archCustom:
InitArchiveFmt_Custom(AH); InitArchiveFmt_Custom(AH);
...@@ -812,12 +1153,16 @@ static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt, ...@@ -812,12 +1153,16 @@ static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt,
InitArchiveFmt_Files(AH); InitArchiveFmt_Files(AH);
break; break;
case archPlainText: case archNull:
InitArchiveFmt_PlainText(AH); InitArchiveFmt_Null(AH);
break;
case archTar:
InitArchiveFmt_Tar(AH);
break; break;
default: default:
die_horribly("Archiver: Unrecognized file format '%d'\n", fmt); die_horribly(AH, "Archiver: Unrecognized file format '%d'\n", fmt);
} }
return AH; return AH;
...@@ -827,13 +1172,25 @@ static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt, ...@@ -827,13 +1172,25 @@ static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt,
void WriteDataChunks(ArchiveHandle* AH) void WriteDataChunks(ArchiveHandle* AH)
{ {
TocEntry *te = AH->toc->next; TocEntry *te = AH->toc->next;
StartDataPtr startPtr;
EndDataPtr endPtr;
while (te != AH->toc) { while (te != AH->toc) {
if (te->dataDumper != NULL) { if (te->dataDumper != NULL) {
AH->currToc = te; AH->currToc = te;
/* printf("Writing data for %d (%x)\n", te->id, te); */ /* printf("Writing data for %d (%x)\n", te->id, te); */
if (AH->StartDataPtr != NULL) {
(*AH->StartDataPtr)(AH, te); if (strcmp(te->desc, "BLOBS") == 0)
{
startPtr = AH->StartBlobsPtr;
endPtr = AH->EndBlobsPtr;
} else {
startPtr = AH->StartDataPtr;
endPtr = AH->EndDataPtr;
}
if (startPtr != NULL) {
(*startPtr)(AH, te);
} }
/* printf("Dumper arg for %d is %x\n", te->id, te->dataDumperArg); */ /* printf("Dumper arg for %d is %x\n", te->id, te->dataDumperArg); */
...@@ -842,8 +1199,8 @@ void WriteDataChunks(ArchiveHandle* AH) ...@@ -842,8 +1199,8 @@ void WriteDataChunks(ArchiveHandle* AH)
*/ */
(*te->dataDumper)((Archive*)AH, te->oid, te->dataDumperArg); (*te->dataDumper)((Archive*)AH, te->oid, te->dataDumperArg);
if (AH->EndDataPtr != NULL) { if (endPtr != NULL) {
(*AH->EndDataPtr)(AH, te); (*endPtr)(AH, te);
} }
AH->currToc = NULL; AH->currToc = NULL;
} }
...@@ -866,6 +1223,7 @@ void WriteToc(ArchiveHandle* AH) ...@@ -866,6 +1223,7 @@ void WriteToc(ArchiveHandle* AH)
WriteStr(AH, te->desc); WriteStr(AH, te->desc);
WriteStr(AH, te->defn); WriteStr(AH, te->defn);
WriteStr(AH, te->dropStmt); WriteStr(AH, te->dropStmt);
WriteStr(AH, te->copyStmt);
WriteStr(AH, te->owner); WriteStr(AH, te->owner);
if (AH->WriteExtraTocPtr) { if (AH->WriteExtraTocPtr) {
(*AH->WriteExtraTocPtr)(AH, te); (*AH->WriteExtraTocPtr)(AH, te);
...@@ -884,12 +1242,12 @@ void ReadToc(ArchiveHandle* AH) ...@@ -884,12 +1242,12 @@ void ReadToc(ArchiveHandle* AH)
for( i = 0 ; i < AH->tocCount ; i++) { for( i = 0 ; i < AH->tocCount ; i++) {
te = (TocEntry*)malloc(sizeof(TocEntry)); te = (TocEntry*)calloc(1, sizeof(TocEntry));
te->id = ReadInt(AH); te->id = ReadInt(AH);
/* Sanity check */ /* Sanity check */
if (te->id <= 0 || te->id > AH->tocCount) if (te->id <= 0 || te->id > AH->tocCount)
die_horribly("Archiver: failed sanity check (bad entry id) - perhaps a corrupt TOC\n"); die_horribly(AH, "Archiver: failed sanity check (bad entry id) - perhaps a corrupt TOC\n");
te->hadDumper = ReadInt(AH); te->hadDumper = ReadInt(AH);
te->oid = ReadStr(AH); te->oid = ReadStr(AH);
...@@ -898,10 +1256,18 @@ void ReadToc(ArchiveHandle* AH) ...@@ -898,10 +1256,18 @@ void ReadToc(ArchiveHandle* AH)
te->desc = ReadStr(AH); te->desc = ReadStr(AH);
te->defn = ReadStr(AH); te->defn = ReadStr(AH);
te->dropStmt = ReadStr(AH); te->dropStmt = ReadStr(AH);
if (AH->version >= K_VERS_1_3)
te->copyStmt = ReadStr(AH);
te->owner = ReadStr(AH); te->owner = ReadStr(AH);
if (AH->ReadExtraTocPtr) { if (AH->ReadExtraTocPtr) {
(*AH->ReadExtraTocPtr)(AH, te); (*AH->ReadExtraTocPtr)(AH, te);
} }
ahlog(AH, 3, "Read TOC entry %d (id %d) for %s %s\n", i, te->id, te->desc, te->name);
te->prev = AH->toc->prev; te->prev = AH->toc->prev;
AH->toc->prev->next = te; AH->toc->prev->next = te;
AH->toc->prev = te; AH->toc->prev = te;
...@@ -911,7 +1277,7 @@ void ReadToc(ArchiveHandle* AH) ...@@ -911,7 +1277,7 @@ void ReadToc(ArchiveHandle* AH)
static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt) static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt)
{ {
int res = 3; /* Data and Schema */ int res = 3; /* Schema = 1, Data = 2, Both = 3 */
/* If it's an ACL, maybe ignore it */ /* If it's an ACL, maybe ignore it */
if (ropt->aclsSkip && strcmp(te->desc,"ACL") == 0) if (ropt->aclsSkip && strcmp(te->desc,"ACL") == 0)
...@@ -979,7 +1345,8 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) ...@@ -979,7 +1345,8 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
ahprintf(AH, "--\n\n"); ahprintf(AH, "--\n\n");
if (te->owner && strlen(te->owner) != 0 && strcmp(AH->currUser, te->owner) != 0) { if (te->owner && strlen(te->owner) != 0 && strcmp(AH->currUser, te->owner) != 0) {
ahprintf(AH, "\\connect - %s\n", te->owner); //todo pjw - fix for db connection...
//ahprintf(AH, "\\connect - %s\n", te->owner);
AH->currUser = te->owner; AH->currUser = te->owner;
} }
...@@ -990,6 +1357,8 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) ...@@ -990,6 +1357,8 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
void WriteHead(ArchiveHandle* AH) void WriteHead(ArchiveHandle* AH)
{ {
struct tm crtm;
(*AH->WriteBufPtr)(AH, "PGDMP", 5); /* Magic code */ (*AH->WriteBufPtr)(AH, "PGDMP", 5); /* Magic code */
(*AH->WriteBytePtr)(AH, AH->vmaj); (*AH->WriteBytePtr)(AH, AH->vmaj);
(*AH->WriteBytePtr)(AH, AH->vmin); (*AH->WriteBytePtr)(AH, AH->vmin);
...@@ -1003,26 +1372,35 @@ void WriteHead(ArchiveHandle* AH) ...@@ -1003,26 +1372,35 @@ void WriteHead(ArchiveHandle* AH)
"archive will be uncompressed \n", progname); "archive will be uncompressed \n", progname);
AH->compression = 0; AH->compression = 0;
(*AH->WriteBytePtr)(AH, 0);
#else
(*AH->WriteBytePtr)(AH, AH->compression);
#endif #endif
WriteInt(AH, AH->compression);
crtm = *localtime(&AH->createDate);
WriteInt(AH, crtm.tm_sec);
WriteInt(AH, crtm.tm_min);
WriteInt(AH, crtm.tm_hour);
WriteInt(AH, crtm.tm_mday);
WriteInt(AH, crtm.tm_mon);
WriteInt(AH, crtm.tm_year);
WriteInt(AH, crtm.tm_isdst);
WriteStr(AH, AH->dbname);
} }
void ReadHead(ArchiveHandle* AH) void ReadHead(ArchiveHandle* AH)
{ {
char tmpMag[7]; char tmpMag[7];
int fmt; int fmt;
struct tm crtm;
/* If we haven't already read the header... */
if (!AH->readHeader) { if (!AH->readHeader) {
(*AH->ReadBufPtr)(AH, tmpMag, 5); (*AH->ReadBufPtr)(AH, tmpMag, 5);
if (strncmp(tmpMag,"PGDMP", 5) != 0) if (strncmp(tmpMag,"PGDMP", 5) != 0)
die_horribly("Archiver: Did not fing magic PGDMP in file header\n"); die_horribly(AH, "Archiver: Did not fing magic PGDMP in file header\n");
AH->vmaj = (*AH->ReadBytePtr)(AH); AH->vmaj = (*AH->ReadBytePtr)(AH);
AH->vmin = (*AH->ReadBytePtr)(AH); AH->vmin = (*AH->ReadBytePtr)(AH);
...@@ -1038,35 +1416,58 @@ void ReadHead(ArchiveHandle* AH) ...@@ -1038,35 +1416,58 @@ void ReadHead(ArchiveHandle* AH)
if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX) if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
die_horribly("Archiver: unsupported version (%d.%d) in file header\n", AH->vmaj, AH->vmin); die_horribly(AH, "%s: unsupported version (%d.%d) in file header\n",
progname, AH->vmaj, AH->vmin);
AH->intSize = (*AH->ReadBytePtr)(AH); AH->intSize = (*AH->ReadBytePtr)(AH);
if (AH->intSize > 32) if (AH->intSize > 32)
die_horribly("Archiver: sanity check on integer size (%d) failes\n", AH->intSize); die_horribly(AH, "Archiver: sanity check on integer size (%d) failes\n", AH->intSize);
if (AH->intSize > sizeof(int)) if (AH->intSize > sizeof(int))
fprintf(stderr, "\nWARNING: Backup file was made on a machine with larger integers, " fprintf(stderr, "\n%s: WARNING - archive was made on a machine with larger integers, "
"some operations may fail\n"); "some operations may fail\n", progname);
fmt = (*AH->ReadBytePtr)(AH); fmt = (*AH->ReadBytePtr)(AH);
if (AH->format != fmt) if (AH->format != fmt)
die_horribly("Archiver: expected format (%d) differs from format found in file (%d)\n", die_horribly(AH, "%s: expected format (%d) differs from format found in file (%d)\n",
AH->format, fmt); progname, AH->format, fmt);
} }
if (AH->version >= K_VERS_1_2) if (AH->version >= K_VERS_1_2)
{ {
if (AH->version < K_VERS_1_4)
AH->compression = (*AH->ReadBytePtr)(AH); AH->compression = (*AH->ReadBytePtr)(AH);
else
AH->compression = ReadInt(AH);
} else { } else {
AH->compression = Z_DEFAULT_COMPRESSION; AH->compression = Z_DEFAULT_COMPRESSION;
} }
#ifndef HAVE_LIBZ #ifndef HAVE_LIBZ
if (AH->compression != 0) if (AH->compression != 0)
fprintf(stderr, "%s: WARNING - archive is compressed - any data will not be available\n", progname); fprintf(stderr, "%s: WARNING - archive is compressed - any data will not be available\n",
progname);
#endif #endif
if (AH->version >= K_VERS_1_4)
{
crtm.tm_sec = ReadInt(AH);
crtm.tm_min = ReadInt(AH);
crtm.tm_hour = ReadInt(AH);
crtm.tm_mday = ReadInt(AH);
crtm.tm_mon = ReadInt(AH);
crtm.tm_year = ReadInt(AH);
crtm.tm_isdst = ReadInt(AH);
AH->archdbname = ReadStr(AH);
AH->createDate = mktime(&crtm);
if (AH->createDate == (time_t)-1)
fprintf(stderr, "%s: WARNING - bad creation date in header\n", progname);
}
} }
...@@ -1144,5 +1545,12 @@ static int _tocSortCompareByIDNum(const void* p1, const void* p2) ...@@ -1144,5 +1545,12 @@ static int _tocSortCompareByIDNum(const void* p1, const void* p2)
} }
} }
/*
* Maybe I can use this somewhere...
*
*create table pgdump_blob_path(p text);
*insert into pgdump_blob_path values('/home/pjw/work/postgresql-cvs/pgsql/src/bin/pg_dump_140');
*
*insert into dump_blob_xref select 12345,lo_import(p || '/q.q') from pgdump_blob_path;
*/
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
#define __PG_BACKUP_ARCHIVE__ #define __PG_BACKUP_ARCHIVE__
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include "postgres.h"
#include "pqexpbuffer.h"
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
#include <zlib.h> #include <zlib.h>
...@@ -51,15 +55,23 @@ typedef z_stream *z_streamp; ...@@ -51,15 +55,23 @@ typedef z_stream *z_streamp;
#endif #endif
#include "pg_backup.h" #include "pg_backup.h"
#include "libpq-fe.h"
#define K_VERS_MAJOR 1 #define K_VERS_MAJOR 1
#define K_VERS_MINOR 2 #define K_VERS_MINOR 4
#define K_VERS_REV 2 #define K_VERS_REV 3
/* Data block types */
#define BLK_DATA 1
#define BLK_BLOB 2
#define BLK_BLOBS 3
/* Some important version numbers (checked in code) */ /* Some important version numbers (checked in code) */
#define K_VERS_1_0 (( (1 * 256 + 0) * 256 + 0) * 256 + 0) #define K_VERS_1_0 (( (1 * 256 + 0) * 256 + 0) * 256 + 0)
#define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) #define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) /* Allow No ZLIB */
#define K_VERS_MAX (( (1 * 256 + 2) * 256 + 255) * 256 + 0) #define K_VERS_1_3 (( (1 * 256 + 3) * 256 + 0) * 256 + 0) /* BLOBs */
#define K_VERS_1_4 (( (1 * 256 + 4) * 256 + 0) * 256 + 0) /* Date & name in header */
#define K_VERS_MAX (( (1 * 256 + 4) * 256 + 255) * 256 + 0)
struct _archiveHandle; struct _archiveHandle;
struct _tocEntry; struct _tocEntry;
...@@ -72,6 +84,11 @@ typedef void (*StartDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te); ...@@ -72,6 +84,11 @@ typedef void (*StartDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef int (*WriteDataPtr) (struct _archiveHandle* AH, const void* data, int dLen); typedef int (*WriteDataPtr) (struct _archiveHandle* AH, const void* data, int dLen);
typedef void (*EndDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te); typedef void (*EndDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef void (*StartBlobsPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef void (*StartBlobPtr) (struct _archiveHandle* AH, struct _tocEntry* te, int oid);
typedef void (*EndBlobPtr) (struct _archiveHandle* AH, struct _tocEntry* te, int oid);
typedef void (*EndBlobsPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef int (*WriteBytePtr) (struct _archiveHandle* AH, const int i); typedef int (*WriteBytePtr) (struct _archiveHandle* AH, const int i);
typedef int (*ReadBytePtr) (struct _archiveHandle* AH); typedef int (*ReadBytePtr) (struct _archiveHandle* AH);
typedef int (*WriteBufPtr) (struct _archiveHandle* AH, const void* c, int len); typedef int (*WriteBufPtr) (struct _archiveHandle* AH, const void* c, int len);
...@@ -83,6 +100,8 @@ typedef void (*PrintExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* t ...@@ -83,6 +100,8 @@ typedef void (*PrintExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* t
typedef void (*PrintTocDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te, typedef void (*PrintTocDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te,
RestoreOptions *ropt); RestoreOptions *ropt);
typedef int (*CustomOutPtr) (struct _archiveHandle* AH, const void* buf, int len);
typedef int (*TocSortCompareFn) (const void* te1, const void *te2); typedef int (*TocSortCompareFn) (const void* te1, const void *te2);
typedef enum _archiveMode { typedef enum _archiveMode {
...@@ -95,16 +114,44 @@ typedef struct _outputContext { ...@@ -95,16 +114,44 @@ typedef struct _outputContext {
int gzOut; int gzOut;
} OutputContext; } OutputContext;
typedef enum {
SQL_SCAN = 0,
SQL_IN_SQL_COMMENT,
SQL_IN_EXT_COMMENT,
SQL_IN_QUOTE} sqlparseState;
typedef struct {
int backSlash;
sqlparseState state;
char lastChar;
char quoteChar;
} sqlparseInfo;
typedef struct _archiveHandle { typedef struct _archiveHandle {
Archive public; /* Public part of archive */
char vmaj; /* Version of file */ char vmaj; /* Version of file */
char vmin; char vmin;
char vrev; char vrev;
int version; /* Conveniently formatted version */ int version; /* Conveniently formatted version */
int debugLevel; /* Not used. Intended for logging */
int intSize; /* Size of an integer in the archive */ int intSize; /* Size of an integer in the archive */
ArchiveFormat format; /* Archive format */ ArchiveFormat format; /* Archive format */
sqlparseInfo sqlparse;
PQExpBuffer sqlBuf;
time_t createDate; /* Date archive created */
/*
* Fields used when discovering header.
* A format can always get the previous read bytes from here...
*/
int readHeader; /* Used if file header has been read already */ int readHeader; /* Used if file header has been read already */
char *lookahead; /* Buffer used when reading header to discover format */
int lookaheadSize; /* Size of allocated buffer */
int lookaheadLen; /* Length of data in lookahead */
int lookaheadPos; /* Current read position in lookahead buffer */
ArchiveEntryPtr ArchiveEntryPtr; /* Called for each metadata object */ ArchiveEntryPtr ArchiveEntryPtr; /* Called for each metadata object */
StartDataPtr StartDataPtr; /* Called when table data is about to be dumped */ StartDataPtr StartDataPtr; /* Called when table data is about to be dumped */
...@@ -121,6 +168,28 @@ typedef struct _archiveHandle { ...@@ -121,6 +168,28 @@ typedef struct _archiveHandle {
PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */ PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */
PrintTocDataPtr PrintTocDataPtr; PrintTocDataPtr PrintTocDataPtr;
StartBlobsPtr StartBlobsPtr;
EndBlobsPtr EndBlobsPtr;
StartBlobPtr StartBlobPtr;
EndBlobPtr EndBlobPtr;
CustomOutPtr CustomOutPtr; /* Alternate script output routine */
/* Stuff for direct DB connection */
char username[100];
char *dbname; /* Name of db for connection */
char *archdbname; /* DB name *read* from archive */
char *pghost;
char *pgport;
PGconn *connection;
int connectToDB; /* Flag to indicate if direct DB connection is required */
int pgCopyIn; /* Currently in libpq 'COPY IN' mode. */
PQExpBuffer pgCopyBuf; /* Left-over data from incomplete lines in COPY IN */
int loFd; /* BLOB fd */
int writingBlob; /* Flag */
int createdBlobXref; /* Flag */
int lastID; /* Last internal ID for a TOC entry */ int lastID; /* Last internal ID for a TOC entry */
char* fSpec; /* Archive File Spec */ char* fSpec; /* Archive File Spec */
FILE *FH; /* General purpose file handle */ FILE *FH; /* General purpose file handle */
...@@ -135,6 +204,7 @@ typedef struct _archiveHandle { ...@@ -135,6 +204,7 @@ typedef struct _archiveHandle {
ArchiveMode mode; /* File mode - r or w */ ArchiveMode mode; /* File mode - r or w */
void* formatData; /* Header data specific to file format */ void* formatData; /* Header data specific to file format */
RestoreOptions *ropt; /* Used to check restore options in ahwrite etc */
} ArchiveHandle; } ArchiveHandle;
typedef struct _tocEntry { typedef struct _tocEntry {
...@@ -148,6 +218,7 @@ typedef struct _tocEntry { ...@@ -148,6 +218,7 @@ typedef struct _tocEntry {
char* desc; char* desc;
char* defn; char* defn;
char* dropStmt; char* dropStmt;
char* copyStmt;
char* owner; char* owner;
char** depOid; char** depOid;
int printed; /* Indicates if entry defn has been dumped */ int printed; /* Indicates if entry defn has been dumped */
...@@ -159,7 +230,8 @@ typedef struct _tocEntry { ...@@ -159,7 +230,8 @@ typedef struct _tocEntry {
} TocEntry; } TocEntry;
extern void die_horribly(const char *fmt, ...); /* Used everywhere */
extern void die_horribly(ArchiveHandle *AH, const char *fmt, ...);
extern void WriteTOC(ArchiveHandle* AH); extern void WriteTOC(ArchiveHandle* AH);
extern void ReadTOC(ArchiveHandle* AH); extern void ReadTOC(ArchiveHandle* AH);
...@@ -180,9 +252,15 @@ extern int ReadInt(ArchiveHandle* AH); ...@@ -180,9 +252,15 @@ extern int ReadInt(ArchiveHandle* AH);
extern char* ReadStr(ArchiveHandle* AH); extern char* ReadStr(ArchiveHandle* AH);
extern int WriteStr(ArchiveHandle* AH, char* s); extern int WriteStr(ArchiveHandle* AH, char* s);
extern void StartRestoreBlob(ArchiveHandle* AH, int oid);
extern void EndRestoreBlob(ArchiveHandle* AH, int oid);
extern void InitArchiveFmt_Custom(ArchiveHandle* AH); extern void InitArchiveFmt_Custom(ArchiveHandle* AH);
extern void InitArchiveFmt_Files(ArchiveHandle* AH); extern void InitArchiveFmt_Files(ArchiveHandle* AH);
extern void InitArchiveFmt_PlainText(ArchiveHandle* AH); extern void InitArchiveFmt_Null(ArchiveHandle* AH);
extern void InitArchiveFmt_Tar(ArchiveHandle* AH);
extern int isValidTarHeader(char *header);
extern OutputContext SetOutput(ArchiveHandle* AH, char *filename, int compression); extern OutputContext SetOutput(ArchiveHandle* AH, char *filename, int compression);
extern void ResetOutput(ArchiveHandle* AH, OutputContext savedContext); extern void ResetOutput(ArchiveHandle* AH, OutputContext savedContext);
...@@ -190,4 +268,6 @@ extern void ResetOutput(ArchiveHandle* AH, OutputContext savedContext); ...@@ -190,4 +268,6 @@ extern void ResetOutput(ArchiveHandle* AH, OutputContext savedContext);
int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH); int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH);
int ahprintf(ArchiveHandle* AH, const char *fmt, ...); int ahprintf(ArchiveHandle* AH, const char *fmt, ...);
void ahlog(ArchiveHandle* AH, int level, const char *fmt, ...);
#endif #endif
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
* *
* Implements the custom output format. * Implements the custom output format.
* *
* The comments with the routined in this code are a good place to
* understand how to write a new format.
*
* See the headers to pg_restore for more details. * See the headers to pg_restore for more details.
* *
* Copyright (c) 2000, Philip Warner * Copyright (c) 2000, Philip Warner
...@@ -11,7 +14,8 @@ ...@@ -11,7 +14,8 @@
* as this notice is not removed. * as this notice is not removed.
* *
* The author is not responsible for loss or damages that may * The author is not responsible for loss or damages that may
* result from it's use. * and any liability will be limited to the time taken to fix any
* related bug.
* *
* *
* IDENTIFICATION * IDENTIFICATION
...@@ -26,8 +30,12 @@ ...@@ -26,8 +30,12 @@
#include <stdlib.h> #include <stdlib.h>
#include "pg_backup.h" #include "pg_backup.h"
#include "pg_backup_archiver.h" #include "pg_backup_archiver.h"
#include <errno.h>
extern int errno; /*--------
* Routines in the format interface
*--------
*/
static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te); static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);
static void _StartData(ArchiveHandle* AH, TocEntry* te); static void _StartData(ArchiveHandle* AH, TocEntry* te);
...@@ -45,7 +53,19 @@ static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te); ...@@ -45,7 +53,19 @@ static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _PrintData(ArchiveHandle* AH); static void _PrintData(ArchiveHandle* AH);
static void _skipData(ArchiveHandle* AH); static void _skipData(ArchiveHandle* AH);
static void _skipBlobs(ArchiveHandle* AH);
static void _StartBlobs(ArchiveHandle* AH, TocEntry* te);
static void _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid);
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid);
static void _EndBlobs(ArchiveHandle* AH, TocEntry* te);
static void _LoadBlobs(ArchiveHandle* AH);
/*------------
* Buffers used in zlib compression and extra data stored in archive and
* in TOC entries.
*------------
*/
#define zlibOutSize 4096 #define zlibOutSize 4096
#define zlibInSize 4096 #define zlibInSize 4096
...@@ -64,12 +84,30 @@ typedef struct { ...@@ -64,12 +84,30 @@ typedef struct {
int dataLen; int dataLen;
} lclTocEntry; } lclTocEntry;
/*------
* Static declarations
*------
*/
static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id);
static void _StartDataCompressor(ArchiveHandle* AH, TocEntry* te);
static void _EndDataCompressor(ArchiveHandle* AH, TocEntry* te);
static int _getFilePos(ArchiveHandle* AH, lclContext* ctx); static int _getFilePos(ArchiveHandle* AH, lclContext* ctx);
static int _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush);
static char* progname = "Archiver(custom)"; static char* progname = "Archiver(custom)";
/* /*
* Handler functions. * Init routine required by ALL formats. This is a global routine
* and should be declared in pg_backup_archiver.h
*
* It's task is to create any extra archive context (using AH->formatData),
* and to initialize the supported function pointers.
*
* It should also prepare whatever it's input source is for reading/writing,
* and in the case of a read mode connection, it should load the Header & TOC.
*/ */
void InitArchiveFmt_Custom(ArchiveHandle* AH) void InitArchiveFmt_Custom(ArchiveHandle* AH)
{ {
...@@ -90,17 +128,22 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH) ...@@ -90,17 +128,22 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH)
AH->WriteExtraTocPtr = _WriteExtraToc; AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc; AH->PrintExtraTocPtr = _PrintExtraToc;
AH->StartBlobsPtr = _StartBlobs;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
AH->EndBlobsPtr = _EndBlobs;
/* /*
* Set up some special context used in compressing data. * Set up some special context used in compressing data.
*/ */
ctx = (lclContext*)malloc(sizeof(lclContext)); ctx = (lclContext*)malloc(sizeof(lclContext));
if (ctx == NULL) if (ctx == NULL)
die_horribly("%s: Unable to allocate archive context",progname); die_horribly(AH, "%s: Unable to allocate archive context",progname);
AH->formatData = (void*)ctx; AH->formatData = (void*)ctx;
ctx->zp = (z_streamp)malloc(sizeof(z_stream)); ctx->zp = (z_streamp)malloc(sizeof(z_stream));
if (ctx->zp == NULL) if (ctx->zp == NULL)
die_horribly("%s: unable to allocate zlib stream archive context",progname); die_horribly(AH, "%s: unable to allocate zlib stream archive context",progname);
ctx->zlibOut = (char*)malloc(zlibOutSize); ctx->zlibOut = (char*)malloc(zlibOutSize);
ctx->zlibIn = (char*)malloc(zlibInSize); ctx->zlibIn = (char*)malloc(zlibInSize);
...@@ -108,12 +151,13 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH) ...@@ -108,12 +151,13 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH)
ctx->filePos = 0; ctx->filePos = 0;
if (ctx->zlibOut == NULL || ctx->zlibIn == NULL) if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)
die_horribly("%s: unable to allocate buffers in archive context",progname); die_horribly(AH, "%s: unable to allocate buffers in archive context",progname);
/* /*
* Now open the file * Now open the file
*/ */
if (AH->mode == archModeWrite) { if (AH->mode == archModeWrite) {
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
AH->FH = fopen(AH->fSpec, PG_BINARY_W); AH->FH = fopen(AH->fSpec, PG_BINARY_W);
} else { } else {
...@@ -121,18 +165,19 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH) ...@@ -121,18 +165,19 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH)
} }
if (!AH) if (!AH)
die_horribly("%s: unable to open archive file %s",progname, AH->fSpec); die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0); ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
} else { } else {
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
AH->FH = fopen(AH->fSpec, PG_BINARY_R); AH->FH = fopen(AH->fSpec, PG_BINARY_R);
} else { } else {
AH->FH = stdin; AH->FH = stdin;
} }
if (!AH) if (!AH)
die_horribly("%s: unable to open archive file %s",progname, AH->fSpec); die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0); ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
...@@ -144,13 +189,17 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH) ...@@ -144,13 +189,17 @@ void InitArchiveFmt_Custom(ArchiveHandle* AH)
} }
/* /*
* - Start a new TOC entry * Called by the Archiver when the dumper creates a new TOC entry.
*
* Optional.
*
* Set up extrac format-related TOC data.
*/ */
static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx; lclTocEntry* ctx;
ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry)); ctx = (lclTocEntry*)calloc(1, sizeof(lclTocEntry));
if (te->dataDumper) { if (te->dataDumper) {
ctx->dataPos = -1; ctx->dataPos = -1;
} else { } else {
...@@ -161,6 +210,15 @@ static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) ...@@ -161,6 +210,15 @@ static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te)
} }
/*
* Called by the Archiver to save any extra format-related TOC entry
* data.
*
* Optional.
*
* Use the Archiver routines to write data - they are non-endian, and
* maintain other important file information.
*/
static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te) static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx = (lclTocEntry*)te->formatData; lclTocEntry* ctx = (lclTocEntry*)te->formatData;
...@@ -169,6 +227,14 @@ static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te) ...@@ -169,6 +227,14 @@ static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)
WriteInt(AH, ctx->dataLen); WriteInt(AH, ctx->dataLen);
} }
/*
* Called by the Archiver to read any extra format-related TOC data.
*
* Optional.
*
* Needs to match the order defined in _WriteExtraToc, and sould also
* use the Archiver input routines.
*/
static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te) static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx = (lclTocEntry*)te->formatData; lclTocEntry* ctx = (lclTocEntry*)te->formatData;
...@@ -180,9 +246,15 @@ static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te) ...@@ -180,9 +246,15 @@ static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)
ctx->dataPos = ReadInt( AH ); ctx->dataPos = ReadInt( AH );
ctx->dataLen = ReadInt( AH ); ctx->dataLen = ReadInt( AH );
} }
/*
* Called by the Archiver when restoring an archive to output a comment
* that includes useful information about the TOC entry.
*
* Optional.
*
*/
static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te) static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx = (lclTocEntry*)te->formatData; lclTocEntry* ctx = (lclTocEntry*)te->formatData;
...@@ -190,101 +262,40 @@ static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te) ...@@ -190,101 +262,40 @@ static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)
ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen); ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen);
} }
/*
* Called by the archiver when saving TABLE DATA (not schema). This routine
* should save whatever format-specific information is needed to read
* the archive back.
*
* It is called just prior to the dumper's 'DataDumper' routine being called.
*
* Optional, but strongly recommended.
*
*/
static void _StartData(ArchiveHandle* AH, TocEntry* te) static void _StartData(ArchiveHandle* AH, TocEntry* te)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
z_streamp zp = ctx->zp;
lclTocEntry* tctx = (lclTocEntry*)te->formatData; lclTocEntry* tctx = (lclTocEntry*)te->formatData;
tctx->dataPos = _getFilePos(AH, ctx); tctx->dataPos = _getFilePos(AH, ctx);
_WriteByte(AH, BLK_DATA); /* Block type */
WriteInt(AH, te->id); /* For sanity check */ WriteInt(AH, te->id); /* For sanity check */
#ifdef HAVE_LIBZ _StartDataCompressor(AH, te);
if (AH->compression < 0 || AH->compression > 9) {
AH->compression = Z_DEFAULT_COMPRESSION;
}
if (AH->compression != 0) {
zp->zalloc = Z_NULL;
zp->zfree = Z_NULL;
zp->opaque = Z_NULL;
if (deflateInit(zp, AH->compression) != Z_OK)
die_horribly("%s: could not initialize compression library - %s\n",progname, zp->msg);
}
#else
AH->compression = 0;
#endif
/* Just be paranoid - maye End is called after Start, with no Write */
zp->next_out = ctx->zlibOut;
zp->avail_out = zlibOutSize;
} }
static int _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush) /*
{ * Called by archiver when dumper calls WriteData. This routine is
z_streamp zp = ctx->zp; * called for both BLOB and TABLE data; it is the responsibility of
* the format to manage each kind of data using StartBlob/StartData.
#ifdef HAVE_LIBZ *
char* out = ctx->zlibOut; * It should only be called from withing a DataDumper routine.
int res = Z_OK; *
* Mandatory.
if (AH->compression != 0) *
{
res = deflate(zp, flush);
if (res == Z_STREAM_ERROR)
die_horribly("%s: could not compress data - %s\n",progname, zp->msg);
if ( ( (flush == Z_FINISH) && (zp->avail_out < zlibOutSize) )
|| (zp->avail_out == 0)
|| (zp->avail_in != 0)
)
{
/*
* Extra paranoia: avoid zero-length chunks since a zero
* length chunk is the EOF marker. This should never happen
* but...
*/ */
if (zp->avail_out < zlibOutSize) {
/* printf("Wrote %d byte deflated chunk\n", zlibOutSize - zp->avail_out); */
WriteInt(AH, zlibOutSize - zp->avail_out);
fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH);
ctx->filePos += zlibOutSize - zp->avail_out;
}
zp->next_out = out;
zp->avail_out = zlibOutSize;
}
} else {
#endif
if (zp->avail_in > 0)
{
WriteInt(AH, zp->avail_in);
fwrite(zp->next_in, 1, zp->avail_in, AH->FH);
ctx->filePos += zp->avail_in;
zp->avail_in = 0;
} else {
#ifdef HAVE_LIBZ
if (flush == Z_FINISH)
res = Z_STREAM_END;
#endif
}
#ifdef HAVE_LIBZ
}
return res;
#else
return 1;
#endif
}
static int _WriteData(ArchiveHandle* AH, const void* data, int dLen) static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -300,35 +311,82 @@ static int _WriteData(ArchiveHandle* AH, const void* data, int dLen) ...@@ -300,35 +311,82 @@ static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
return dLen; return dLen;
} }
/*
* Called by the archiver when a dumper's 'DataDumper' routine has
* finished.
*
* Optional.
*
*/
static void _EndData(ArchiveHandle* AH, TocEntry* te) static void _EndData(ArchiveHandle* AH, TocEntry* te)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
lclTocEntry* tctx = (lclTocEntry*) te->formatData; lclTocEntry* tctx = (lclTocEntry*) te->formatData;
#ifdef HAVE_LIBZ _EndDataCompressor(AH, te);
z_streamp zp = ctx->zp;
int res;
if (AH->compression != 0) tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;
{ }
zp->next_in = NULL;
zp->avail_in = 0;
do { /*
/* printf("Ending data output\n"); */ * Called by the archiver when starting to save all BLOB DATA (not schema).
res = _DoDeflate(AH, ctx, Z_FINISH); * This routine should save whatever format-specific information is needed
} while (res != Z_STREAM_END); * 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;
lclTocEntry* tctx = (lclTocEntry*)te->formatData;
if (deflateEnd(zp) != Z_OK) tctx->dataPos = _getFilePos(AH, ctx);
die_horribly("%s: error closing compression stream - %s\n", progname, zp->msg);
}
#endif
/* Send the end marker */ _WriteByte(AH, BLK_BLOBS); /* Block type */
WriteInt(AH, 0); WriteInt(AH, te->id); /* For sanity check */
tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos; }
/*
* Called by the archiver when the dumper calls StartBlob.
*
* Mandatory.
*
* Must save the passed OID for retrieval at restore-time.
*/
static void _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid)
{
if (oid == 0)
die_horribly(AH, "%s: illegal OID for BLOB (%d)\n", progname, oid);
WriteInt(AH, oid);
_StartDataCompressor(AH, te);
}
/*
* Called by the archiver when the dumper calls EndBlob.
*
* Optional.
*
*/
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid)
{
_EndDataCompressor(AH, te);
}
/*
* Called by the archiver when finishing saving all BLOB DATA.
*
* Optional.
*
*/
static void _EndBlobs(ArchiveHandle* AH, TocEntry* te)
{
/* Write out a fake zero OID to mark end-of-blobs. */
WriteInt(AH, 0);
} }
/* /*
...@@ -339,37 +397,86 @@ static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) ...@@ -339,37 +397,86 @@ static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
int id; int id;
lclTocEntry* tctx = (lclTocEntry*) te->formatData; lclTocEntry* tctx = (lclTocEntry*) te->formatData;
int blkType;
int found = 0;
if (tctx->dataPos == 0) if (tctx->dataPos == 0)
return; return;
if (!ctx->hasSeek || tctx->dataPos < 0) { if (!ctx->hasSeek || tctx->dataPos < 0) {
id = ReadInt(AH);
/* Skip over unnecessary blocks until we get the one we want. */
found = 0;
_readBlockHeader(AH, &blkType, &id);
while (id != te->id) { while (id != te->id) {
if (TocIDRequired(AH, id, ropt) & 2)
die_horribly("%s: Dumping a specific TOC data block out of order is not supported" if ( (TocIDRequired(AH, id, ropt) & 2) != 0)
die_horribly(AH, "%s: Dumping a specific TOC data block out of order is not supported"
" without on this input stream (fseek required)\n", progname); " without on this input stream (fseek required)\n", progname);
switch (blkType) {
case BLK_DATA:
_skipData(AH); _skipData(AH);
id = ReadInt(AH); break;
case BLK_BLOBS:
_skipBlobs(AH);
break;
default: /* Always have a default */
die_horribly(AH, "%s: unrecognized data block type while searching archive %d\n",
progname, blkType);
break;
} }
_readBlockHeader(AH, &blkType, &id);
}
} else { } else {
/* Grab it */
if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0) if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)
die_horribly("%s: error %d in file seek\n",progname, errno); die_horribly(AH, "%s: error %d in file seek\n",progname, errno);
id = ReadInt(AH); _readBlockHeader(AH, &blkType, &id);
} }
/* Are we sane? */
if (id != te->id) if (id != te->id)
die_horribly("%s: Found unexpected block ID (%d) when reading data - expected %d\n", die_horribly(AH, "%s: Found unexpected block ID (%d) when reading data - expected %d\n",
progname, id, te->id); progname, id, te->id);
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n", switch (blkType) {
te->id, te->oid, te->desc, te->name);
case BLK_DATA:
_PrintData(AH); _PrintData(AH);
break;
case BLK_BLOBS:
if (!AH->connection)
die_horribly(AH, "%s: BLOBs can not be loaded without a database connection\n", progname);
_LoadBlobs(AH);
break;
default: /* Always have a default */
die_horribly(AH, "%s: unrecognized data block type %d while restoring archive\n",
progname, blkType);
break;
}
ahprintf(AH, "\n\n"); ahprintf(AH, "\n\n");
} }
...@@ -384,11 +491,12 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -384,11 +491,12 @@ static void _PrintData(ArchiveHandle* AH)
int blkLen; int blkLen;
char* in = ctx->zlibIn; char* in = ctx->zlibIn;
int cnt; int cnt;
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
int res; int res;
char* out = ctx->zlibOut; char* out = ctx->zlibOut;
#endif
#ifdef HAVE_LIBZ
res = Z_OK; res = Z_OK;
...@@ -398,26 +506,27 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -398,26 +506,27 @@ static void _PrintData(ArchiveHandle* AH)
zp->opaque = Z_NULL; zp->opaque = Z_NULL;
if (inflateInit(zp) != Z_OK) if (inflateInit(zp) != Z_OK)
die_horribly("%s: could not initialize compression library - %s\n", progname, zp->msg); die_horribly(AH, "%s: could not initialize compression library - %s\n", progname, zp->msg);
} }
#endif #endif
blkLen = ReadInt(AH); blkLen = ReadInt(AH);
while (blkLen != 0) { while (blkLen != 0) {
if (blkLen > ctx->inSize) { if (blkLen > (ctx->inSize - 1)) {
free(ctx->zlibIn); free(ctx->zlibIn);
ctx->zlibIn = NULL; ctx->zlibIn = NULL;
ctx->zlibIn = (char*)malloc(blkLen); ctx->zlibIn = (char*)malloc(blkLen);
if (!ctx->zlibIn) if (!ctx->zlibIn)
die_horribly("%s: failed to allocate decompression buffer\n", progname); die_horribly(AH, "%s: failed to allocate decompression buffer\n", progname);
ctx->inSize = blkLen; ctx->inSize = blkLen;
in = ctx->zlibIn; in = ctx->zlibIn;
} }
cnt = fread(in, 1, blkLen, AH->FH); cnt = fread(in, 1, blkLen, AH->FH);
if (cnt != blkLen) if (cnt != blkLen)
die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt); die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
ctx->filePos += blkLen; ctx->filePos += blkLen;
...@@ -433,13 +542,14 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -433,13 +542,14 @@ static void _PrintData(ArchiveHandle* AH)
zp->avail_out = zlibOutSize; zp->avail_out = zlibOutSize;
res = inflate(zp, 0); res = inflate(zp, 0);
if (res != Z_OK && res != Z_STREAM_END) if (res != Z_OK && res != Z_STREAM_END)
die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg); die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
out[zlibOutSize - zp->avail_out] = '\0'; out[zlibOutSize - zp->avail_out] = '\0';
ahwrite(out, 1, zlibOutSize - zp->avail_out, AH); ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
} }
} else { } else {
#endif #endif
in[zp->avail_in] = '\0';
ahwrite(in, 1, zp->avail_in, AH); ahwrite(in, 1, zp->avail_in, AH);
zp->avail_in = 0; zp->avail_in = 0;
...@@ -448,6 +558,7 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -448,6 +558,7 @@ static void _PrintData(ArchiveHandle* AH)
#endif #endif
blkLen = ReadInt(AH); blkLen = ReadInt(AH);
} }
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
...@@ -460,7 +571,7 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -460,7 +571,7 @@ static void _PrintData(ArchiveHandle* AH)
zp->avail_out = zlibOutSize; zp->avail_out = zlibOutSize;
res = inflate(zp, 0); res = inflate(zp, 0);
if (res != Z_OK && res != Z_STREAM_END) if (res != Z_OK && res != Z_STREAM_END)
die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg); die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
out[zlibOutSize - zp->avail_out] = '\0'; out[zlibOutSize - zp->avail_out] = '\0';
ahwrite(out, 1, zlibOutSize - zp->avail_out, AH); ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
...@@ -470,8 +581,42 @@ static void _PrintData(ArchiveHandle* AH) ...@@ -470,8 +581,42 @@ static void _PrintData(ArchiveHandle* AH)
} }
static void _LoadBlobs(ArchiveHandle* AH)
{
int oid;
oid = ReadInt(AH);
while(oid != 0)
{
StartRestoreBlob(AH, oid);
_PrintData(AH);
EndRestoreBlob(AH, oid);
oid = ReadInt(AH);
}
}
/*
* Skip the BLOBs from the current file position.
* BLOBS are written sequentially as data blocks (see below).
* Each BLOB is preceded by it's original OID.
* A zero OID indicated the end of the BLOBS
*/
static void _skipBlobs(ArchiveHandle* AH)
{
int oid;
oid = ReadInt(AH);
while(oid != 0)
{
_skipData(AH);
oid = ReadInt(AH);
}
}
/* /*
* Skip data from current file position. * Skip data from current file position.
* Data blocks are formatted as an integer length, followed by data.
* A zero length denoted the end of the block.
*/ */
static void _skipData(ArchiveHandle* AH) static void _skipData(ArchiveHandle* AH)
{ {
...@@ -490,7 +635,7 @@ static void _skipData(ArchiveHandle* AH) ...@@ -490,7 +635,7 @@ static void _skipData(ArchiveHandle* AH)
} }
cnt = fread(in, 1, blkLen, AH->FH); cnt = fread(in, 1, blkLen, AH->FH);
if (cnt != blkLen) if (cnt != blkLen)
die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt); die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
ctx->filePos += blkLen; ctx->filePos += blkLen;
...@@ -499,6 +644,15 @@ static void _skipData(ArchiveHandle* AH) ...@@ -499,6 +644,15 @@ static void _skipData(ArchiveHandle* AH)
} }
/*
* Write a byte of data to the archive.
*
* Mandatory.
*
* Called by the archiver to do integer & byte output to the archive.
* These routines are only used to read & write headers & TOC.
*
*/
static int _WriteByte(ArchiveHandle* AH, const int i) static int _WriteByte(ArchiveHandle* AH, const int i)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -511,6 +665,15 @@ static int _WriteByte(ArchiveHandle* AH, const int i) ...@@ -511,6 +665,15 @@ static int _WriteByte(ArchiveHandle* AH, const int i)
return res; return res;
} }
/*
* Read a byte of data from the archive.
*
* Mandatory
*
* Called by the archiver to read bytes & integers from the archive.
* These routines are only used to read & write headers & TOC.
*
*/
static int _ReadByte(ArchiveHandle* AH) static int _ReadByte(ArchiveHandle* AH)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -523,6 +686,15 @@ static int _ReadByte(ArchiveHandle* AH) ...@@ -523,6 +686,15 @@ static int _ReadByte(ArchiveHandle* AH)
return res; return res;
} }
/*
* Write a buffer of data to the archive.
*
* Mandatory.
*
* Called by the archiver to write a block of bytes to the archive.
* These routines are only used to read & write headers & TOC.
*
*/
static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len) static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -532,6 +704,15 @@ static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len) ...@@ -532,6 +704,15 @@ static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len)
return res; return res;
} }
/*
* Read a block of bytes from the archive.
*
* Mandatory.
*
* Called by the archiver to read a block of bytes from the archive
* These routines are only used to read & write headers & TOC.
*
*/
static int _ReadBuf(ArchiveHandle* AH, void* buf, int len) static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -541,6 +722,21 @@ static int _ReadBuf(ArchiveHandle* AH, void* buf, int len) ...@@ -541,6 +722,21 @@ static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)
return res; return res;
} }
/*
* Close the archive.
*
* Mandatory.
*
* When writing the archive, this is the routine that actually starts
* the process of saving it to files. No data should be written prior
* to this point, since the user could sort the TOC after creating it.
*
* If an archive is to be written, this toutine must call:
* WriteHead to save the archive header
* WriteToc to save the TOC entries
* WriteDataChunks to save all DATA & BLOBs.
*
*/
static void _CloseArchive(ArchiveHandle* AH) static void _CloseArchive(ArchiveHandle* AH)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -567,6 +763,14 @@ static void _CloseArchive(ArchiveHandle* AH) ...@@ -567,6 +763,14 @@ static void _CloseArchive(ArchiveHandle* AH)
AH->FH = NULL; AH->FH = NULL;
} }
/*--------------------------------------------------
* END OF FORMAT CALLBACKS
*--------------------------------------------------
*/
/*
* Get the current position in the archive file.
*/
static int _getFilePos(ArchiveHandle* AH, lclContext* ctx) static int _getFilePos(ArchiveHandle* AH, lclContext* ctx)
{ {
int pos; int pos;
...@@ -581,4 +785,151 @@ static int _getFilePos(ArchiveHandle* AH, lclContext* ctx) ...@@ -581,4 +785,151 @@ static int _getFilePos(ArchiveHandle* AH, lclContext* ctx)
return pos; return pos;
} }
/*
* Read a data block header. The format changed in V1.3, so we
* put the code here for simplicity.
*/
static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
{
if (AH->version < K_VERS_1_3)
*type = BLK_DATA;
else
*type = _ReadByte(AH);;
*id = ReadInt(AH);
}
/*
* If zlib is available, then startit up. This is called from
* StartData & StartBlob. The buffers are setup in the Init routine.
*
*/
static void _StartDataCompressor(ArchiveHandle* AH, TocEntry* te)
{
lclContext* ctx = (lclContext*)AH->formatData;
z_streamp zp = ctx->zp;
#ifdef HAVE_LIBZ
if (AH->compression < 0 || AH->compression > 9) {
AH->compression = Z_DEFAULT_COMPRESSION;
}
if (AH->compression != 0) {
zp->zalloc = Z_NULL;
zp->zfree = Z_NULL;
zp->opaque = Z_NULL;
if (deflateInit(zp, AH->compression) != Z_OK)
die_horribly(AH, "%s: could not initialize compression library - %s\n",progname, zp->msg);
}
#else
AH->compression = 0;
#endif
/* Just be paranoid - maye End is called after Start, with no Write */
zp->next_out = ctx->zlibOut;
zp->avail_out = zlibOutSize;
}
/*
* Send compressed data to the output stream (via ahwrite).
* Each data chunk is preceded by it's length.
* In the case of Z0, or no zlib, just write the raw data.
*
*/
static int _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush)
{
z_streamp zp = ctx->zp;
#ifdef HAVE_LIBZ
char* out = ctx->zlibOut;
int res = Z_OK;
if (AH->compression != 0)
{
res = deflate(zp, flush);
if (res == Z_STREAM_ERROR)
die_horribly(AH, "%s: could not compress data - %s\n",progname, zp->msg);
if ( ( (flush == Z_FINISH) && (zp->avail_out < zlibOutSize) )
|| (zp->avail_out == 0)
|| (zp->avail_in != 0)
)
{
/*
* Extra paranoia: avoid zero-length chunks since a zero
* length chunk is the EOF marker. This should never happen
* but...
*/
if (zp->avail_out < zlibOutSize) {
/* printf("Wrote %d byte deflated chunk\n", zlibOutSize - zp->avail_out); */
WriteInt(AH, zlibOutSize - zp->avail_out);
fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH);
ctx->filePos += zlibOutSize - zp->avail_out;
}
zp->next_out = out;
zp->avail_out = zlibOutSize;
}
} else {
#endif
if (zp->avail_in > 0)
{
WriteInt(AH, zp->avail_in);
fwrite(zp->next_in, 1, zp->avail_in, AH->FH);
ctx->filePos += zp->avail_in;
zp->avail_in = 0;
} else {
#ifdef HAVE_LIBZ
if (flush == Z_FINISH)
res = Z_STREAM_END;
#endif
}
#ifdef HAVE_LIBZ
}
return res;
#else
return 1;
#endif
}
/*
* Terminate zlib context and flush it's buffers. If no zlib
* then just return.
*
*/
static void _EndDataCompressor(ArchiveHandle* AH, TocEntry* te)
{
#ifdef HAVE_LIBZ
lclContext* ctx = (lclContext*)AH->formatData;
z_streamp zp = ctx->zp;
int res;
if (AH->compression != 0)
{
zp->next_in = NULL;
zp->avail_in = 0;
do {
/* printf("Ending data output\n"); */
res = _DoDeflate(AH, ctx, Z_FINISH);
} while (res != Z_STREAM_END);
if (deflateEnd(zp) != Z_OK)
die_horribly(AH, "%s: error closing compression stream - %s\n", progname, zp->msg);
}
#endif
/* Send the end marker */
WriteInt(AH, 0);
}
...@@ -47,10 +47,17 @@ static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te); ...@@ -47,10 +47,17 @@ static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te); static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _PrintExtraToc(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, int oid);
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid);
static void _EndBlobs(ArchiveHandle* AH, TocEntry* te);
#define K_STD_BUF_SIZE 1024
typedef struct { typedef struct {
int hasSeek; int hasSeek;
int filePos; int filePos;
FILE *blobToc;
} lclContext; } lclContext;
typedef struct { typedef struct {
...@@ -62,6 +69,10 @@ typedef struct { ...@@ -62,6 +69,10 @@ typedef struct {
char *filename; char *filename;
} lclTocEntry; } lclTocEntry;
static char* progname = "Archiver(files)";
static void _LoadBlobs(ArchiveHandle* AH, RestoreOptions *ropt);
static void _getBlobTocEntry(ArchiveHandle* AH, int *oid, char *fname);
/* /*
* Initializer * Initializer
*/ */
...@@ -84,6 +95,11 @@ void InitArchiveFmt_Files(ArchiveHandle* AH) ...@@ -84,6 +95,11 @@ void InitArchiveFmt_Files(ArchiveHandle* AH)
AH->WriteExtraTocPtr = _WriteExtraToc; AH->WriteExtraTocPtr = _WriteExtraToc;
AH->PrintExtraTocPtr = _PrintExtraToc; AH->PrintExtraTocPtr = _PrintExtraToc;
AH->StartBlobsPtr = _StartBlobs;
AH->StartBlobPtr = _StartBlob;
AH->EndBlobPtr = _EndBlob;
AH->EndBlobsPtr = _EndBlobs;
/* /*
* Set up some special context used in compressing data. * Set up some special context used in compressing data.
*/ */
...@@ -95,6 +111,13 @@ void InitArchiveFmt_Files(ArchiveHandle* AH) ...@@ -95,6 +111,13 @@ void InitArchiveFmt_Files(ArchiveHandle* AH)
* Now open the TOC file * Now open the TOC file
*/ */
if (AH->mode == archModeWrite) { if (AH->mode == archModeWrite) {
fprintf(stderr, "\n*************************************************************\n"
"* WARNING: This format is for demonstration purposes. It is *\n"
"* not intended for general use. Files will be dumped *\n"
"* into the current working directory. *\n"
"***************************************************************\n\n");
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
AH->FH = fopen(AH->fSpec, PG_BINARY_W); AH->FH = fopen(AH->fSpec, PG_BINARY_W);
} else { } else {
...@@ -107,7 +130,8 @@ void InitArchiveFmt_Files(ArchiveHandle* AH) ...@@ -107,7 +130,8 @@ void InitArchiveFmt_Files(ArchiveHandle* AH)
} }
} else { } else { /* Read Mode */
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
AH->FH = fopen(AH->fSpec, PG_BINARY_R); AH->FH = fopen(AH->fSpec, PG_BINARY_R);
} else { } else {
...@@ -129,7 +153,7 @@ void InitArchiveFmt_Files(ArchiveHandle* AH) ...@@ -129,7 +153,7 @@ void InitArchiveFmt_Files(ArchiveHandle* AH)
static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx; lclTocEntry* ctx;
char fn[1024]; char fn[K_STD_BUF_SIZE];
ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry)); ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
if (te->dataDumper) { if (te->dataDumper) {
...@@ -218,35 +242,98 @@ static void _EndData(ArchiveHandle* AH, TocEntry* te) ...@@ -218,35 +242,98 @@ static void _EndData(ArchiveHandle* AH, TocEntry* te)
} }
/* /*
* Print data for a given TOC entry * Print data for a given file
*/ */
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) static void _PrintFileData(ArchiveHandle* AH, char *filename, RestoreOptions *ropt)
{ {
lclTocEntry* tctx = (lclTocEntry*) te->formatData;
char buf[4096]; char buf[4096];
int cnt; int cnt;
if (!tctx->filename) if (!filename)
return; return;
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
AH->FH = gzopen(tctx->filename,"rb"); AH->FH = gzopen(filename,"rb");
#else #else
AH->FH = fopen(tctx->filename,PG_BINARY_R); AH->FH = fopen(filename,PG_BINARY_R);
#endif #endif
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n", while ( (cnt = GZREAD(buf, 1, 4095, AH->FH)) > 0) {
te->id, te->oid, te->desc, te->name); buf[cnt] = '\0';
while ( (cnt = GZREAD(buf, 1, 4096, AH->FH)) > 0) {
ahwrite(buf, 1, cnt, AH); ahwrite(buf, 1, cnt, AH);
} }
GZCLOSE(AH->FH); GZCLOSE(AH->FH);
}
/*
* Print data for a given TOC entry
*/
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
{
lclTocEntry* tctx = (lclTocEntry*) te->formatData;
if (!tctx->filename)
return;
if (strcmp(te->desc, "BLOBS") == 0)
_LoadBlobs(AH, ropt);
else
{
_PrintFileData(AH, tctx->filename, ropt);
}
}
static void _getBlobTocEntry(ArchiveHandle* AH, int *oid, char fname[K_STD_BUF_SIZE])
{
lclContext* ctx = (lclContext*)AH->formatData;
char blobTe[K_STD_BUF_SIZE];
int fpos;
int eos;
if (fgets(&blobTe[0], K_STD_BUF_SIZE - 1, ctx->blobToc) != NULL)
{
*oid = atoi(blobTe);
fpos = strcspn(blobTe, " ");
ahprintf(AH, "\n\n"); strncpy(fname, &blobTe[fpos+1], K_STD_BUF_SIZE - 1);
eos = strlen(fname)-1;
if (fname[eos] == '\n')
fname[eos] = '\0';
} else {
*oid = 0;
fname[0] = '\0';
}
} }
static void _LoadBlobs(ArchiveHandle* AH, RestoreOptions *ropt)
{
int oid;
lclContext* ctx = (lclContext*)AH->formatData;
char fname[K_STD_BUF_SIZE];
ctx->blobToc = fopen("blobs.toc", PG_BINARY_R);
_getBlobTocEntry(AH, &oid, fname);
while(oid != 0)
{
StartRestoreBlob(AH, oid);
_PrintFileData(AH, fname, ropt);
EndRestoreBlob(AH, oid);
_getBlobTocEntry(AH, &oid, fname);
}
fclose(ctx->blobToc);
}
static int _WriteByte(ArchiveHandle* AH, const int i) static int _WriteByte(ArchiveHandle* AH, const int i)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
...@@ -284,6 +371,7 @@ static int _ReadBuf(ArchiveHandle* AH, void* buf, int len) ...@@ -284,6 +371,7 @@ static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)
{ {
lclContext* ctx = (lclContext*)AH->formatData; lclContext* ctx = (lclContext*)AH->formatData;
int res; int res;
res = fread(buf, 1, len, AH->FH); res = fread(buf, 1, len, AH->FH);
ctx->filePos += res; ctx->filePos += res;
return res; return res;
...@@ -301,3 +389,95 @@ static void _CloseArchive(ArchiveHandle* AH) ...@@ -301,3 +389,95 @@ static void _CloseArchive(ArchiveHandle* AH)
AH->FH = NULL; AH->FH = NULL;
} }
/*
* BLOB support
*/
/*
* 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);
}
/*
* Called by the archiver when the dumper calls StartBlob.
*
* Mandatory.
*
* Must save the passed OID for retrieval at restore-time.
*/
static void _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid)
{
lclContext* ctx = (lclContext*)AH->formatData;
lclTocEntry* tctx = (lclTocEntry*)te->formatData;
char fmode[10];
char fname[255];
char *sfx;
if (oid == 0)
die_horribly(AH, "%s: illegal OID for BLOB (%d)\n", progname, oid);
if (AH->compression != 0)
sfx = ".gz";
else
sfx = "";
sprintf(fmode, "wb%d", AH->compression);
sprintf(fname, "blob_%d.dat%s", oid, sfx);
fprintf(ctx->blobToc, "%d %s\n", oid, fname);
#ifdef HAVE_LIBZ
tctx->FH = gzopen(fname, fmode);
#else
tctx->FH = fopen(fname, PG_BINARY_W);
#endif
}
/*
* Called by the archiver when the dumper calls EndBlob.
*
* Optional.
*
*/
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid)
{
lclTocEntry* tctx = (lclTocEntry*)te->formatData;
GZCLOSE(tctx->FH);
}
/*
* 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); */
fclose(ctx->blobToc);
}
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.159 2000/07/17 03:05:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.160 2000/07/21 11:40:08 pjw Exp $
* *
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
* *
...@@ -61,12 +61,19 @@ ...@@ -61,12 +61,19 @@
* - Added a -Z option for compression level on compressed formats * - Added a -Z option for compression level on compressed formats
* - Restored '-f' in usage output * - Restored '-f' in usage output
* *
*------------------------------------------------------------------------- *
* Modifications - 17-Jul-2000 - Philip Warner pjw@rhyme.com.au
* - Support for BLOB output.
* - Sort archive by OID, put some items at end (out of OID order)
*
*-------------------------------------------------------------------------
*/ */
#include <unistd.h> /* for getopt() */ #include <unistd.h> /* for getopt() */
#include <ctype.h> #include <ctype.h>
#include "pg_backup.h"
#include "postgres.h" #include "postgres.h"
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
...@@ -84,6 +91,7 @@ ...@@ -84,6 +91,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "libpq-fe.h" #include "libpq-fe.h"
#include <libpq/libpq-fs.h>
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#include "strdup.h" #include "strdup.h"
#endif #endif
...@@ -109,6 +117,9 @@ static void setMaxOid(Archive *fout); ...@@ -109,6 +117,9 @@ static void setMaxOid(Archive *fout);
static void AddAcl(char *aclbuf, const char *keyword); static void AddAcl(char *aclbuf, const char *keyword);
static char *GetPrivileges(const char *s); static char *GetPrivileges(const char *s);
static int dumpBlobs(Archive *AH, char*, void*);
extern char *optarg; extern char *optarg;
extern int optind, extern int optind,
opterr; opterr;
...@@ -268,14 +279,26 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv) ...@@ -268,14 +279,26 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
if (oids == true) if (oids == true)
{ {
archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n", /*
fmtId(classname, force_quotes)); * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
* fmtId(classname, force_quotes));
*
* - Not used as of V1.3 (needs to be in ArchiveEntry call)
*
*/
sprintf(query, "COPY %s WITH OIDS TO stdout;\n", sprintf(query, "COPY %s WITH OIDS TO stdout;\n",
fmtId(classname, force_quotes)); fmtId(classname, force_quotes));
} }
else else
{ {
archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes)); /*
*archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes));
*
* - Not used as of V1.3 (needs to be in ArchiveEntry call)
*
*/
sprintf(query, "COPY %s TO stdout;\n", fmtId(classname, force_quotes)); sprintf(query, "COPY %s TO stdout;\n", fmtId(classname, force_quotes));
} }
res = PQexec(g_conn, query); res = PQexec(g_conn, query);
...@@ -452,19 +475,28 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv) ...@@ -452,19 +475,28 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
*/ */
static void static void
dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
const char *onlytable, const bool oids) const char *onlytable, const bool oids, const bool force_quotes)
{ {
int i; int i;
char *all_only; char *all_only;
DataDumperPtr dumpFn; DataDumperPtr dumpFn;
DumpContext *dumpCtx; DumpContext *dumpCtx;
char *oidsPart;
char copyBuf[512];
char *copyStmt;
if (onlytable == NULL) if (onlytable == NULL)
all_only = "all"; all_only = "all";
else else
all_only = "only"; all_only = "only";
if (oids == true)
oidsPart = "WITH OIDS ";
else
oidsPart = "";
if (g_verbose) if (g_verbose)
fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n", fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n",
g_comment_start, all_only, g_comment_start, all_only,
...@@ -514,112 +546,28 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, ...@@ -514,112 +546,28 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
dumpCtx->tblidx = i; dumpCtx->tblidx = i;
dumpCtx->oids = oids; dumpCtx->oids = oids;
if (!dumpData) if (!dumpData) /* Dump/restore using COPY */
{
dumpFn = dumpClasses_nodumpData; dumpFn = dumpClasses_nodumpData;
/* dumpClasses_nodumpData(fout, classname, oids); */ /* dumpClasses_nodumpData(fout, classname, oids); */
else sprintf(copyBuf, "COPY %s %s FROM stdin;\n", fmtId(tblinfo[i].relname, force_quotes),
oidsPart);
copyStmt = copyBuf;
}
else /* Restore using INSERT */
{
dumpFn = dumpClasses_dumpData; dumpFn = dumpClasses_dumpData;
/* dumpClasses_dumpData(fout, classname); */ /* dumpClasses_dumpData(fout, classname); */
copyStmt = NULL;
}
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false), ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
"TABLE DATA", NULL, "", "", tblinfo[i].usename, "TABLE DATA", NULL, "", "", copyStmt, tblinfo[i].usename,
dumpFn, dumpCtx); dumpFn, dumpCtx);
} }
} }
} }
static void
prompt_for_password(char *username, char *password)
{
char buf[512];
int length;
#ifdef HAVE_TERMIOS_H
struct termios t_orig,
t;
#endif
fprintf(stderr, "Username: ");
fflush(stderr);
fgets(username, 100, stdin);
length = strlen(username);
/* skip rest of the line */
if (length > 0 && username[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && username[length - 1] == '\n')
username[length - 1] = '\0';
#ifdef HAVE_TERMIOS_H
tcgetattr(0, &t);
t_orig = t;
t.c_lflag &= ~ECHO;
tcsetattr(0, TCSADRAIN, &t);
#endif
fprintf(stderr, "Password: ");
fflush(stderr);
fgets(password, 100, stdin);
#ifdef HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &t_orig);
#endif
length = strlen(password);
/* skip rest of the line */
if (length > 0 && password[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && password[length - 1] == '\n')
password[length - 1] = '\0';
fprintf(stderr, "\n\n");
}
static void
check_database_version(bool ignoreVersion)
{
PGresult *res;
double myversion;
const char *remoteversion_str;
double remoteversion;
myversion = strtod(PG_VERSION, NULL);
res = PQexec(g_conn, "SELECT version()");
if (!res ||
PQresultStatus(res) != PGRES_TUPLES_OK ||
PQntuples(res) != 1)
{
fprintf(stderr, "check_database_version(): command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
remoteversion_str = PQgetvalue(res, 0, 0);
remoteversion = strtod(remoteversion_str + 11, NULL);
if (myversion != remoteversion)
{
fprintf(stderr, "Database version: %s\npg_dump version: %s\n",
remoteversion_str, PG_VERSION);
if (ignoreVersion)
fprintf(stderr, "Proceeding despite version mismatch.\n");
else
{
fprintf(stderr, "Aborting because of version mismatch.\n"
"Use --ignore-version if you think it's safe to proceed anyway.\n");
exit_nicely(g_conn);
}
}
PQclear(res);
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
...@@ -634,20 +582,19 @@ main(int argc, char **argv) ...@@ -634,20 +582,19 @@ main(int argc, char **argv)
bool oids = false; bool oids = false;
TableInfo *tblinfo; TableInfo *tblinfo;
int numTables; int numTables;
char connect_string[512] = "";
char tmp_string[128];
char username[100];
char password[100];
bool use_password = false; bool use_password = false;
int compressLevel = -1; int compressLevel = -1;
bool ignore_version = false; bool ignore_version = false;
int plainText = 0; int plainText = 0;
int outputClean = 0; int outputClean = 0;
int outputBlobs = 0;
RestoreOptions *ropt; RestoreOptions *ropt;
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static struct option long_options[] = { static struct option long_options[] = {
{"data-only", no_argument, NULL, 'a'}, {"data-only", no_argument, NULL, 'a'},
{"blobs", no_argument, NULL, 'b' },
{"clean", no_argument, NULL, 'c'}, {"clean", no_argument, NULL, 'c'},
{"file", required_argument, NULL, 'f'}, {"file", required_argument, NULL, 'f'},
{"format", required_argument, NULL, 'F'}, {"format", required_argument, NULL, 'F'},
...@@ -686,6 +633,12 @@ main(int argc, char **argv) ...@@ -686,6 +633,12 @@ main(int argc, char **argv)
else else
progname = strrchr(argv[0], SEP_CHAR) + 1; progname = strrchr(argv[0], SEP_CHAR) + 1;
/* Set defaulty options based on progname */
if (strcmp(progname, "pg_backup") == 0)
{
format = "c";
outputBlobs = 1;
}
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?", long_options, &optindex)) != -1) while ((c = getopt_long(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?", long_options, &optindex)) != -1)
...@@ -698,6 +651,10 @@ main(int argc, char **argv) ...@@ -698,6 +651,10 @@ main(int argc, char **argv)
case 'a': /* Dump data only */ case 'a': /* Dump data only */
dataOnly = true; dataOnly = true;
break; break;
case 'b': /* Dump blobs */
outputBlobs = true;
break;
case 'c': /* clean (i.e., drop) schema prior to case 'c': /* clean (i.e., drop) schema prior to
* create */ * create */
outputClean = 1; outputClean = 1;
...@@ -843,7 +800,12 @@ main(int argc, char **argv) ...@@ -843,7 +800,12 @@ main(int argc, char **argv)
case 'p': case 'p':
case 'P': case 'P':
plainText = 1; plainText = 1;
g_fout = CreateArchive(filename, archPlainText, 0); g_fout = CreateArchive(filename, archNull, 0);
break;
case 't':
case 'T':
g_fout = CreateArchive(filename, archTar, compressLevel);
break; break;
default: default:
...@@ -860,53 +822,13 @@ main(int argc, char **argv) ...@@ -860,53 +822,13 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
/* find database */ /* Let the archiver know how noisy to be */
if (!(dbname = argv[optind]) && g_fout->verbose = g_verbose;
!(dbname = getenv("PGDATABASE")))
{
fprintf(stderr, "%s: no database name specified\n", progname);
exit(1);
}
/* g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname); */ dbname = argv[optind];
if (pghost != NULL)
{
sprintf(tmp_string, "host=%s ", pghost);
strcat(connect_string, tmp_string);
}
if (pgport != NULL)
{
sprintf(tmp_string, "port=%s ", pgport);
strcat(connect_string, tmp_string);
}
if (dbname != NULL)
{
sprintf(tmp_string, "dbname=%s ", dbname);
strcat(connect_string, tmp_string);
}
if (use_password)
{
prompt_for_password(username, password);
strcat(connect_string, "authtype=password ");
sprintf(tmp_string, "user=%s ", username);
strcat(connect_string, tmp_string);
sprintf(tmp_string, "password=%s ", password);
strcat(connect_string, tmp_string);
MemSet(tmp_string, 0, sizeof(tmp_string));
MemSet(password, 0, sizeof(password));
}
g_conn = PQconnectdb(connect_string);
MemSet(connect_string, 0, sizeof(connect_string));
/* check to see that the backend connection was successfully made */
if (PQstatus(g_conn) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
fprintf(stderr, "%s\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* check for version mismatch */ /* Open the database using the Archiver, so it knows about it. Errors mean death */
check_database_version(ignore_version); g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, use_password, ignore_version);
/* /*
* Start serializable transaction to dump consistent data * Start serializable transaction to dump consistent data
...@@ -916,17 +838,15 @@ main(int argc, char **argv) ...@@ -916,17 +838,15 @@ main(int argc, char **argv)
res = PQexec(g_conn, "begin"); res = PQexec(g_conn, "begin");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ exit_horribly(g_fout, "BEGIN command failed. Explanation from backend: '%s'.\n",
fprintf(stderr, "BEGIN command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn)); PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
PQclear(res); PQclear(res);
res = PQexec(g_conn, "set transaction isolation level serializable"); res = PQexec(g_conn, "set transaction isolation level serializable");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ exit_horribly(g_fout, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n",
fprintf(stderr, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn)); PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
PQclear(res); PQclear(res);
} }
...@@ -941,7 +861,12 @@ main(int argc, char **argv) ...@@ -941,7 +861,12 @@ main(int argc, char **argv)
tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly); tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
if (!schemaOnly) if (!schemaOnly)
dumpClasses(tblinfo, numTables, g_fout, tablename, oids); {
dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes);
}
if (outputBlobs)
ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0);
if (!dataOnly) /* dump indexes and triggers at the end if (!dataOnly) /* dump indexes and triggers at the end
* for performance */ * for performance */
...@@ -951,6 +876,15 @@ main(int argc, char **argv) ...@@ -951,6 +876,15 @@ main(int argc, char **argv)
dumpRules(g_fout, tablename, tblinfo, numTables); dumpRules(g_fout, tablename, tblinfo, numTables);
} }
/* Now sort the output nicely */
SortTocByOID(g_fout);
MoveToEnd(g_fout, "TABLE DATA");
MoveToEnd(g_fout, "BLOBS");
MoveToEnd(g_fout, "INDEX");
MoveToEnd(g_fout, "TRIGGER");
MoveToEnd(g_fout, "RULE");
MoveToEnd(g_fout, "ACL");
if (plainText) if (plainText)
{ {
ropt = NewRestoreOptions(); ropt = NewRestoreOptions();
...@@ -973,6 +907,92 @@ main(int argc, char **argv) ...@@ -973,6 +907,92 @@ main(int argc, char **argv)
exit(0); exit(0);
} }
/*
* dumpBlobs:
* dump all blobs
*
*/
#define loBufSize 16384
#define loFetchSize 1000
static int
dumpBlobs(Archive *AH, char* junkOid, void *junkVal)
{
PQExpBuffer oidQry = createPQExpBuffer();
PQExpBuffer oidFetchQry = createPQExpBuffer();
PGresult *res;
int i;
int loFd;
char buf[loBufSize];
int cnt;
int blobOid;
if (g_verbose)
fprintf(stderr, "%s saving BLOBs\n", g_comment_start);
/* Cursor to get all BLOB tables */
appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
res = PQexec(g_conn, oidQry->data);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "dumpBlobs(): Declare Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* Fetch for cursor */
appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
do {
/* Do a fetch */
PQclear(res);
res = PQexec(g_conn, oidFetchQry->data);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "dumpBlobs(): Fetch Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* Process the tuples, if any */
for (i = 0; i < PQntuples(res); i++)
{
blobOid = atoi(PQgetvalue(res, i, 0));
/* Open the BLOB */
loFd = lo_open(g_conn, blobOid, INV_READ);
if (loFd == -1)
{
fprintf(stderr, "dumpBlobs(): Could not open large object. "
"Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
StartBlob(AH, blobOid);
/* Now read it in chunks, sending data to archive */
do {
cnt = lo_read(g_conn, loFd, buf, loBufSize);
if (cnt < 0) {
fprintf(stderr, "dumpBlobs(): Error reading large object. "
" Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
WriteData(AH, buf, cnt);
} while (cnt > 0);
lo_close(g_conn, loFd);
EndBlob(AH, blobOid);
}
} while (PQntuples(res) > 0);
return 1;
}
/* /*
* getTypes: * getTypes:
* read all base types in the system catalogs and return them in the * read all base types in the system catalogs and return them in the
...@@ -2409,7 +2429,7 @@ dumpComment(Archive *fout, const char *target, const char *oid) ...@@ -2409,7 +2429,7 @@ dumpComment(Archive *fout, const char *target, const char *oid)
target, checkForQuote(PQgetvalue(res, 0, i_description))); target, checkForQuote(PQgetvalue(res, 0, i_description)));
ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/, ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/,
"" /*Owner*/, NULL, NULL); "" /* Copy */, "" /*Owner*/, NULL, NULL);
} }
...@@ -2542,7 +2562,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, ...@@ -2542,7 +2562,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
appendPQExpBuffer(q, ");\n"); appendPQExpBuffer(q, ");\n");
ArchiveEntry(fout, tinfo[i].oid, fmtId(tinfo[i].typname, force_quotes), "TYPE", NULL, ArchiveEntry(fout, tinfo[i].oid, fmtId(tinfo[i].typname, force_quotes), "TYPE", NULL,
q->data, delq->data, tinfo[i].usename, NULL, NULL); q->data, delq->data, "", tinfo[i].usename, NULL, NULL);
/*** Dump Type Comments ***/ /*** Dump Type Comments ***/
...@@ -2629,7 +2649,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, ...@@ -2629,7 +2649,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
lancompiler); lancompiler);
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE", ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
NULL, defqry->data, delqry->data, "", NULL, NULL); NULL, defqry->data, delqry->data, "", "", NULL, NULL);
free(lanname); free(lanname);
free(lancompiler); free(lancompiler);
...@@ -2669,8 +2689,8 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, ...@@ -2669,8 +2689,8 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
PQExpBuffer fn = createPQExpBuffer(); PQExpBuffer fn = createPQExpBuffer();
PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer();
PQExpBuffer fnlist = createPQExpBuffer(); PQExpBuffer fnlist = createPQExpBuffer();
PQExpBuffer asPart = createPQExpBuffer();
int j; int j;
PQExpBuffer asPart = createPQExpBuffer();
char func_lang[NAMEDATALEN + 1]; char func_lang[NAMEDATALEN + 1];
PGresult *res; PGresult *res;
int nlangs; int nlangs;
...@@ -2751,7 +2771,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, ...@@ -2751,7 +2771,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
asPart->data, func_lang); asPart->data, func_lang);
ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data, ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data,
finfo[i].usename, NULL, NULL); "", finfo[i].usename, NULL, NULL);
/*** Dump Function Comments ***/ /*** Dump Function Comments ***/
...@@ -2870,7 +2890,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators, ...@@ -2870,7 +2890,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
sort2->data); sort2->data);
ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL, ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL,
q->data, delq->data, oprinfo[i].usename, NULL, NULL); q->data, delq->data, "", oprinfo[i].usename, NULL, NULL);
} }
} }
...@@ -2927,7 +2947,7 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs, ...@@ -2927,7 +2947,7 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
details->data); details->data);
ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL, ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
q->data, delq->data, agginfo[i].usename, NULL, NULL); q->data, delq->data, "", agginfo[i].usename, NULL, NULL);
/*** Dump Aggregate Comments ***/ /*** Dump Aggregate Comments ***/
...@@ -3096,7 +3116,7 @@ dumpACL(Archive *fout, TableInfo tbinfo) ...@@ -3096,7 +3116,7 @@ dumpACL(Archive *fout, TableInfo tbinfo)
free(aclbuf); free(aclbuf);
ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", NULL, NULL); ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", "", NULL, NULL);
} }
...@@ -3274,7 +3294,7 @@ dumpTables(Archive *fout, TableInfo *tblinfo, int numTables, ...@@ -3274,7 +3294,7 @@ dumpTables(Archive *fout, TableInfo *tblinfo, int numTables,
if (!dataOnly) { if (!dataOnly) {
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false), ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
"TABLE", NULL, q->data, delq->data, tblinfo[i].usename, "TABLE", NULL, q->data, delq->data, "", tblinfo[i].usename,
NULL, NULL); NULL, NULL);
} }
...@@ -3468,7 +3488,7 @@ dumpIndices(Archive *fout, IndInfo *indinfo, int numIndices, ...@@ -3468,7 +3488,7 @@ dumpIndices(Archive *fout, IndInfo *indinfo, int numIndices,
/* Dump Index Comments */ /* Dump Index Comments */
ArchiveEntry(fout, tblinfo[tableInd].oid, id1->data, "INDEX", NULL, q->data, delq->data, ArchiveEntry(fout, tblinfo[tableInd].oid, id1->data, "INDEX", NULL, q->data, delq->data,
tblinfo[tableInd].usename, NULL, NULL); "", tblinfo[tableInd].usename, NULL, NULL);
resetPQExpBuffer(q); resetPQExpBuffer(q);
appendPQExpBuffer(q, "INDEX %s", id1->data); appendPQExpBuffer(q, "INDEX %s", id1->data);
...@@ -3599,7 +3619,7 @@ setMaxOid(Archive *fout) ...@@ -3599,7 +3619,7 @@ setMaxOid(Archive *fout)
pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n"); pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n");
pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n"); pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n");
ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "","", NULL, NULL); ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "", "", "", NULL, NULL);
} }
/* /*
...@@ -3750,7 +3770,7 @@ dumpSequence(Archive *fout, TableInfo tbinfo) ...@@ -3750,7 +3770,7 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
} }
ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE", NULL, ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE", NULL,
query->data, delqry->data, tbinfo.usename, NULL, NULL); query->data, delqry->data, "", tbinfo.usename, NULL, NULL);
/* Dump Sequence Comments */ /* Dump Sequence Comments */
...@@ -3779,7 +3799,7 @@ dumpTriggers(Archive *fout, const char *tablename, ...@@ -3779,7 +3799,7 @@ dumpTriggers(Archive *fout, const char *tablename,
for (j = 0; j < tblinfo[i].ntrig; j++) for (j = 0; j < tblinfo[i].ntrig; j++)
{ {
ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname, ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname,
"TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "",
tblinfo[i].usename, NULL, NULL); tblinfo[i].usename, NULL, NULL);
dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid); dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid);
} }
...@@ -3846,7 +3866,7 @@ dumpRules(Archive *fout, const char *tablename, ...@@ -3846,7 +3866,7 @@ dumpRules(Archive *fout, const char *tablename,
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename), ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
"RULE", NULL, PQgetvalue(res, i, i_definition), "RULE", NULL, PQgetvalue(res, i, i_definition),
"", "", NULL, NULL); "", "", "", NULL, NULL);
/* Dump rule comments */ /* Dump rule comments */
......
...@@ -84,19 +84,24 @@ typedef struct option optType; ...@@ -84,19 +84,24 @@ typedef struct option optType;
struct option cmdopts[] = { struct option cmdopts[] = {
{ "clean", 0, NULL, 'c' }, { "clean", 0, NULL, 'c' },
{ "data-only", 0, NULL, 'a' }, { "data-only", 0, NULL, 'a' },
{ "dbname", 1, NULL, 'd' },
{ "file", 1, NULL, 'f' }, { "file", 1, NULL, 'f' },
{ "format", 1, NULL, 'F' }, { "format", 1, NULL, 'F' },
{ "function", 2, NULL, 'p' }, { "function", 2, NULL, 'P' },
{ "index", 2, NULL, 'i'}, { "host", 1, NULL, 'h' },
{ "ignore-version", 0, NULL, 'i'},
{ "index", 2, NULL, 'I'},
{ "list", 0, NULL, 'l'}, { "list", 0, NULL, 'l'},
{ "no-acl", 0, NULL, 'x' }, { "no-acl", 0, NULL, 'x' },
{ "port", 1, NULL, 'p' },
{ "oid-order", 0, NULL, 'o'}, { "oid-order", 0, NULL, 'o'},
{ "orig-order", 0, NULL, 'O' }, { "orig-order", 0, NULL, 'O' },
{ "password", 0, NULL, 'u' },
{ "rearrange", 0, NULL, 'r'}, { "rearrange", 0, NULL, 'r'},
{ "schema-only", 0, NULL, 's' }, { "schema-only", 0, NULL, 's' },
{ "table", 2, NULL, 't'}, { "table", 2, NULL, 't'},
{ "trigger", 2, NULL, 'T' }, { "trigger", 2, NULL, 'T' },
{ "use-list", 1, NULL, 'u'}, { "use-list", 1, NULL, 'U'},
{ "verbose", 0, NULL, 'v' }, { "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0} { NULL, 0, NULL, 0}
}; };
...@@ -115,9 +120,9 @@ int main(int argc, char **argv) ...@@ -115,9 +120,9 @@ int main(int argc, char **argv)
progname = *argv; progname = *argv;
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "acf:F:i:loOp:st:T:u:vx", cmdopts, NULL)) != EOF) while ((c = getopt_long(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx", cmdopts, NULL)) != EOF)
#else #else
while ((c = getopt(argc, argv, "acf:F:i:loOp:st:T:u:vx")) != -1) while ((c = getopt(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx")) != -1)
#endif #endif
{ {
switch (c) switch (c)
...@@ -129,6 +134,13 @@ int main(int argc, char **argv) ...@@ -129,6 +134,13 @@ int main(int argc, char **argv)
* create */ * create */
opts->dropSchema = 1; opts->dropSchema = 1;
break; break;
case 'd':
if (strlen(optarg) != 0)
{
opts->dbname = strdup(optarg);
opts->useDB = 1;
}
break;
case 'f': /* output file name */ case 'f': /* output file name */
opts->filename = strdup(optarg); opts->filename = strdup(optarg);
break; break;
...@@ -136,22 +148,32 @@ int main(int argc, char **argv) ...@@ -136,22 +148,32 @@ int main(int argc, char **argv)
if (strlen(optarg) != 0) if (strlen(optarg) != 0)
opts->formatName = strdup(optarg); opts->formatName = strdup(optarg);
break; break;
case 'h':
if (strlen(optarg) != 0)
opts->pghost = strdup(optarg);
break;
case 'i':
opts->ignoreVersion = 1;
break;
case 'o': case 'o':
opts->oidOrder = 1; opts->oidOrder = 1;
break; break;
case 'O': case 'O':
opts->origOrder = 1; opts->origOrder = 1;
break; break;
case 'p':
if (strlen(optarg) != 0)
opts->pgport = strdup(optarg);
break;
case 'r': case 'r':
opts->rearrange = 1; opts->rearrange = 1;
break; break;
case 'P': /* Function */
case 'p': /* Function */
opts->selTypes = 1; opts->selTypes = 1;
opts->selFunction = 1; opts->selFunction = 1;
opts->functionNames = _cleanupName(optarg); opts->functionNames = _cleanupName(optarg);
break; break;
case 'i': /* Index */ case 'I': /* Index */
opts->selTypes = 1; opts->selTypes = 1;
opts->selIndex = 1; opts->selIndex = 1;
opts->indexNames = _cleanupName(optarg); opts->indexNames = _cleanupName(optarg);
...@@ -173,7 +195,11 @@ int main(int argc, char **argv) ...@@ -173,7 +195,11 @@ int main(int argc, char **argv)
opts->tocSummary = 1; opts->tocSummary = 1;
break; break;
case 'u': /* input TOC summary file name */ case 'u':
opts->requirePassword = 1;
break;
case 'U': /* input TOC summary file name */
opts->tocFile = strdup(optarg); opts->tocFile = strdup(optarg);
break; break;
...@@ -209,14 +235,23 @@ int main(int argc, char **argv) ...@@ -209,14 +235,23 @@ int main(int argc, char **argv)
opts->format = archFiles; opts->format = archFiles;
break; break;
case 't':
case 'T':
opts->format = archTar;
break;
default: default:
fprintf(stderr, "%s: Unknown archive format '%s', please specify 'f' or 'c'\n", progname, opts->formatName); fprintf(stderr, "%s: Unknown archive format '%s', please specify 't' or 'c'\n",
progname, opts->formatName);
exit (1); exit (1);
} }
} }
AH = OpenArchive(fileSpec, opts->format); AH = OpenArchive(fileSpec, opts->format);
/* Let the archiver know how noisy to be */
AH->verbose = opts->verbose;
if (opts->tocFile) if (opts->tocFile)
SortTocFromFile(AH, opts); SortTocFromFile(AH, opts);
...@@ -226,7 +261,9 @@ int main(int argc, char **argv) ...@@ -226,7 +261,9 @@ int main(int argc, char **argv)
SortTocByID(AH); SortTocByID(AH);
if (opts->rearrange) { if (opts->rearrange) {
MoveToStart(AH, "<Init>");
MoveToEnd(AH, "TABLE DATA"); MoveToEnd(AH, "TABLE DATA");
MoveToEnd(AH, "BLOBS");
MoveToEnd(AH, "INDEX"); MoveToEnd(AH, "INDEX");
MoveToEnd(AH, "TRIGGER"); MoveToEnd(AH, "TRIGGER");
MoveToEnd(AH, "RULE"); MoveToEnd(AH, "RULE");
...@@ -250,39 +287,48 @@ static void usage(const char *progname) ...@@ -250,39 +287,48 @@ static void usage(const char *progname)
fprintf(stderr, fprintf(stderr,
"usage: %s [options] [backup file]\n" "usage: %s [options] [backup file]\n"
" -a, --data-only \t dump out only the data, no schema\n" " -a, --data-only \t dump out only the data, no schema\n"
" -d, --dbname <name> \t specify database name\n"
" -c, --clean \t clean(drop) schema prior to create\n" " -c, --clean \t clean(drop) schema prior to create\n"
" -f filename \t script output filename\n" " -f filename \t script output filename\n"
" -F, --format {c|f} \t specify backup file format\n" " -F, --format {c|f} \t specify backup file format\n"
" -p, --function[=name] \t dump functions or named function\n" " -h, --host <hostname> \t server host name\n"
" -i, --index[=name] \t dump indexes or named index\n" " -i, --index[=name] \t dump indexes or named index\n"
" -l, --list \t dump summarized TOC for this file\n" " -l, --list \t dump summarized TOC for this file\n"
" -o, --oid-order \t dump in oid order\n" " -o, --oid-order \t dump in oid order\n"
" -O, --orig-order \t dump in original dump order\n" " -O, --orig-order \t dump in original dump order\n"
" -p, --port <port> \t server port number\n"
" -P, --function[=name] \t dump functions or named function\n"
" -r, --rearrange \t rearrange output to put indexes etc at end\n" " -r, --rearrange \t rearrange output to put indexes etc at end\n"
" -s, --schema-only \t dump out only the schema, no data\n" " -s, --schema-only \t dump out only the schema, no data\n"
" -t [table], --table[=table] \t dump for this table only\n" " -t [table], --table[=table] \t dump for this table only\n"
" -T, --trigger[=name] \t dump triggers or named trigger\n" " -T, --trigger[=name] \t dump triggers or named trigger\n"
" -u, --use-list filename \t use specified TOC for ordering output from this file\n" " -u, --password \t use password authentication\n"
" -v \t verbose\n" " -U, --use-list filename \t use specified TOC for ordering output from this file\n"
" -v, --verbose \t verbose\n"
" -x, --no-acl \t skip dumping of ACLs (grant/revoke)\n" " -x, --no-acl \t skip dumping of ACLs (grant/revoke)\n"
, progname); , progname);
#else #else
fprintf(stderr, fprintf(stderr,
"usage: %s [options] [backup file]\n" "usage: %s [options] [backup file]\n"
" -a \t dump out only the data, no schema\n" " -a \t dump out only the data, no schema\n"
" -d, <name> \t specify database name\n"
" -c \t clean(drop) schema prior to create\n" " -c \t clean(drop) schema prior to create\n"
" -f filename NOT IMPLEMENTED \t script output filename\n" " -f filename NOT IMPLEMENTED \t script output filename\n"
" -F {c|f} \t specify backup file format\n" " -F {c|f} \t specify backup file format\n"
" -p name \t dump functions or named function\n" " -h, <hostname> \t server host name\n"
" -i name \t dump indexes or named index\n" " -i name \t dump indexes or named index\n"
" -l \t dump summarized TOC for this file\n" " -l \t dump summarized TOC for this file\n"
" -o \t dump in oid order\n" " -o \t dump in oid order\n"
" -O \t dump in original dump order\n" " -O \t dump in original dump order\n"
" -p <port> \t server port number\n"
" -P name \t dump functions or named function\n"
" -r \t rearrange output to put indexes etc at end\n" " -r \t rearrange output to put indexes etc at end\n"
" -s \t dump out only the schema, no data\n" " -s \t dump out only the schema, no data\n"
" -t name \t dump for this table only\n" " -t name \t dump for this table only\n"
" -T name \t dump triggers or named trigger\n" " -T name \t dump triggers or named trigger\n"
" -u filename \t use specified TOC for ordering output from this file\n" " -u \t use password authentication\n"
" -U filename \t use specified TOC for ordering output from this file\n"
" -v \t verbose\n" " -v \t verbose\n"
" -x \t skip dumping of ACLs (grant/revoke)\n" " -x \t skip dumping of ACLs (grant/revoke)\n"
, progname); , progname);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment