- Support for BLOB output from pg_dump and input via pg_restore
authorPhilip Warner
Fri, 21 Jul 2000 11:40:08 +0000 (11:40 +0000)
committerPhilip Warner
Fri, 21 Jul 2000 11:40:08 +0000 (11:40 +0000)
- 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

src/bin/pg_dump/Makefile
src/bin/pg_dump/README
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_backup_archiver.h
src/bin/pg_dump/pg_backup_custom.c
src/bin/pg_dump/pg_backup_files.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_restore.c

index 4f03eb3f255580c228839cfc85a1e168b70e7d05..6b104028615e806a7ea613e8b3aec0bed5131c6b 100644 (file)
@@ -4,7 +4,7 @@
 #
 # 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
 top_builddir = ../../..
 include ../../Makefile.global
 
-OBJS= pg_backup_archiver.o pg_backup_custom.o pg_backup_files.o \
-       pg_backup_plain_text.o $(STRDUP)
+OBJS= pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o pg_backup_files.o \
+       pg_backup_null.o pg_backup_tar.o $(STRDUP)
 
 CFLAGS+= -I$(LIBPQDIR)
 LIBS+= -lz
index 0dfa0aabe8227b30a390f059da29db72cebf9826..a386ac5a017c3b9a981848cc881e6f1eb846547d 100644 (file)
@@ -1,17 +1,23 @@
 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:
 
     pg_dump  -Fc > 
 
+or, in TAR format
+
+   pg_dump  -Ft > 
+
 To restore, try
  
    To list contents:
@@ -53,7 +59,37 @@ or, simply:
     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
 
 
index e84b4dd16cc4fbc24695280b7ccff024ce9da459..fb930637ccc304ce20b7819dd9feeb62ad677fa9 100644 (file)
-/*-------------------------------------------------------------------------\r
- *\r
- * pg_backup.h\r
- *\r
- * Public interface to the pg_dump archiver routines.\r
- *\r
- * See the headers to pg_restore for more details.\r
- *\r
- * Copyright (c) 2000, Philip Warner\r
- *      Rights are granted to use this software in any way so long\r
- *      as this notice is not removed.\r
- *\r
- * The author is not responsible for loss or damages that may\r
- * result from it's use.\r
- *\r
- *\r
- * IDENTIFICATION\r
- *\r
- * Modifications - 28-Jun-2000 - [email protected]\r
- *\r
- * Initial version. \r
- *\r
- *-------------------------------------------------------------------------\r
- */\r
-\r
-#ifndef PG_BACKUP__\r
-\r
-#include "config.h"\r
-#include "c.h"\r
-\r
-#define PG_BACKUP__\r
-\r
-typedef enum _archiveFormat {\r
-    archUnknown = 0,\r
-    archCustom = 1,\r
-    archFiles = 2,\r
-    archTar = 3,\r
-    archPlainText = 4\r
-} ArchiveFormat;\r
-\r
-/*\r
- *  We may want to have so user-readbale data, but in the mean\r
- *  time this gives us some abstraction and type checking.\r
- */\r
-typedef struct _Archive {\r
-    /* Nothing here */\r
-} Archive;\r
-\r
-typedef int     (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);\r
-\r
-typedef struct _restoreOptions {\r
-   int         dataOnly;\r
-   int         dropSchema;\r
-   char        *filename;\r
-   int         schemaOnly;\r
-   int         verbose;\r
-   int         aclsSkip;\r
-   int         tocSummary;\r
-   char        *tocFile;\r
-   int         oidOrder;\r
-   int         origOrder;\r
-   int         rearrange;\r
-   int         format;\r
-   char        *formatName;\r
-\r
-   int         selTypes;\r
-   int     selIndex;\r
-   int     selFunction;\r
-   int     selTrigger;\r
-   int     selTable;\r
-   char        *indexNames;\r
-   char        *functionNames;\r
-   char        *tableNames;\r
-   char        *triggerNames;\r
-\r
-   int     *idWanted;\r
-   int     limitToList;\r
-   int     compression;\r
-\r
-} RestoreOptions;\r
-\r
-/*\r
- * Main archiver interface.\r
- */\r
-\r
-/* Called to add a TOC entry */\r
-extern void    ArchiveEntry(Archive* AH, const char* oid, const char* name,\r
-           const char* desc, const char* (deps[]), const char* defn,\r
-           const char* dropStmt, const char* owner, \r
-           DataDumperPtr dumpFn, void* dumpArg);\r
-\r
-/* Called to write *data* to the archive */\r
-extern int WriteData(Archive* AH, const void* data, int dLen);\r
-\r
-extern void    CloseArchive(Archive* AH);\r
-\r
-extern void    RestoreArchive(Archive* AH, RestoreOptions *ropt);\r
-\r
-/* Open an existing archive */\r
-extern Archive* OpenArchive(const char* FileSpec, ArchiveFormat fmt);\r
-\r
-/* Create a new archive */\r
-extern Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression);\r
-\r
-/* The --list option */\r
-extern void    PrintTOCSummary(Archive* AH, RestoreOptions *ropt);\r
-\r
-extern RestoreOptions*     NewRestoreOptions(void);\r
-\r
-/* Rearrange TOC entries */\r
-extern void    MoveToStart(Archive* AH, char *oType);\r
-extern void    MoveToEnd(Archive* AH, char *oType); \r
-extern void    SortTocByOID(Archive* AH);\r
-extern void    SortTocByID(Archive* AH);\r
-extern void    SortTocFromFile(Archive* AH, RestoreOptions *ropt);\r
-\r
-/* Convenience functions used only when writing DATA */\r
-extern int archputs(const char *s, Archive* AH);\r
-extern int archputc(const char c, Archive* AH);\r
-extern int archprintf(Archive* AH, const char *fmt, ...);\r
-\r
-#endif\r
-\r
-\r
-\r
+/*-------------------------------------------------------------------------
+ *
+ * pg_backup.h
+ *
+ * Public interface to the pg_dump archiver routines.
+ *
+ * See the headers to pg_restore for more details.
+ *
+ * Copyright (c) 2000, Philip Warner
+ *      Rights are granted to use this software in any way so long
+ *      as this notice is not removed.
+ *
+ * The author is not responsible for loss or damages that may
+ * result from it's use.
+ *
+ *
+ * IDENTIFICATION
+ *
+ * Modifications - 28-Jun-2000 - [email protected]
+ *
+ * Initial version. 
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PG_BACKUP__
+
+#include "config.h"
+#include "c.h"
+
+#define PG_BACKUP__
+
+#include "postgres.h"
+#include "libpq-fe.h"
+
+typedef enum _archiveFormat {
+    archUnknown = 0,
+    archCustom = 1,
+    archFiles = 2,
+    archTar = 3,
+    archNull = 4
+} ArchiveFormat;
+
+/*
+ *  We may want to have so user-readbale data, but in the mean
+ *  time this gives us some abstraction and type checking.
+ */
+typedef struct _Archive {
+   int         verbose;
+    /* The rest is private */
+} Archive;
+
+typedef int     (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);
+
+typedef struct _restoreOptions {
+   int         dataOnly;
+   int         dropSchema;
+   char        *filename;
+   int         schemaOnly;
+   int         verbose;
+   int         aclsSkip;
+   int         tocSummary;
+   char        *tocFile;
+   int         oidOrder;
+   int         origOrder;
+   int         rearrange;
+   int         format;
+   char        *formatName;
+
+   int         selTypes;
+   int         selIndex;
+   int         selFunction;
+   int         selTrigger;
+   int         selTable;
+   char        *indexNames;
+   char        *functionNames;
+   char        *tableNames;
+   char        *triggerNames;
+
+   int         useDB;
+   char        *dbname;
+   char        *pgport;
+   char        *pghost;
+   int         ignoreVersion;
+   int         requirePassword;
+
+   int     *idWanted;
+   int     limitToList;
+   int     compression;
+
+} RestoreOptions;
+
+/*
+ * 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 */
+extern void    ArchiveEntry(Archive* AH, const char* oid, const char* name,
+           const char* desc, const char* (deps[]), const char* defn,
+           const char* dropStmt, const char* copyStmt, const char* owner, 
+           DataDumperPtr dumpFn, void* dumpArg);
+
+/* Called to write *data* to the archive */
+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    RestoreArchive(Archive* AH, RestoreOptions *ropt);
+
+/* Open an existing archive */
+extern Archive* OpenArchive(const char* FileSpec, const ArchiveFormat fmt);
+
+/* Create a new archive */
+extern Archive* CreateArchive(const char* FileSpec, const ArchiveFormat fmt, 
+                               const int compression);
+
+/* The --list option */
+extern void    PrintTOCSummary(Archive* AH, RestoreOptions *ropt);
+
+extern RestoreOptions*     NewRestoreOptions(void);
+
+/* Rearrange TOC entries */
+extern void    MoveToStart(Archive* AH, char *oType);
+extern void    MoveToEnd(Archive* AH, char *oType); 
+extern void    SortTocByOID(Archive* AH);
+extern void    SortTocByID(Archive* AH);
+extern void    SortTocFromFile(Archive* AH, RestoreOptions *ropt);
+
+/* Convenience functions used only when writing DATA */
+extern int archputs(const char *s, Archive* AH);
+extern int archputc(const char c, Archive* AH);
+extern int archprintf(Archive* AH, const char *fmt, ...);
+
+#endif
+
+
+
index e25cf5675634bee1796100eb9b48b8c7a62745ce..e210780196be7a7f27daccfe073e42617a5f203e 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "pg_backup.h"
 #include "pg_backup_archiver.h"
+#include "pg_backup_db.h"
+
 #include 
 #include  /* for dup */
 
 #include 
 #include 
 
+#include "pqexpbuffer.h"
+#include "libpq/libpq-fs.h"
+
 static void        _SortToc(ArchiveHandle* AH, TocSortCompareFn fn);
 static int     _tocSortCompareByOIDNum(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);
 static int         _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
 static int     _tocEntryRequired(TocEntry* te, RestoreOptions *ropt);
@@ -45,8 +50,14 @@ static TocEntry* _getTocEntry(ArchiveHandle* AH, int id);
 static void        _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te);
 static void        _moveBefore(ArchiveHandle* AH, TocEntry* pos, TocEntry* te);
 static int     _discoverArchiveFormat(ArchiveHandle* AH);
+
 static char    *progname = "Archiver";
 
+static void        _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap);
+
+static int         _canRestoreBlobs(ArchiveHandle *AH);
+
+
 /*
  *  Wrapper functions.
  * 
@@ -58,7 +69,9 @@ static char   *progname = "Archiver";
 
 /* Create a new archive */
 /* 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);
     return (Archive*)AH;
@@ -66,7 +79,7 @@ Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression)
 
 /* Open an existing archive */
 /* Public */
-Archive* OpenArchive(const char* FileSpec, ArchiveFormat fmt) 
+Archive* OpenArchive(const char* FileSpec, const ArchiveFormat fmt) 
 {
     ArchiveHandle*      AH = _allocAH(FileSpec, fmt, 0, archModeRead);
     return (Archive*)AH;
@@ -80,9 +93,9 @@ void  CloseArchive(Archive* AHX)
 
     /* Close the output */
     if (AH->gzOut)
-   GZCLOSE(AH->OF);
+       GZCLOSE(AH->OF);
     else if (AH->OF != stdout)
-   fclose(AH->OF);
+       fclose(AH->OF);
 }
 
 /* Public */
@@ -93,47 +106,155 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
     int            reqs;
     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)
-   sav = SetOutput(AH, ropt->filename, ropt->compression);
+       sav = SetOutput(AH, ropt->filename, ropt->compression);
 
     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) {
-   te = AH->toc->prev;
-   while (te != AH->toc) {
-       reqs = _tocEntryRequired(te, ropt);
-       if ( (reqs & 1) && te->dropStmt) {  /* We want the schema */
-       ahprintf(AH, "%s", te->dropStmt);
-       }
-       te = te->prev;
-   }
+       te = AH->toc->prev;
+       while (te != AH->toc) {
+           reqs = _tocEntryRequired(te, ropt);
+           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);
+           }
+           te = te->prev;
+       }
     }
 
+   /*
+    * Now process each TOC entry
+    */
     te = AH->toc->next;
     while (te != AH->toc) {
-   reqs = _tocEntryRequired(te, ropt);
 
-   if (reqs & 1) /* We want the schema */
-       _printTocEntry(AH, te, ropt);
+       /* Work out what, if anything, we want from this entry */
+       reqs = _tocEntryRequired(te, ropt);
+
+       if ( (reqs & 1) != 0) /* We want the schema */
+       {
+           ahlog(AH, 1, "Creating %s %s\n", te->desc, te->name);
+           _printTocEntry(AH, te, ropt);
+       }
 
-   if (AH->PrintTocDataPtr != NULL && (reqs & 2) != 0) {
+       /* 
+        * If we want data, and it has data, then restore that too 
+        */
+       if (AH->PrintTocDataPtr != NULL && (reqs & 2) != 0) {
 #ifndef HAVE_LIBZ
-       if (AH->compression != 0)
-       die_horribly("%s: Unable to restore data from a compressed archive\n", progname);
+           if (AH->compression != 0)
+               die_horribly(AH, "%s: Unable to restore data from a compressed archive\n", progname);
 #endif
-       _disableTriggers(AH, te, ropt);
-       (*AH->PrintTocDataPtr)(AH, te, ropt);
-       _enableTriggers(AH, te, ropt);
-   }
-   te = te->next;
+
+           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);
+
+
+               /* 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);
+
+               _enableTriggers(AH, te, ropt);
+           }
+       }
+       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)
-   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* opts;
@@ -145,6 +266,11 @@ RestoreOptions*        NewRestoreOptions(void)
    return opts;
 }
 
+static int _canRestoreBlobs(ArchiveHandle *AH)
+{
+   return (AH->ropt->useDB && AH->connection);
+}
+
 static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
 {
     ahprintf(AH, "-- Disable triggers\n");
@@ -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 */
@@ -176,6 +302,10 @@ int    WriteData(Archive* AHX, const void* data, int dLen)
 {
     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);
 }
 
@@ -187,7 +317,7 @@ int WriteData(Archive* AHX, const void* data, int dLen)
 /* Public */
 void   ArchiveEntry(Archive* AHX, const char* oid, const char* name,
            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)
 {
     ArchiveHandle* AH = (ArchiveHandle*)AHX;
@@ -196,9 +326,9 @@ void    ArchiveEntry(Archive* AHX, const char* oid, const char* name,
     AH->lastID++;
     AH->tocCount++;
 
-    newToc = (TocEntry*)malloc(sizeof(TocEntry));
+    newToc = (TocEntry*)calloc(1, sizeof(TocEntry));
     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->next = AH->toc;
@@ -212,6 +342,7 @@ void    ArchiveEntry(Archive* AHX, const char* oid, const char* name,
     newToc->desc = strdup(desc);
     newToc->defn = strdup(defn);
     newToc->dropStmt = strdup(dropStmt);
+   newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
     newToc->owner = strdup(owner);
     newToc->printed = 0;
     newToc->formatData = NULL;
@@ -233,10 +364,30 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
     ArchiveHandle* AH = (ArchiveHandle*) AHX;
     TocEntry       *te = AH->toc->next;
     OutputContext  sav;
+   char            *fmtName;
 
     if (ropt->filename)
    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");
 
     while (te != AH->toc) {
@@ -249,6 +400,79 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
    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
  ***********/
@@ -256,6 +480,7 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
 /*
  * Move TOC entries of the specified type to the START of the TOC.
  */
+
 /* Public */
 void MoveToStart(Archive* AHX, char *oType) 
 {
@@ -356,7 +581,7 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt)
     /* Setup the file */
     fh = fopen(ropt->tocFile, PG_BINARY_R);
     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)
     {
@@ -377,14 +602,14 @@ void SortTocFromFile(Archive* AHX, RestoreOptions *ropt)
    id = strtol(buf, &endptr, 10);
    if (endptr == buf)
    {
-       fprintf(stderr, "%s: warning - line ignored: %s\n", progname, buf);
+       fprintf(stderr, "%s: WARNING - line ignored: %s\n", progname, buf);
        continue;
    }
 
    /* Find TOC entry */
    te = _getTocEntry(AH, id);
    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;
 
@@ -428,7 +653,7 @@ int archprintf(Archive* AH, const char *fmt, ...)
    if ((p = malloc(bSize)) == NULL)
    {
        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);
     }
@@ -519,15 +744,15 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...)
     /* This is paranoid: deal with the possibility that vsnprintf is willing to ignore trailing null */
     /* or returns > 0 even if string does not fit. It may be the case that it returns cnt = bufsize */ 
     while (cnt < 0 || cnt >= (bSize - 1) ) {
-   if (p != NULL) free(p);
-   bSize *= 2;
-   p = (char*)malloc(bSize);
-   if (p == NULL)
-   {
-       va_end(ap);
-       die_horribly("%s: could not allocate buffer for ahprintf\n", progname);
-   }
-   cnt = vsnprintf(p, bSize, fmt, ap);
+       if (p != NULL) free(p);
+       bSize *= 2;
+       p = (char*)malloc(bSize);
+       if (p == NULL)
+       {
+           va_end(ap);
+           die_horribly(AH, "%s: could not allocate buffer for ahprintf\n", progname);
+       }
+       cnt = vsnprintf(p, bSize, fmt, ap);
     }
     va_end(ap);
     ahwrite(p, 1, cnt, AH);
@@ -535,28 +760,82 @@ int ahprintf(ArchiveHandle* AH, const char *fmt, ...)
     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)
 {
-    if (AH->gzOut)
-   return GZWRITE((void*)ptr, size, nmemb, AH->OF);
-    else
-   return fwrite((void*)ptr, size, nmemb, AH->OF);
+   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);
+    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
+           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);
+
+    if (AH)
+   if (AH->connection)
+       PQfinish(AH->connection);
+
+    exit(1);
 }
 
+/* External use */
+void exit_horribly(Archive *AH, const char *fmt, ...)
+{
+    va_list     ap;
+
+    va_start(ap, fmt);
+    _die_horribly((ArchiveHandle*)AH, fmt, ap);
+}
 
-void die_horribly(const char *fmt, ...)
+/* Archiver use (just different arg declaration) */
+void die_horribly(ArchiveHandle *AH, const char *fmt, ...)
 {
     va_list    ap;
 
     va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-    exit(1);
+    _die_horribly(AH, fmt, ap);
 }
 
+
 static void _moveAfter(ArchiveHandle* AH, TocEntry* pos, TocEntry* te)
 {
     te->prev->next = te->next;
@@ -622,9 +901,9 @@ int WriteInt(ArchiveHandle* AH, int i)
     /* SIGN byte */
     if (i < 0) {
    (*AH->WriteBytePtr)(AH, 1);
-   i = -i;
+       i = -i;
     } else {
-   (*AH->WriteBytePtr)(AH, 0);
+       (*AH->WriteBytePtr)(AH, 0);
     }
     
     for(b = 0 ; b < AH->intSize ; b++) {
@@ -638,30 +917,40 @@ int   WriteInt(ArchiveHandle* AH, int i)
 int        ReadInt(ArchiveHandle* AH)
 {
     int res = 0;
-    int shft = 1;
     int bv, b;
     int    sign = 0; /* Default positive */
+   int bitShift = 0;
 
     if (AH->version > K_VERS_1_0)
-   /* Read a sign byte */
-   sign = (*AH->ReadBytePtr)(AH);
+       /* Read a sign byte */
+       sign = (*AH->ReadBytePtr)(AH);
 
     for( b = 0 ; b < AH->intSize ; b++) {
-        bv = (*AH->ReadBytePtr)(AH);
-        res = res  + shft * bv;
-        shft *= 256;
+        bv = (*AH->ReadBytePtr)(AH) & 0xFF;
+       if (bv != 0)
+           res = res + (bv << bitShift);
+       bitShift += 8;
     }
 
     if (sign)
-   res = - res;
+       res = - res;
 
     return res;
 }
 
 int    WriteStr(ArchiveHandle* AH, char* c)
 {
-    int l = WriteInt(AH, strlen(c));
-    return (*AH->WriteBufPtr)(AH, c, strlen(c)) + l;
+    int res;
+
+   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)
@@ -670,12 +959,18 @@ char* ReadStr(ArchiveHandle* AH)
     int        l;
 
     l = ReadInt(AH);
-    buf = (char*)malloc(l+1);
-    if (!buf)
-   die_horribly("Archiver: Unable to allocate sufficient memory in ReadStr\n");
+   if (l == -1)
+       buf = NULL;
+   else
+   {
+       buf = (char*)malloc(l+1);
+       if (!buf)
+           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);
+       buf[l] = '\0';
+   }
 
-    (*AH->ReadBufPtr)(AH, (void*)buf, l);
-    buf[l] = '\0';
     return buf;
 }
 
@@ -686,138 +981,188 @@ int _discoverArchiveFormat(ArchiveHandle* AH)
     int        cnt;
     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) {
-   wantClose = 1;
-   fh = fopen(AH->fSpec, PG_BINARY_R);
+       wantClose = 1;
+       fh = fopen(AH->fSpec, PG_BINARY_R);
     } else {
-   fh = stdin;
+       fh = stdin;
     }
 
     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);
 
     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)
-   die_horribly("%s: input file does not appear to be a valid archive\n", progname);
+   /* Save it, just in case we need it later*/
+   strncpy(&AH->lookahead[0], sig, 5);
+   AH->lookaheadLen = 5;
 
-    AH->vmaj = fgetc(fh);
-    AH->vmin = fgetc(fh);
+    if (strncmp(sig, "PGDMP", 5) == 0)
+   {
+       AH->vmaj = 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 */
+       if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */
+       {   
+           AH->vrev = fgetc(fh);
+           AH->lookahead[AH->lookaheadLen++] = AH->vrev;
+       }
+       else
+           AH->vrev = 0;
+
+       AH->intSize = fgetc(fh);
+       AH->lookahead[AH->lookaheadLen++] = AH->intSize;
+
+       AH->format = fgetc(fh);
+       AH->lookahead[AH->lookaheadLen++] = AH->format;
+
+       /* Make a convenient integer 00 */
+       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;
 
-    /* Check header version; varies from V1.0 */
-    if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */ 
-   AH->vrev = fgetc(fh);
-    else
-   AH->vrev = 0;
+       if (AH->lookaheadLen != 512)
+           die_horribly(AH, "%s: input file does not appear to be a valid archive (too short?)\n",
+                           progname);
 
-    AH->intSize = fgetc(fh);
-    AH->format = fgetc(fh);
+       if (!isValidTarHeader(AH->lookahead))
+           die_horribly(AH, "%s: input file does not appear to be a valid archive\n", progname);
 
-    /* Make a convenient integer 00 */
-    AH->version = ( (AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev ) * 256 + 0;
+       AH->format = archTar;
+   }
 
     /* If we can't seek, then mark the header as read */
     if (fseek(fh, 0, SEEK_SET) != 0) 
-   AH->readHeader = 1;
+   {
+       /*
+        * NOTE: Formats that use the looahead buffer can unset this in their Init routine.
+        */
+       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 */
     if (wantClose)
-   fclose(fh);
+       fclose(fh);
 
     return AH->format;
-
 }
 
 
 /*
  * Allocate an archive handle
  */
-static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt, 
-               int compression, ArchiveMode mode) {
+static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt, 
+               const int compression, ArchiveMode mode) 
+{
     ArchiveHandle* AH;
 
+   /*
+    *fprintf(stderr, "%s: allocating AH for %s, format %d\n", progname, FileSpec, fmt); 
+    */
+
     AH = (ArchiveHandle*)calloc(1, sizeof(ArchiveHandle));
     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->vmin = K_VERS_MINOR;
 
+   AH->createDate = time(NULL);
+
     AH->intSize = sizeof(int);
     AH->lastID = 0;
     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 {
-   AH->fSpec = NULL;
-    }
-    AH->FH = NULL;
-    AH->formatData = NULL;
+       AH->fSpec = NULL;
+    } 
 
-    AH->currToc = NULL;
     AH->currUser = "";
 
     AH->toc = (TocEntry*)calloc(1, sizeof(TocEntry));
     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->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->format = fmt;
     AH->compression = compression;
 
-    AH->ArchiveEntryPtr = NULL;
-
-    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;
+   AH->pgCopyBuf = createPQExpBuffer();
+   AH->sqlBuf = createPQExpBuffer();
 
     /* Open stdout with no compression for AH output handle */
     AH->gzOut = 0;
     AH->OF = stdout;
 
+   /*
+    *fprintf(stderr, "%s: archive format is %d\n", progname, fmt);
+    */
+
     if (fmt == archUnknown)
-   fmt = _discoverArchiveFormat(AH);
+       AH->format = _discoverArchiveFormat(AH);
+   else
+       AH->format = fmt;
 
-    switch (fmt) {
+    switch (AH->format) {
 
-   case archCustom:
-       InitArchiveFmt_Custom(AH);
-       break;
+       case archCustom:
+           InitArchiveFmt_Custom(AH);
+           break;
 
-   case archFiles:
-       InitArchiveFmt_Files(AH);
-       break;
+       case archFiles:
+           InitArchiveFmt_Files(AH);
+           break;
 
-   case archPlainText:
-       InitArchiveFmt_PlainText(AH);
-       break;
+       case archNull:
+           InitArchiveFmt_Null(AH);
+           break;
 
-   default:
-       die_horribly("Archiver: Unrecognized file format '%d'\n", fmt);
+       case archTar:
+           InitArchiveFmt_Tar(AH);
+           break;
+
+       default:
+           die_horribly(AH, "Archiver: Unrecognized file format '%d'\n", fmt);
     }
 
     return AH;
@@ -827,13 +1172,25 @@ static ArchiveHandle* _allocAH(const char* FileSpec, ArchiveFormat fmt,
 void WriteDataChunks(ArchiveHandle* AH)
 {
     TocEntry       *te = AH->toc->next;
+   StartDataPtr    startPtr;
+   EndDataPtr      endPtr;
 
     while (te != AH->toc) {
    if (te->dataDumper != NULL) {
        AH->currToc = 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); */
@@ -842,12 +1199,12 @@ void WriteDataChunks(ArchiveHandle* AH)
         */
        (*te->dataDumper)((Archive*)AH, te->oid, te->dataDumperArg);
 
-       if (AH->EndDataPtr != NULL) {
-       (*AH->EndDataPtr)(AH, te);
+       if (endPtr != NULL) {
+           (*endPtr)(AH, te);
        }
        AH->currToc = NULL;
    }
-   te = te->next;
+       te = te->next;
     }
 }
 
@@ -866,6 +1223,7 @@ void WriteToc(ArchiveHandle* AH)
    WriteStr(AH, te->desc);
    WriteStr(AH, te->defn);
    WriteStr(AH, te->dropStmt);
+   WriteStr(AH, te->copyStmt);
    WriteStr(AH, te->owner);
    if (AH->WriteExtraTocPtr) {
        (*AH->WriteExtraTocPtr)(AH, te);
@@ -884,71 +1242,79 @@ void ReadToc(ArchiveHandle* AH)
 
     for( i = 0 ; i < AH->tocCount ; i++) {
 
-   te = (TocEntry*)malloc(sizeof(TocEntry));
-   te->id = ReadInt(AH);
-
-   /* Sanity check */
-   if (te->id <= 0 || te->id > AH->tocCount)
-       die_horribly("Archiver: failed sanity check (bad entry id) - perhaps a corrupt TOC\n");
-
-   te->hadDumper = ReadInt(AH);
-   te->oid = ReadStr(AH);
-   te->oidVal = atoi(te->oid);
-   te->name = ReadStr(AH);
-   te->desc = ReadStr(AH);
-   te->defn = ReadStr(AH);
-   te->dropStmt = ReadStr(AH);
-   te->owner = ReadStr(AH);
-   if (AH->ReadExtraTocPtr) {
-       (*AH->ReadExtraTocPtr)(AH, te);
-   }
-   te->prev = AH->toc->prev;
-   AH->toc->prev->next = te;
-   AH->toc->prev = te;
-   te->next = AH->toc;
+       te = (TocEntry*)calloc(1, sizeof(TocEntry));
+       te->id = ReadInt(AH);
+
+       /* Sanity check */
+       if (te->id <= 0 || te->id > AH->tocCount)
+           die_horribly(AH, "Archiver: failed sanity check (bad entry id) - perhaps a corrupt TOC\n");
+
+       te->hadDumper = ReadInt(AH);
+       te->oid = ReadStr(AH);
+       te->oidVal = atoi(te->oid);
+       te->name = ReadStr(AH);
+       te->desc = ReadStr(AH);
+       te->defn = ReadStr(AH);
+       te->dropStmt = ReadStr(AH);
+
+       if (AH->version >= K_VERS_1_3)
+           te->copyStmt = ReadStr(AH);
+
+       te->owner = ReadStr(AH);
+
+       if (AH->ReadExtraTocPtr) {
+           (*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;
+       AH->toc->prev->next = te;
+       AH->toc->prev = te;
+       te->next = AH->toc;
     }
 }
 
 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 (ropt->aclsSkip && strcmp(te->desc,"ACL") == 0)
-   return 0;
+       return 0;
 
     /* Check if tablename only is wanted */
     if (ropt->selTypes)
     {
-   if ( (strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0) )
-   {
-       if (!ropt->selTable)
-       return 0;
-       if (ropt->tableNames && strcmp(ropt->tableNames, te->name) != 0)
-       return 0;
-           } else if (strcmp(te->desc, "INDEX") == 0) {
-       if (!ropt->selIndex)
-       return 0;
-       if (ropt->indexNames && strcmp(ropt->indexNames, te->name) != 0)
-       return 0;
-   } else if (strcmp(te->desc, "FUNCTION") == 0) {
-       if (!ropt->selFunction)
-       return 0;
-       if (ropt->functionNames && strcmp(ropt->functionNames, te->name) != 0)
-       return 0;
-   } else if (strcmp(te->desc, "TRIGGER") == 0) {
-       if (!ropt->selTrigger)
-       return 0;
-       if (ropt->triggerNames && strcmp(ropt->triggerNames, te->name) != 0)
-       return 0;
-   } else {
-       return 0;
+       if ( (strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0) )
+       {
+           if (!ropt->selTable)
+               return 0;
+           if (ropt->tableNames && strcmp(ropt->tableNames, te->name) != 0)
+               return 0;
+       } else if (strcmp(te->desc, "INDEX") == 0) {
+           if (!ropt->selIndex)
+               return 0;
+           if (ropt->indexNames && strcmp(ropt->indexNames, te->name) != 0)
+               return 0;
+       } else if (strcmp(te->desc, "FUNCTION") == 0) {
+           if (!ropt->selFunction)
+               return 0;
+           if (ropt->functionNames && strcmp(ropt->functionNames, te->name) != 0)
+               return 0;
+       } else if (strcmp(te->desc, "TRIGGER") == 0) {
+           if (!ropt->selTrigger)
+               return 0;
+           if (ropt->triggerNames && strcmp(ropt->triggerNames, te->name) != 0)
+               return 0;
+       } else {
+           return 0;
+       }
    }
-    }
 
     /* Mask it if we only want schema */
     if (ropt->schemaOnly)
-   res = res & 1;
+       res = res & 1;
 
     /* Mask it we only want data */
     if (ropt->dataOnly) 
@@ -956,15 +1322,15 @@ static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt)
 
     /* Mask it if we don't have a schema contribition */
     if (!te->defn || strlen(te->defn) == 0) 
-   res = res & 2;
+       res = res & 2;
 
     /* Mask it if we don't have a possible data contribition */
     if (!te->hadDumper)
-   res = res & 1;
+       res = res & 1;
 
     /* Finally, if we used a list, limit based on that as well */
     if (ropt->limitToList && !ropt->idWanted[te->id - 1]) 
-   return 0;
+       return 0;
 
     return res;
 }
@@ -979,8 +1345,9 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
     ahprintf(AH, "--\n\n");
 
     if (te->owner && strlen(te->owner) != 0 && strcmp(AH->currUser, te->owner) != 0) {
-   ahprintf(AH, "\\connect - %s\n", te->owner);
-   AH->currUser = te->owner;
+       //todo pjw - fix for db connection...
+       //ahprintf(AH, "\\connect - %s\n", te->owner);
+       AH->currUser = te->owner;
     }
 
     ahprintf(AH, "%s\n", te->defn);
@@ -990,6 +1357,8 @@ static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
 
 void WriteHead(ArchiveHandle* AH) 
 {
+   struct tm       crtm;
+
     (*AH->WriteBufPtr)(AH, "PGDMP", 5);    /* Magic code */
     (*AH->WriteBytePtr)(AH, AH->vmaj);
     (*AH->WriteBytePtr)(AH, AH->vmin);
@@ -1003,70 +1372,102 @@ void WriteHead(ArchiveHandle* AH)
            "archive will be uncompressed \n", progname);
 
     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)
 {
-    char   tmpMag[7];
-    int        fmt;
+    char       tmpMag[7];
+    int            fmt;
+   struct tm   crtm;
 
+   /* If we haven't already read the header... */
     if (!AH->readHeader) {
 
-   (*AH->ReadBufPtr)(AH, tmpMag, 5);
+       (*AH->ReadBufPtr)(AH, tmpMag, 5);
 
-   if (strncmp(tmpMag,"PGDMP", 5) != 0)
-       die_horribly("Archiver: Did not fing magic PGDMP in file header\n");
+       if (strncmp(tmpMag,"PGDMP", 5) != 0)
+           die_horribly(AH, "Archiver: Did not fing magic PGDMP in file header\n");
 
-   AH->vmaj = (*AH->ReadBytePtr)(AH);
-   AH->vmin = (*AH->ReadBytePtr)(AH);
+       AH->vmaj = (*AH->ReadBytePtr)(AH);
+       AH->vmin = (*AH->ReadBytePtr)(AH);
 
-   if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */
-   {
-       AH->vrev = (*AH->ReadBytePtr)(AH);
-   } else {
-       AH->vrev = 0;
-   }
+       if (AH->vmaj > 1 || ( (AH->vmaj == 1) && (AH->vmin > 0) ) ) /* Version > 1.0 */
+       {
+           AH->vrev = (*AH->ReadBytePtr)(AH);
+       } else {
+           AH->vrev = 0;
+       }
 
-   AH->version = ( (AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev ) * 256 + 0;
+       AH->version = ( (AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev ) * 256 + 0;
 
 
-   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);
+       if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
+           die_horribly(AH, "%s: unsupported version (%d.%d) in file header\n", 
+                   progname, AH->vmaj, AH->vmin);
 
-   AH->intSize = (*AH->ReadBytePtr)(AH);
-   if (AH->intSize > 32)
-       die_horribly("Archiver: sanity check on integer size (%d) failes\n", AH->intSize);
+       AH->intSize = (*AH->ReadBytePtr)(AH);
+       if (AH->intSize > 32)
+           die_horribly(AH, "Archiver: sanity check on integer size (%d) failes\n", AH->intSize);
 
-   if (AH->intSize > sizeof(int))
-       fprintf(stderr, "\nWARNING: Backup file was made on a machine with larger integers, "
-               "some operations may fail\n");
+       if (AH->intSize > sizeof(int))
+           fprintf(stderr, "\n%s: WARNING - archive was made on a machine with larger integers, "
+                   "some operations may fail\n", progname);
 
-   fmt = (*AH->ReadBytePtr)(AH);
+       fmt = (*AH->ReadBytePtr)(AH);
 
-   if (AH->format != fmt)
-       die_horribly("Archiver: expected format (%d) differs from format found in file (%d)\n", 
-               AH->format, fmt);
+       if (AH->format != fmt)
+           die_horribly(AH, "%s: expected format (%d) differs from format found in file (%d)\n", 
+                   progname, AH->format, fmt);
     }
 
     if (AH->version >= K_VERS_1_2)
     {
-   AH->compression = (*AH->ReadBytePtr)(AH);
+       if (AH->version < K_VERS_1_4)
+           AH->compression = (*AH->ReadBytePtr)(AH);
+       else
+           AH->compression = ReadInt(AH);
     } else {
-   AH->compression = Z_DEFAULT_COMPRESSION;
+       AH->compression = Z_DEFAULT_COMPRESSION;
     }
 
 #ifndef HAVE_LIBZ
     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
 
+   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)
     }
 }
 
-
+/*
+ * 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;
+ */
 
index f34e1584091c7f60c503c4eb14bf1b69e37163f2..f3b0ba538589c7458d8ead57971d2b597c0e3598 100644 (file)
 #define __PG_BACKUP_ARCHIVE__
 
 #include 
+#include 
+
+#include "postgres.h"
+#include "pqexpbuffer.h"
 
 #ifdef HAVE_LIBZ
 #include 
@@ -51,15 +55,23 @@ typedef z_stream *z_streamp;
 #endif
 
 #include "pg_backup.h"
+#include "libpq-fe.h"
 
 #define K_VERS_MAJOR 1
-#define K_VERS_MINOR 2 
-#define K_VERS_REV 2
+#define K_VERS_MINOR 4 
+#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) */
 #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_MAX (( (1 * 256 + 2) * 256 + 255) * 256 + 0)
+#define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) /* Allow No ZLIB */
+#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 _tocEntry;
@@ -72,10 +84,15 @@ typedef void    (*StartDataPtr)     (struct _archiveHandle* AH, struct _tocEntry* te);
 typedef int    (*WriteDataPtr)     (struct _archiveHandle* AH, const void* data, int dLen);
 typedef void   (*EndDataPtr)       (struct _archiveHandle* AH, struct _tocEntry* te);
 
-typedef int    (*WriteBytePtr)     (struct _archiveHandle* AH, const int i);
+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        (*ReadBytePtr)      (struct _archiveHandle* AH);
-typedef int    (*WriteBufPtr)      (struct _archiveHandle* AH, const void* c, int len);
-typedef int    (*ReadBufPtr)       (struct _archiveHandle* AH, void* buf, int len);
+typedef int        (*WriteBufPtr)      (struct _archiveHandle* AH, const void* c, int len);
+typedef int        (*ReadBufPtr)       (struct _archiveHandle* AH, void* buf, int len);
 typedef void   (*SaveArchivePtr)   (struct _archiveHandle* AH);
 typedef void   (*WriteExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
 typedef void   (*ReadExtraTocPtr)  (struct _archiveHandle* AH, struct _tocEntry* te);
@@ -83,6 +100,8 @@ typedef void (*PrintExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* t
 typedef void   (*PrintTocDataPtr)  (struct _archiveHandle* AH, struct _tocEntry* te, 
                        RestoreOptions *ropt);
 
+typedef int        (*CustomOutPtr)     (struct _archiveHandle* AH, const void* buf, int len);
+
 typedef int    (*TocSortCompareFn) (const void* te1, const void *te2); 
 
 typedef enum _archiveMode {
@@ -95,16 +114,44 @@ typedef struct _outputContext {
    int     gzOut;
 } 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 {
+   Archive             public;             /* Public part of archive */
    char                vmaj;               /* Version of file */
    char                vmin;
    char                vrev;
    int                 version;            /* Conveniently formatted version */
 
+   int                 debugLevel;         /* Not used. Intended for logging */
    int                 intSize;            /* Size of an integer in the archive */
    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 */
+   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 */
    StartDataPtr        StartDataPtr;       /* Called when table data is about to be dumped */
@@ -121,11 +168,33 @@ typedef struct _archiveHandle {
    PrintExtraTocPtr    PrintExtraTocPtr;   /* Extra TOC info for format */
    PrintTocDataPtr     PrintTocDataPtr;
 
-   int         lastID;                     /* Last internal ID for a TOC entry */
-   char*       fSpec;                      /* Archive File Spec */
-   FILE        *FH;                        /* General purpose file handle */
-   void        *OF;
-   int     gzOut;                      /* Output file */
+   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 */
+   char*               fSpec;              /* Archive File Spec */
+   FILE                *FH;                /* General purpose file handle */
+   void                *OF;
+   int                 gzOut;              /* Output file */
 
    struct _tocEntry*       toc;            /* List of TOC entries */
    int                     tocCount;       /* Number of TOC entries */
@@ -135,6 +204,7 @@ typedef struct _archiveHandle {
    ArchiveMode             mode;           /* File mode - r or w */
    void*                   formatData;     /* Header data specific to file format */
 
+   RestoreOptions          *ropt;          /* Used to check restore options in ahwrite etc */
 } ArchiveHandle;
 
 typedef struct _tocEntry {
@@ -148,6 +218,7 @@ typedef struct _tocEntry {
    char*               desc;
    char*               defn;
    char*               dropStmt;
+   char*               copyStmt;
    char*               owner;
    char**              depOid;
    int                 printed;        /* Indicates if entry defn has been dumped */
@@ -159,7 +230,8 @@ typedef struct _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 ReadTOC(ArchiveHandle* AH);
@@ -175,19 +247,27 @@ extern int TocIDRequired(ArchiveHandle* AH, int id, RestoreOptions *ropt);
  * Mandatory routines for each supported format
  */
 
-extern int WriteInt(ArchiveHandle* AH, int i);
-extern int ReadInt(ArchiveHandle* AH);
-extern char* ReadStr(ArchiveHandle* AH);
-extern int WriteStr(ArchiveHandle* AH, char* s);
+extern int                 WriteInt(ArchiveHandle* AH, int i);
+extern int                 ReadInt(ArchiveHandle* AH);
+extern char*           ReadStr(ArchiveHandle* AH);
+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_Files(ArchiveHandle* AH);
-extern void InitArchiveFmt_PlainText(ArchiveHandle* AH);
+extern void            InitArchiveFmt_Custom(ArchiveHandle* AH);
+extern void            InitArchiveFmt_Files(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 void        ResetOutput(ArchiveHandle* AH, OutputContext savedContext);
+extern void            ResetOutput(ArchiveHandle* AH, OutputContext savedContext);
 
 int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH);
 int ahprintf(ArchiveHandle* AH, const char *fmt, ...);
 
+void ahlog(ArchiveHandle* AH, int level, const char *fmt, ...);
+
 #endif
index 3edbb751f9a0a80ada721fdc9e9012610083fd64..f5b208e233ebc933b11e00a73390c51a1ce858e7 100644 (file)
-/*-------------------------------------------------------------------------\r
- *\r
- * pg_backup_custom.c\r
- *\r
- * Implements the custom output format.\r
- *\r
- * See the headers to pg_restore for more details.\r
- *\r
- * Copyright (c) 2000, Philip Warner\r
- *      Rights are granted to use this software in any way so long\r
- *      as this notice is not removed.\r
- *\r
- * The author is not responsible for loss or damages that may\r
- * result from it's use.\r
- *\r
- *\r
- * IDENTIFICATION\r
- *\r
- * Modifications - 28-Jun-2000 - [email protected]\r
- *\r
- * Initial version. \r
- *\r
- *-------------------------------------------------------------------------\r
- */\r
-\r
-#include \r
-#include "pg_backup.h"\r
-#include "pg_backup_archiver.h"\r
-\r
-extern int errno;\r
-\r
-static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te);\r
-static int _WriteData(ArchiveHandle* AH, const void* data, int dLen);\r
-static void     _EndData(ArchiveHandle* AH, TocEntry* te);\r
-static int      _WriteByte(ArchiveHandle* AH, const int i);\r
-static int      _ReadByte(ArchiveHandle* );\r
-static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);\r
-static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len);\r
-static void     _CloseArchive(ArchiveHandle* AH);\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-\r
-static void    _PrintData(ArchiveHandle* AH);\r
-static void     _skipData(ArchiveHandle* AH);\r
-\r
-#define zlibOutSize    4096\r
-#define zlibInSize 4096\r
-\r
-typedef struct {\r
-    z_streamp  zp;\r
-    char*  zlibOut;\r
-    char*  zlibIn;\r
-    int        inSize;\r
-    int        hasSeek;\r
-    int        filePos;\r
-    int        dataStart;\r
-} lclContext;\r
-\r
-typedef struct {\r
-    int        dataPos;\r
-    int        dataLen;\r
-} lclTocEntry;\r
-\r
-static int _getFilePos(ArchiveHandle* AH, lclContext* ctx);\r
-\r
-static char* progname = "Archiver(custom)";\r
-\r
-/*\r
- *  Handler functions. \r
- */\r
-void InitArchiveFmt_Custom(ArchiveHandle* AH) \r
-{\r
-    lclContext*        ctx;\r
-\r
-    /* Assuming static functions, this can be copied for each format. */\r
-    AH->ArchiveEntryPtr = _ArchiveEntry;\r
-    AH->StartDataPtr = _StartData;\r
-    AH->WriteDataPtr = _WriteData;\r
-    AH->EndDataPtr = _EndData;\r
-    AH->WriteBytePtr = _WriteByte;\r
-    AH->ReadBytePtr = _ReadByte;\r
-    AH->WriteBufPtr = _WriteBuf;\r
-    AH->ReadBufPtr = _ReadBuf;\r
-    AH->ClosePtr = _CloseArchive;\r
-    AH->PrintTocDataPtr = _PrintTocData;\r
-    AH->ReadExtraTocPtr = _ReadExtraToc;\r
-    AH->WriteExtraTocPtr = _WriteExtraToc;\r
-    AH->PrintExtraTocPtr = _PrintExtraToc;\r
-\r
-    /*\r
-     * Set up some special context used in compressing data.\r
-    */\r
-    ctx = (lclContext*)malloc(sizeof(lclContext));\r
-    if (ctx == NULL)\r
-   die_horribly("%s: Unable to allocate archive context",progname);\r
-    AH->formatData = (void*)ctx;\r
-\r
-    ctx->zp = (z_streamp)malloc(sizeof(z_stream));\r
-    if (ctx->zp == NULL)\r
-   die_horribly("%s: unable to allocate zlib stream archive context",progname);\r
-\r
-    ctx->zlibOut = (char*)malloc(zlibOutSize);\r
-    ctx->zlibIn = (char*)malloc(zlibInSize);\r
-    ctx->inSize = zlibInSize;\r
-    ctx->filePos = 0;\r
-\r
-    if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)\r
-   die_horribly("%s: unable to allocate buffers in archive context",progname);\r
-\r
-    /*\r
-     * Now open the file\r
-    */\r
-    if (AH->mode == archModeWrite) {\r
-   if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-       AH->FH = fopen(AH->fSpec, PG_BINARY_W);\r
-   } else {\r
-       AH->FH = stdout;\r
-   }\r
-\r
-   if (!AH)\r
-       die_horribly("%s: unable to open archive file %s",progname, AH->fSpec);\r
-\r
-   ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-    } else {\r
-   if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-       AH->FH = fopen(AH->fSpec, PG_BINARY_R);\r
-   } else {\r
-       AH->FH = stdin;\r
-   }\r
-   if (!AH)\r
-       die_horribly("%s: unable to open archive file %s",progname, AH->fSpec);\r
-\r
-   ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-   ReadHead(AH);\r
-   ReadToc(AH);\r
-   ctx->dataStart = _getFilePos(AH, ctx);\r
-    }\r
-\r
-}\r
-\r
-/*\r
- * - Start a new TOC entry\r
-*/\r
-static void    _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) \r
-{\r
-    lclTocEntry*   ctx;\r
-\r
-    ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-    if (te->dataDumper) {\r
-   ctx->dataPos = -1;\r
-    } else {\r
-   ctx->dataPos = 0;\r
-    }\r
-    ctx->dataLen = 0;\r
-    te->formatData = (void*)ctx;\r
-\r
-}\r
-\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    WriteInt(AH, ctx->dataPos);\r
-    WriteInt(AH, ctx->dataLen);\r
-}\r
-\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    if (ctx == NULL) {\r
-   ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-   te->formatData = (void*)ctx;\r
-    }\r
-\r
-    ctx->dataPos = ReadInt( AH );\r
-    ctx->dataLen = ReadInt( AH );\r
-    \r
-}\r
-\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen);\r
-}\r
-\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    z_streamp      zp = ctx->zp;\r
-    lclTocEntry*   tctx = (lclTocEntry*)te->formatData;\r
-\r
-    tctx->dataPos = _getFilePos(AH, ctx);\r
-\r
-    WriteInt(AH, te->id); /* For sanity check */\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-    if (AH->compression < 0 || AH->compression > 9) {\r
-   AH->compression = Z_DEFAULT_COMPRESSION;\r
-    }\r
-\r
-    if (AH->compression != 0) {\r
-   zp->zalloc = Z_NULL;\r
-   zp->zfree = Z_NULL;\r
-   zp->opaque = Z_NULL;\r
-\r
-   if (deflateInit(zp, AH->compression) != Z_OK)\r
-       die_horribly("%s: could not initialize compression library - %s\n",progname, zp->msg);\r
-    }\r
-\r
-#else\r
-\r
-    AH->compression = 0;\r
-\r
-#endif\r
-\r
-    /* Just be paranoid - maye End is called after Start, with no Write */\r
-    zp->next_out = ctx->zlibOut;\r
-    zp->avail_out = zlibOutSize;\r
-}\r
-\r
-static int _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush) \r
-{\r
-    z_streamp   zp = ctx->zp;\r
-\r
-#ifdef HAVE_LIBZ\r
-    char*  out = ctx->zlibOut;\r
-    int        res = Z_OK;\r
-\r
-    if (AH->compression != 0) \r
-    {\r
-   res = deflate(zp, flush);\r
-   if (res == Z_STREAM_ERROR)\r
-       die_horribly("%s: could not compress data - %s\n",progname, zp->msg);\r
-\r
-   if  (      ( (flush == Z_FINISH) && (zp->avail_out < zlibOutSize) )\r
-       || (zp->avail_out == 0) \r
-       || (zp->avail_in != 0)\r
-       ) \r
-   {\r
-       /*\r
-        * Extra paranoia: avoid zero-length chunks since a zero \r
-        * length chunk is the EOF marker. This should never happen\r
-        * but...\r
-       */\r
-       if (zp->avail_out < zlibOutSize) {\r
-       /* printf("Wrote %d byte deflated chunk\n", zlibOutSize - zp->avail_out); */\r
-       WriteInt(AH, zlibOutSize - zp->avail_out);\r
-       fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH);\r
-       ctx->filePos += zlibOutSize - zp->avail_out;\r
-       }\r
-       zp->next_out = out;\r
-       zp->avail_out = zlibOutSize;\r
-   }\r
-    } else {\r
-#endif\r
-   if (zp->avail_in > 0)\r
-   {\r
-       WriteInt(AH, zp->avail_in);\r
-       fwrite(zp->next_in, 1, zp->avail_in, AH->FH);\r
-       ctx->filePos += zp->avail_in;\r
-       zp->avail_in = 0;\r
-   } else {\r
-#ifdef HAVE_LIBZ\r
-       if (flush == Z_FINISH)\r
-       res = Z_STREAM_END;\r
-#endif\r
-   }\r
-\r
-\r
-#ifdef HAVE_LIBZ\r
-    }\r
-\r
-    return res;\r
-#else\r
-    return 1;\r
-#endif\r
-\r
-}\r
-\r
-static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)\r
-{\r
-    lclContext*    ctx = (lclContext*)AH->formatData;\r
-    z_streamp  zp = ctx->zp;\r
-\r
-    zp->next_in = (void*)data;\r
-    zp->avail_in = dLen;\r
-\r
-    while (zp->avail_in != 0) {\r
-   /* printf("Deflating %d bytes\n", dLen); */\r
-   _DoDeflate(AH, ctx, 0);\r
-    }\r
-    return dLen;\r
-}\r
-\r
-static void    _EndData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;\r
-\r
-#ifdef HAVE_LIBZ\r
-    z_streamp      zp = ctx->zp;\r
-    int            res;\r
-\r
-    if (AH->compression != 0)\r
-    {\r
-   zp->next_in = NULL;\r
-   zp->avail_in = 0;\r
-\r
-   do {    \r
-       /* printf("Ending data output\n"); */\r
-       res = _DoDeflate(AH, ctx, Z_FINISH);\r
-   } while (res != Z_STREAM_END);\r
-\r
-   if (deflateEnd(zp) != Z_OK)\r
-       die_horribly("%s: error closing compression stream - %s\n", progname, zp->msg);\r
-    }\r
-#endif\r
-\r
-    /* Send the end marker */\r
-    WriteInt(AH, 0);\r
-\r
-    tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;\r
-\r
-}\r
-\r
-/*\r
- * Print data for a gievn TOC entry\r
-*/\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)\r
-{\r
-    lclContext*    ctx = (lclContext*)AH->formatData;\r
-    int            id;\r
-    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;\r
-\r
-    if (tctx->dataPos == 0) \r
-   return;\r
-\r
-    if (!ctx->hasSeek || tctx->dataPos < 0) {\r
-   id = ReadInt(AH);\r
-\r
-   while (id != te->id) {\r
-       if (TocIDRequired(AH, id, ropt) & 2)\r
-       die_horribly("%s: Dumping a specific TOC data block out of order is not supported"\r
-                  " without on this input stream (fseek required)\n", progname);\r
-       _skipData(AH);\r
-       id = ReadInt(AH);\r
-   }\r
-    } else {\r
-\r
-   if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)\r
-       die_horribly("%s: error %d in file seek\n",progname, errno);\r
-\r
-   id = ReadInt(AH);\r
-\r
-    }\r
-\r
-    if (id != te->id)\r
-   die_horribly("%s: Found unexpected block ID (%d) when reading data - expected %d\n",\r
-           progname, id, te->id);\r
-\r
-    ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",\r
-       te->id, te->oid, te->desc, te->name);\r
-\r
-    _PrintData(AH);\r
-\r
-    ahprintf(AH, "\n\n");\r
-}\r
-\r
-/*\r
- * Print data from current file position.\r
-*/\r
-static void    _PrintData(ArchiveHandle* AH)\r
-{\r
-    lclContext*    ctx = (lclContext*)AH->formatData;\r
-    z_streamp  zp = ctx->zp;\r
-    int        blkLen;\r
-    char*  in = ctx->zlibIn;\r
-    int        cnt;\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-    int        res;\r
-    char*  out = ctx->zlibOut;\r
-\r
-    res = Z_OK;\r
-\r
-    if (AH->compression != 0) {\r
-   zp->zalloc = Z_NULL;\r
-   zp->zfree = Z_NULL;\r
-   zp->opaque = Z_NULL;\r
-\r
-   if (inflateInit(zp) != Z_OK)\r
-       die_horribly("%s: could not initialize compression library - %s\n", progname, zp->msg);\r
-    }\r
-\r
-#endif\r
-\r
-    blkLen = ReadInt(AH);\r
-    while (blkLen != 0) {\r
-   if (blkLen > ctx->inSize) {\r
-       free(ctx->zlibIn);\r
-       ctx->zlibIn = NULL;\r
-       ctx->zlibIn = (char*)malloc(blkLen);\r
-       if (!ctx->zlibIn)\r
-       die_horribly("%s: failed to allocate decompression buffer\n", progname);\r
-\r
-       ctx->inSize = blkLen;\r
-       in = ctx->zlibIn;\r
-   }\r
-   cnt = fread(in, 1, blkLen, AH->FH);\r
-   if (cnt != blkLen) \r
-       die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);\r
-\r
-   ctx->filePos += blkLen;\r
-\r
-   zp->next_in = in;\r
-   zp->avail_in = blkLen;\r
-\r
-#ifdef HAVE_LIBZ\r
-\r
-   if (AH->compression != 0) {\r
-\r
-       while (zp->avail_in != 0) {\r
-       zp->next_out = out;\r
-       zp->avail_out = zlibOutSize;\r
-       res = inflate(zp, 0);\r
-       if (res != Z_OK && res != Z_STREAM_END)\r
-           die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg);\r
-\r
-       out[zlibOutSize - zp->avail_out] = '\0';\r
-       ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);\r
-       }\r
-   } else {\r
-#endif\r
-       ahwrite(in, 1, zp->avail_in, AH);\r
-       zp->avail_in = 0;\r
-\r
-#ifdef HAVE_LIBZ\r
-   }\r
-#endif\r
-\r
-   blkLen = ReadInt(AH);\r
-    }\r
-\r
-#ifdef HAVE_LIBZ\r
-    if (AH->compression != 0) \r
-    {\r
-   zp->next_in = NULL;\r
-   zp->avail_in = 0;\r
-   while (res != Z_STREAM_END) {\r
-       zp->next_out = out;\r
-       zp->avail_out = zlibOutSize;\r
-       res = inflate(zp, 0);\r
-       if (res != Z_OK && res != Z_STREAM_END)\r
-       die_horribly("%s: unable to uncompress data - %s\n", progname, zp->msg);\r
-\r
-       out[zlibOutSize - zp->avail_out] = '\0';\r
-       ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);\r
-   }\r
-    }\r
-#endif\r
-\r
-}\r
-\r
-/*\r
- * Skip data from current file position.\r
-*/\r
-static void    _skipData(ArchiveHandle* AH)\r
-{\r
-    lclContext*    ctx = (lclContext*)AH->formatData;\r
-    int        blkLen;\r
-    char*  in = ctx->zlibIn;\r
-    int        cnt;\r
-\r
-    blkLen = ReadInt(AH);\r
-    while (blkLen != 0) {\r
-   if (blkLen > ctx->inSize) {\r
-       free(ctx->zlibIn);\r
-       ctx->zlibIn = (char*)malloc(blkLen);\r
-       ctx->inSize = blkLen;\r
-       in = ctx->zlibIn;\r
-   }\r
-   cnt = fread(in, 1, blkLen, AH->FH);\r
-   if (cnt != blkLen) \r
-       die_horribly("%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);\r
-\r
-   ctx->filePos += blkLen;\r
-\r
-   blkLen = ReadInt(AH);\r
-    }\r
-\r
-}\r
-\r
-static int _WriteByte(ArchiveHandle* AH, const int i)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-\r
-    res = fputc(i, AH->FH);\r
-    if (res != EOF) {\r
-   ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int     _ReadByte(ArchiveHandle* AH)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-\r
-    res = fgetc(AH->FH);\r
-    if (res != EOF) {\r
-   ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-    res = fwrite(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-    res = fread(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static void    _CloseArchive(ArchiveHandle* AH)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            tpos;\r
-\r
-    if (AH->mode == archModeWrite) {\r
-   WriteHead(AH);\r
-   tpos = ftell(AH->FH);\r
-   WriteToc(AH);\r
-   ctx->dataStart = _getFilePos(AH, ctx);\r
-   WriteDataChunks(AH);\r
-   /* This is not an essential operation - it is really only\r
-    * needed if we expect to be doing seeks to read the data back\r
-    * - it may be ok to just use the existing self-consistent block\r
-    * formatting.\r
-    */\r
-   if (ctx->hasSeek) {\r
-       fseek(AH->FH, tpos, SEEK_SET);\r
-       WriteToc(AH);\r
-   }\r
-    }\r
-\r
-    fclose(AH->FH);\r
-    AH->FH = NULL; \r
-}\r
-\r
-static int _getFilePos(ArchiveHandle* AH, lclContext* ctx) \r
-{\r
-    int        pos;\r
-    if (ctx->hasSeek) {\r
-   pos = ftell(AH->FH);\r
-   if (pos != ctx->filePos) {\r
-       fprintf(stderr, "Warning: ftell mismatch with filePos\n");\r
-   }\r
-    } else {\r
-   pos = ctx->filePos;\r
-    }\r
-    return pos;\r
-}\r
-\r
-\r
+/*-------------------------------------------------------------------------
+ *
+ * pg_backup_custom.c
+ *
+ * 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.
+ *
+ * Copyright (c) 2000, Philip Warner
+ *      Rights are granted to use this software in any way so long
+ *      as this notice is not removed.
+ *
+ * The author is not responsible for loss or damages that may
+ * and any liability will be limited to the time taken to fix any
+ *     related bug.
+ *
+ *
+ * IDENTIFICATION
+ *
+ * Modifications - 28-Jun-2000 - [email protected]
+ *
+ * Initial version. 
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include 
+#include "pg_backup.h"
+#include "pg_backup_archiver.h"
+#include 
+
+/*--------
+ * Routines in the format interface
+ *--------
+ */
+
+static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);
+static void        _StartData(ArchiveHandle* AH, TocEntry* te);
+static int     _WriteData(ArchiveHandle* AH, const void* data, int dLen);
+static void     _EndData(ArchiveHandle* AH, TocEntry* te);
+static int      _WriteByte(ArchiveHandle* AH, const int i);
+static int      _ReadByte(ArchiveHandle* );
+static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);
+static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len);
+static void     _CloseArchive(ArchiveHandle* AH);
+static void        _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
+static void        _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);
+static void        _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);
+static void        _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);
+
+static void        _PrintData(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 zlibInSize 4096
+
+typedef struct {
+    z_streamp  zp;
+    char*  zlibOut;
+    char*  zlibIn;
+    int        inSize;
+    int        hasSeek;
+    int        filePos;
+    int        dataStart;
+} lclContext;
+
+typedef struct {
+    int        dataPos;
+    int        dataLen;
+} 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     _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush);
+
+static char*   progname = "Archiver(custom)";
+
+
+
+/*
+ *  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) 
+{
+    lclContext*        ctx;
+
+    /* Assuming static functions, this can be copied for each format. */
+    AH->ArchiveEntryPtr = _ArchiveEntry;
+    AH->StartDataPtr = _StartData;
+    AH->WriteDataPtr = _WriteData;
+    AH->EndDataPtr = _EndData;
+    AH->WriteBytePtr = _WriteByte;
+    AH->ReadBytePtr = _ReadByte;
+    AH->WriteBufPtr = _WriteBuf;
+    AH->ReadBufPtr = _ReadBuf;
+    AH->ClosePtr = _CloseArchive;
+    AH->PrintTocDataPtr = _PrintTocData;
+    AH->ReadExtraTocPtr = _ReadExtraToc;
+    AH->WriteExtraTocPtr = _WriteExtraToc;
+    AH->PrintExtraTocPtr = _PrintExtraToc;
+
+    AH->StartBlobsPtr = _StartBlobs;
+    AH->StartBlobPtr = _StartBlob;
+    AH->EndBlobPtr = _EndBlob;
+    AH->EndBlobsPtr = _EndBlobs;
+
+    /*
+     * Set up some special context used in compressing data.
+    */
+    ctx = (lclContext*)malloc(sizeof(lclContext));
+    if (ctx == NULL)
+       die_horribly(AH, "%s: Unable to allocate archive context",progname);
+    AH->formatData = (void*)ctx;
+
+    ctx->zp = (z_streamp)malloc(sizeof(z_stream));
+    if (ctx->zp == NULL)
+   die_horribly(AH, "%s: unable to allocate zlib stream archive context",progname);
+
+    ctx->zlibOut = (char*)malloc(zlibOutSize);
+    ctx->zlibIn = (char*)malloc(zlibInSize);
+    ctx->inSize = zlibInSize;
+    ctx->filePos = 0;
+
+    if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)
+       die_horribly(AH, "%s: unable to allocate buffers in archive context",progname);
+
+    /*
+     * Now open the file
+    */
+    if (AH->mode == archModeWrite) {
+
+       if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
+           AH->FH = fopen(AH->fSpec, PG_BINARY_W);
+       } else {
+           AH->FH = stdout;
+       }
+
+       if (!AH)
+           die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
+
+       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
+
+   } else {
+
+       if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
+           AH->FH = fopen(AH->fSpec, PG_BINARY_R);
+       } else {
+           AH->FH = stdin;
+       }
+       if (!AH)
+           die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
+
+       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
+
+       ReadHead(AH);
+       ReadToc(AH);
+       ctx->dataStart = _getFilePos(AH, ctx);
+    }
+
+}
+
+/*
+ * 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) 
+{
+    lclTocEntry*   ctx;
+
+    ctx = (lclTocEntry*)calloc(1, sizeof(lclTocEntry));
+    if (te->dataDumper) {
+       ctx->dataPos = -1;
+    } else {
+       ctx->dataPos = 0;
+    }
+    ctx->dataLen = 0;
+    te->formatData = (void*)ctx;
+
+}
+
+/*
+ * 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)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    WriteInt(AH, ctx->dataPos);
+    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)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    if (ctx == NULL) {
+       ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
+       te->formatData = (void*)ctx;
+    }
+
+    ctx->dataPos = 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)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    lclTocEntry*   tctx = (lclTocEntry*)te->formatData;
+
+    tctx->dataPos = _getFilePos(AH, ctx);
+
+    _WriteByte(AH, BLK_DATA); /* Block type */
+    WriteInt(AH, te->id); /* For sanity check */
+
+    _StartDataCompressor(AH, te);
+
+}
+
+/*
+ * Called by archiver when dumper calls WriteData. This routine is 
+ * called for both BLOB and TABLE data; it is the responsibility of
+ * the format to manage each kind of data using StartBlob/StartData.
+ *
+ * It should only be called from withing a DataDumper routine.
+ *
+ * Mandatory.
+ *
+ */
+static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
+{
+    lclContext*    ctx = (lclContext*)AH->formatData;
+    z_streamp  zp = ctx->zp;
+
+    zp->next_in = (void*)data;
+    zp->avail_in = dLen;
+
+    while (zp->avail_in != 0) {
+   /* printf("Deflating %d bytes\n", dLen); */
+       _DoDeflate(AH, ctx, 0);
+    }
+    return dLen;
+}
+
+/*
+ * Called by the archiver when a dumper's 'DataDumper' routine has
+ * finished.
+ *
+ * Optional.
+ *
+ */
+static void    _EndData(ArchiveHandle* AH, TocEntry* te)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;
+
+    _EndDataCompressor(AH, te);
+
+    tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;
+}
+
+/*
+ * 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;
+    lclTocEntry*   tctx = (lclTocEntry*)te->formatData;
+
+    tctx->dataPos = _getFilePos(AH, ctx);
+
+    _WriteByte(AH, BLK_BLOBS); /* Block type */
+    WriteInt(AH, te->id); /* For sanity check */
+
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Print data for a gievn TOC entry
+*/
+static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
+{
+    lclContext*    ctx = (lclContext*)AH->formatData;
+    int            id;
+    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;
+    int            blkType;
+    int            found = 0;
+
+    if (tctx->dataPos == 0) 
+       return;
+
+    if (!ctx->hasSeek || tctx->dataPos < 0) {
+
+   /* Skip over unnecessary blocks until we get the one we want. */
+
+   found = 0;
+
+   _readBlockHeader(AH, &blkType, &id);
+
+   while (id != te->id) {
+
+       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);
+
+       switch (blkType) {
+
+           case BLK_DATA:
+
+               _skipData(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 {
+
+   /* Grab it */
+
+       if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)
+           die_horribly(AH, "%s: error %d in file seek\n",progname, errno);
+
+       _readBlockHeader(AH, &blkType, &id);
+
+    }
+
+    /* Are we sane? */
+    if (id != te->id)
+       die_horribly(AH, "%s: Found unexpected block ID (%d) when reading data - expected %d\n",
+                       progname, id, te->id);
+
+    switch (blkType) {
+
+       case BLK_DATA:
+
+           _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");
+}
+
+/*
+ * Print data from current file position.
+*/
+static void    _PrintData(ArchiveHandle* AH)
+{
+    lclContext*    ctx = (lclContext*)AH->formatData;
+    z_streamp  zp = ctx->zp;
+    int        blkLen;
+    char*  in = ctx->zlibIn;
+    int        cnt;
+#ifdef HAVE_LIBZ
+    int         res;
+    char*  out = ctx->zlibOut;
+#endif
+
+#ifdef HAVE_LIBZ
+
+    res = Z_OK;
+
+    if (AH->compression != 0) {
+       zp->zalloc = Z_NULL;
+       zp->zfree = Z_NULL;
+       zp->opaque = Z_NULL;
+
+       if (inflateInit(zp) != Z_OK)
+           die_horribly(AH, "%s: could not initialize compression library - %s\n", progname, zp->msg);
+    }
+
+#endif
+
+    blkLen = ReadInt(AH);
+    while (blkLen != 0) {
+       if (blkLen > (ctx->inSize - 1)) {
+           free(ctx->zlibIn);
+           ctx->zlibIn = NULL;
+           ctx->zlibIn = (char*)malloc(blkLen);
+           if (!ctx->zlibIn)
+               die_horribly(AH, "%s: failed to allocate decompression buffer\n", progname);
+
+           ctx->inSize = blkLen;
+           in = ctx->zlibIn;
+       }
+
+       cnt = fread(in, 1, blkLen, AH->FH);
+       if (cnt != blkLen) 
+           die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
+
+       ctx->filePos += blkLen;
+
+       zp->next_in = in;
+       zp->avail_in = blkLen;
+
+#ifdef HAVE_LIBZ
+
+       if (AH->compression != 0) {
+
+           while (zp->avail_in != 0) {
+               zp->next_out = out;
+               zp->avail_out = zlibOutSize;
+               res = inflate(zp, 0);
+               if (res != Z_OK && res != Z_STREAM_END)
+                   die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
+
+               out[zlibOutSize - zp->avail_out] = '\0';
+               ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
+           }
+       } else {
+#endif
+           in[zp->avail_in] = '\0';
+           ahwrite(in, 1, zp->avail_in, AH);
+           zp->avail_in = 0;
+
+#ifdef HAVE_LIBZ
+       }
+#endif
+
+       blkLen = ReadInt(AH);
+
+    }
+
+#ifdef HAVE_LIBZ
+    if (AH->compression != 0) 
+    {
+       zp->next_in = NULL;
+       zp->avail_in = 0;
+       while (res != Z_STREAM_END) {
+           zp->next_out = out;
+           zp->avail_out = zlibOutSize;
+           res = inflate(zp, 0);
+           if (res != Z_OK && res != Z_STREAM_END)
+               die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
+
+           out[zlibOutSize - zp->avail_out] = '\0';
+           ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
+       }
+    }
+#endif
+
+}
+
+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.
+ * 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)
+{
+    lclContext*    ctx = (lclContext*)AH->formatData;
+    int        blkLen;
+    char*  in = ctx->zlibIn;
+    int        cnt;
+
+    blkLen = ReadInt(AH);
+    while (blkLen != 0) {
+   if (blkLen > ctx->inSize) {
+       free(ctx->zlibIn);
+       ctx->zlibIn = (char*)malloc(blkLen);
+       ctx->inSize = blkLen;
+       in = ctx->zlibIn;
+   }
+   cnt = fread(in, 1, blkLen, AH->FH);
+   if (cnt != blkLen) 
+       die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
+
+   ctx->filePos += blkLen;
+
+   blkLen = ReadInt(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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+
+    res = fputc(i, AH->FH);
+    if (res != EOF) {
+       ctx->filePos += 1;
+    }
+    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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+
+    res = fgetc(AH->FH);
+    if (res != EOF) {
+       ctx->filePos += 1;
+    }
+    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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+    res = fwrite(buf, 1, len, AH->FH);
+    ctx->filePos += 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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int                res;
+    res = fread(buf, 1, len, AH->FH);
+    ctx->filePos += 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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            tpos;
+
+    if (AH->mode == archModeWrite) {
+       WriteHead(AH);
+       tpos = ftell(AH->FH);
+       WriteToc(AH);
+       ctx->dataStart = _getFilePos(AH, ctx);
+       WriteDataChunks(AH);
+       /* This is not an essential operation - it is really only
+        * needed if we expect to be doing seeks to read the data back
+        * - it may be ok to just use the existing self-consistent block
+        * formatting.
+        */
+       if (ctx->hasSeek) {
+           fseek(AH->FH, tpos, SEEK_SET);
+           WriteToc(AH);
+       }
+    }
+
+    fclose(AH->FH);
+    AH->FH = NULL; 
+}
+
+/*--------------------------------------------------
+ * END OF FORMAT CALLBACKS
+ *--------------------------------------------------
+ */
+
+/*
+ * Get the current position in the archive file.
+ */
+static int _getFilePos(ArchiveHandle* AH, lclContext* ctx) 
+{
+    int        pos;
+    if (ctx->hasSeek) {
+       pos = ftell(AH->FH);
+       if (pos != ctx->filePos) {
+           fprintf(stderr, "Warning: ftell mismatch with filePos\n");
+       }
+    } else {
+       pos = ctx->filePos;
+    }
+    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);
+}
+
+
index ef2ea57f2ce61971eb1b2e1e7eb84b50fb66ba20..1583a497b9c87e4c27f1d040c60e681327f75a91 100644 (file)
-/*-------------------------------------------------------------------------\r
- *\r
- * pg_backup_files.c\r
- *\r
- * This file is copied from the 'custom' format file, but dumps data into\r
- * separate files, and the TOC into the 'main' file.\r
- *\r
- * IT IS FOR DEMONSTRATION PURPOSES ONLY.\r
- *\r
- * (and could probably be used as a basis for writing a tar file)\r
- *\r
- * See the headers to pg_restore for more details.\r
- *\r
- * Copyright (c) 2000, Philip Warner\r
- *      Rights are granted to use this software in any way so long\r
- *      as this notice is not removed.\r
- *\r
- * The author is not responsible for loss or damages that may\r
- * result from it's use.\r
- *\r
- *\r
- * IDENTIFICATION\r
- *\r
- * Modifications - 28-Jun-2000 - [email protected]\r
- *\r
- * Initial version. \r
- *\r
- *-------------------------------------------------------------------------\r
- */\r
-\r
-#include \r
-#include \r
-#include "pg_backup.h"\r
-#include "pg_backup_archiver.h"\r
-\r
-static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te);\r
-static int _WriteData(ArchiveHandle* AH, const void* data, int dLen);\r
-static void     _EndData(ArchiveHandle* AH, TocEntry* te);\r
-static int      _WriteByte(ArchiveHandle* AH, const int i);\r
-static int      _ReadByte(ArchiveHandle* );\r
-static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);\r
-static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len);\r
-static void     _CloseArchive(ArchiveHandle* AH);\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);\r
-\r
-\r
-typedef struct {\r
-    int        hasSeek;\r
-    int        filePos;\r
-} lclContext;\r
-\r
-typedef struct {\r
-#ifdef HAVE_LIBZ\r
-    gzFile *FH;\r
-#else\r
-    FILE   *FH;\r
-#endif\r
-    char   *filename;\r
-} lclTocEntry;\r
-\r
-/*\r
- *  Initializer\r
- */\r
-void InitArchiveFmt_Files(ArchiveHandle* AH) \r
-{\r
-    lclContext*        ctx;\r
-\r
-    /* Assuming static functions, this can be copied for each format. */\r
-    AH->ArchiveEntryPtr = _ArchiveEntry;\r
-    AH->StartDataPtr = _StartData;\r
-    AH->WriteDataPtr = _WriteData;\r
-    AH->EndDataPtr = _EndData;\r
-    AH->WriteBytePtr = _WriteByte;\r
-    AH->ReadBytePtr = _ReadByte;\r
-    AH->WriteBufPtr = _WriteBuf;\r
-    AH->ReadBufPtr = _ReadBuf;\r
-    AH->ClosePtr = _CloseArchive;\r
-    AH->PrintTocDataPtr = _PrintTocData;\r
-    AH->ReadExtraTocPtr = _ReadExtraToc;\r
-    AH->WriteExtraTocPtr = _WriteExtraToc;\r
-    AH->PrintExtraTocPtr = _PrintExtraToc;\r
-\r
-    /*\r
-     * Set up some special context used in compressing data.\r
-    */\r
-    ctx = (lclContext*)malloc(sizeof(lclContext));\r
-    AH->formatData = (void*)ctx;\r
-    ctx->filePos = 0;\r
-\r
-    /*\r
-     * Now open the TOC file\r
-     */\r
-    if (AH->mode == archModeWrite) {\r
-   if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-       AH->FH = fopen(AH->fSpec, PG_BINARY_W);\r
-   } else {\r
-       AH->FH = stdout;\r
-   }\r
-   ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-   if (AH->compression < 0 || AH->compression > 9) {\r
-       AH->compression = Z_DEFAULT_COMPRESSION;\r
-   }\r
-\r
-\r
-    } else {\r
-   if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {\r
-       AH->FH = fopen(AH->fSpec, PG_BINARY_R);\r
-   } else {\r
-       AH->FH = stdin;\r
-   }\r
-   ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);\r
-\r
-   ReadHead(AH);\r
-   ReadToc(AH);\r
-   fclose(AH->FH); /* Nothing else in the file... */\r
-    }\r
-\r
-}\r
-\r
-/*\r
- * - Start a new TOC entry\r
- *   Setup the output file name.\r
- */\r
-static void    _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) \r
-{\r
-    lclTocEntry*   ctx;\r
-    char       fn[1024];\r
-\r
-    ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-    if (te->dataDumper) {\r
-#ifdef HAVE_LIBZ\r
-   if (AH->compression == 0) {\r
-       sprintf(fn, "%d.dat", te->id);\r
-   } else {\r
-       sprintf(fn, "%d.dat.gz", te->id);\r
-   }\r
-#else\r
-   sprintf(fn, "%d.dat", te->id);\r
-#endif\r
-   ctx->filename = strdup(fn);\r
-    } else {\r
-   ctx->filename = NULL;\r
-   ctx->FH = NULL;\r
-    }\r
-    te->formatData = (void*)ctx;\r
-}\r
-\r
-static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    if (ctx->filename) {\r
-   WriteStr(AH, ctx->filename);\r
-    } else {\r
-   WriteStr(AH, "");\r
-    }\r
-}\r
-\r
-static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    if (ctx == NULL) {\r
-   ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));\r
-   te->formatData = (void*)ctx;\r
-    }\r
-\r
-    ctx->filename = ReadStr(AH);\r
-    if (strlen(ctx->filename) == 0) {\r
-   free(ctx->filename);\r
-   ctx->filename = NULL;\r
-    }\r
-    ctx->FH = NULL;\r
-}\r
-\r
-static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;\r
-\r
-    ahprintf(AH, "-- File: %s\n", ctx->filename);\r
-}\r
-\r
-static void    _StartData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   tctx = (lclTocEntry*)te->formatData;\r
-    char       fmode[10];\r
-\r
-    sprintf(fmode, "wb%d", AH->compression);\r
-\r
-#ifdef HAVE_LIBZ\r
-    tctx->FH = gzopen(tctx->filename, fmode);\r
-#else\r
-    tctx->FH = fopen(tctx->filename, PG_BINARY_W);\r
-#endif\r
-}\r
-\r
-static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)\r
-{\r
-    lclTocEntry*   tctx = (lclTocEntry*)AH->currToc->formatData;\r
-\r
-    GZWRITE((void*)data, 1, dLen, tctx->FH);\r
-\r
-    return dLen;\r
-}\r
-\r
-static void    _EndData(ArchiveHandle* AH, TocEntry* te)\r
-{\r
-    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;\r
-\r
-    /* Close the file */\r
-    GZCLOSE(tctx->FH);\r
-    tctx->FH = NULL;\r
-}\r
-\r
-/*\r
- * Print data for a given TOC entry\r
-*/\r
-static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)\r
-{\r
-    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;\r
-    char       buf[4096];\r
-    int            cnt;\r
-\r
-    if (!tctx->filename) \r
-   return;\r
-\r
-#ifdef HAVE_LIBZ\r
-    AH->FH = gzopen(tctx->filename,"rb");\r
-#else\r
-    AH->FH = fopen(tctx->filename,PG_BINARY_R);\r
-#endif\r
-\r
-    ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",\r
-       te->id, te->oid, te->desc, te->name);\r
-\r
-    while ( (cnt = GZREAD(buf, 1, 4096, AH->FH)) > 0) {\r
-   ahwrite(buf, 1, cnt, AH);\r
-    }\r
-\r
-    GZCLOSE(AH->FH);\r
-\r
-    ahprintf(AH, "\n\n");\r
-}\r
-\r
-static int _WriteByte(ArchiveHandle* AH, const int i)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-\r
-    res = fputc(i, AH->FH);\r
-    if (res != EOF) {\r
-   ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int     _ReadByte(ArchiveHandle* AH)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-\r
-    res = fgetc(AH->FH);\r
-    if (res != EOF) {\r
-   ctx->filePos += 1;\r
-    }\r
-    return res;\r
-}\r
-\r
-static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-    res = fwrite(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)\r
-{\r
-    lclContext*        ctx = (lclContext*)AH->formatData;\r
-    int            res;\r
-    res = fread(buf, 1, len, AH->FH);\r
-    ctx->filePos += res;\r
-    return res;\r
-}\r
-\r
-static void    _CloseArchive(ArchiveHandle* AH)\r
-{\r
-    if (AH->mode == archModeWrite) {\r
-   WriteHead(AH);\r
-   WriteToc(AH);\r
-   fclose(AH->FH);\r
-   WriteDataChunks(AH);\r
-    }\r
-\r
-    AH->FH = NULL; \r
-}\r
-\r
+/*-------------------------------------------------------------------------
+ *
+ * pg_backup_files.c
+ *
+ * This file is copied from the 'custom' format file, but dumps data into
+ * separate files, and the TOC into the 'main' file.
+ *
+ * IT IS FOR DEMONSTRATION PURPOSES ONLY.
+ *
+ * (and could probably be used as a basis for writing a tar file)
+ *
+ * See the headers to pg_restore for more details.
+ *
+ * Copyright (c) 2000, Philip Warner
+ *      Rights are granted to use this software in any way so long
+ *      as this notice is not removed.
+ *
+ * The author is not responsible for loss or damages that may
+ * result from it's use.
+ *
+ *
+ * IDENTIFICATION
+ *
+ * Modifications - 28-Jun-2000 - [email protected]
+ *
+ * Initial version. 
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include 
+#include 
+#include "pg_backup.h"
+#include "pg_backup_archiver.h"
+
+static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);
+static void    _StartData(ArchiveHandle* AH, TocEntry* te);
+static int _WriteData(ArchiveHandle* AH, const void* data, int dLen);
+static void     _EndData(ArchiveHandle* AH, TocEntry* te);
+static int      _WriteByte(ArchiveHandle* AH, const int i);
+static int      _ReadByte(ArchiveHandle* );
+static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);
+static int     _ReadBuf(ArchiveHandle* AH, void* buf, int len);
+static void     _CloseArchive(ArchiveHandle* AH);
+static void    _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
+static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);
+static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);
+static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);
+
+static void    _StartBlobs(ArchiveHandle* AH, TocEntry* te);
+static void        _StartBlob(ArchiveHandle* AH, TocEntry* te, 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 {
+    int        hasSeek;
+    int        filePos;
+   FILE    *blobToc;
+} lclContext;
+
+typedef struct {
+#ifdef HAVE_LIBZ
+    gzFile *FH;
+#else
+    FILE   *FH;
+#endif
+    char   *filename;
+} lclTocEntry;
+
+static char* progname = "Archiver(files)";
+static void _LoadBlobs(ArchiveHandle* AH, RestoreOptions *ropt);
+static void _getBlobTocEntry(ArchiveHandle* AH, int *oid, char *fname);
+
+/*
+ *  Initializer
+ */
+void InitArchiveFmt_Files(ArchiveHandle* AH) 
+{
+    lclContext*        ctx;
+
+    /* Assuming static functions, this can be copied for each format. */
+    AH->ArchiveEntryPtr = _ArchiveEntry;
+    AH->StartDataPtr = _StartData;
+    AH->WriteDataPtr = _WriteData;
+    AH->EndDataPtr = _EndData;
+    AH->WriteBytePtr = _WriteByte;
+    AH->ReadBytePtr = _ReadByte;
+    AH->WriteBufPtr = _WriteBuf;
+    AH->ReadBufPtr = _ReadBuf;
+    AH->ClosePtr = _CloseArchive;
+    AH->PrintTocDataPtr = _PrintTocData;
+    AH->ReadExtraTocPtr = _ReadExtraToc;
+    AH->WriteExtraTocPtr = _WriteExtraToc;
+    AH->PrintExtraTocPtr = _PrintExtraToc;
+
+    AH->StartBlobsPtr = _StartBlobs;
+    AH->StartBlobPtr = _StartBlob;
+    AH->EndBlobPtr = _EndBlob;
+    AH->EndBlobsPtr = _EndBlobs;
+
+    /*
+     * Set up some special context used in compressing data.
+    */
+    ctx = (lclContext*)malloc(sizeof(lclContext));
+    AH->formatData = (void*)ctx;
+    ctx->filePos = 0;
+
+    /*
+     * Now open the TOC file
+     */
+    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) {
+           AH->FH = fopen(AH->fSpec, PG_BINARY_W);
+       } else {
+           AH->FH = stdout;
+       }
+       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
+
+       if (AH->compression < 0 || AH->compression > 9) {
+           AH->compression = Z_DEFAULT_COMPRESSION;
+       }
+
+
+    } else { /* Read Mode */
+
+       if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
+           AH->FH = fopen(AH->fSpec, PG_BINARY_R);
+       } else {
+           AH->FH = stdin;
+       }
+       ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
+
+       ReadHead(AH);
+       ReadToc(AH);
+       fclose(AH->FH); /* Nothing else in the file... */
+    }
+
+}
+
+/*
+ * - Start a new TOC entry
+ *   Setup the output file name.
+ */
+static void    _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) 
+{
+    lclTocEntry*   ctx;
+    char           fn[K_STD_BUF_SIZE];
+
+    ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
+    if (te->dataDumper) {
+#ifdef HAVE_LIBZ
+       if (AH->compression == 0) {
+           sprintf(fn, "%d.dat", te->id);
+       } else {
+           sprintf(fn, "%d.dat.gz", te->id);
+       }
+#else
+       sprintf(fn, "%d.dat", te->id);
+#endif
+       ctx->filename = strdup(fn);
+    } else {
+       ctx->filename = NULL;
+       ctx->FH = NULL;
+    }
+    te->formatData = (void*)ctx;
+}
+
+static void    _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    if (ctx->filename) {
+       WriteStr(AH, ctx->filename);
+    } else {
+       WriteStr(AH, "");
+    }
+}
+
+static void    _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    if (ctx == NULL) {
+       ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
+       te->formatData = (void*)ctx;
+    }
+
+    ctx->filename = ReadStr(AH);
+    if (strlen(ctx->filename) == 0) {
+       free(ctx->filename);
+       ctx->filename = NULL;
+    }
+    ctx->FH = NULL;
+}
+
+static void    _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)
+{
+    lclTocEntry*   ctx = (lclTocEntry*)te->formatData;
+
+    ahprintf(AH, "-- File: %s\n", ctx->filename);
+}
+
+static void    _StartData(ArchiveHandle* AH, TocEntry* te)
+{
+    lclTocEntry*   tctx = (lclTocEntry*)te->formatData;
+    char       fmode[10];
+
+    sprintf(fmode, "wb%d", AH->compression);
+
+#ifdef HAVE_LIBZ
+    tctx->FH = gzopen(tctx->filename, fmode);
+#else
+    tctx->FH = fopen(tctx->filename, PG_BINARY_W);
+#endif
+}
+
+static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
+{
+    lclTocEntry*   tctx = (lclTocEntry*)AH->currToc->formatData;
+
+    GZWRITE((void*)data, 1, dLen, tctx->FH);
+
+    return dLen;
+}
+
+static void    _EndData(ArchiveHandle* AH, TocEntry* te)
+{
+    lclTocEntry*   tctx = (lclTocEntry*) te->formatData;
+
+    /* Close the file */
+    GZCLOSE(tctx->FH);
+    tctx->FH = NULL;
+}
+
+/* 
+ * Print data for a given file 
+ */
+static void    _PrintFileData(ArchiveHandle* AH, char *filename, RestoreOptions *ropt)
+{
+    char       buf[4096];
+    int            cnt;
+
+    if (!filename) 
+       return;
+
+#ifdef HAVE_LIBZ
+    AH->FH = gzopen(filename,"rb");
+#else
+    AH->FH = fopen(filename,PG_BINARY_R);
+#endif
+
+    while ( (cnt = GZREAD(buf, 1, 4095, AH->FH)) > 0) {
+       buf[cnt] = '\0';
+       ahwrite(buf, 1, cnt, AH);
+    }
+
+    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, " ");
+
+       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)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+
+    res = fputc(i, AH->FH);
+    if (res != EOF) {
+       ctx->filePos += 1;
+    }
+    return res;
+}
+
+static int     _ReadByte(ArchiveHandle* AH)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+
+    res = fgetc(AH->FH);
+    if (res != EOF) {
+       ctx->filePos += 1;
+    }
+    return res;
+}
+
+static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+    res = fwrite(buf, 1, len, AH->FH);
+    ctx->filePos += res;
+    return res;
+}
+
+static int _ReadBuf(ArchiveHandle* AH, void* buf, int len)
+{
+    lclContext*        ctx = (lclContext*)AH->formatData;
+    int            res;
+
+    res = fread(buf, 1, len, AH->FH);
+    ctx->filePos += res;
+    return res;
+}
+
+static void    _CloseArchive(ArchiveHandle* AH)
+{
+    if (AH->mode == archModeWrite) {
+       WriteHead(AH);
+       WriteToc(AH);
+       fclose(AH->FH);
+       WriteDataChunks(AH);
+    }
+
+    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);
+
+}
+
+
index 8e8e81feb49067cd7b7377feb3ecc28a41a9a28a..e4a47b9ef10d0d26ad098f04ebe6e53aa47fe969 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * 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 - [email protected] - version 1.13.dhb
  *
  *      - Added a -Z option for compression level on compressed formats
  *      - Restored '-f' in usage output
  *
-*-------------------------------------------------------------------------
+ *
+ * Modifications - 17-Jul-2000 - Philip Warner [email protected]
+ *      - Support for BLOB output.
+ *      - Sort archive by OID, put some items at end (out of OID order)
+ *
+ *-------------------------------------------------------------------------
  */
 
 #include                 /* for getopt() */
 #include 
 
+#include "pg_backup.h"
+
 #include "postgres.h"
 
 #ifdef HAVE_GETOPT_H
@@ -84,6 +91,7 @@
 #include "catalog/pg_type.h"
 
 #include "libpq-fe.h"
+#include 
 #ifndef HAVE_STRDUP
 #include "strdup.h"
 #endif
@@ -109,6 +117,9 @@ static void setMaxOid(Archive *fout);
 static void AddAcl(char *aclbuf, const char *keyword);
 static char *GetPrivileges(const char *s);
 
+static int dumpBlobs(Archive *AH, char*, void*);
+
+
 extern char *optarg;
 extern int optind,
            opterr;
@@ -268,14 +279,26 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
 
    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",
                fmtId(classname, force_quotes));
    }
    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));
    }
    res = PQexec(g_conn, query);
@@ -452,19 +475,28 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
  */
 static void
 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;
    char            *all_only;
    DataDumperPtr   dumpFn;
    DumpContext     *dumpCtx;
+   char            *oidsPart;
+   char            copyBuf[512];
+   char            *copyStmt;
 
    if (onlytable == NULL)
        all_only = "all";
    else
        all_only = "only";
 
+   if (oids == true)
+       oidsPart = "WITH OIDS ";
+   else
+       oidsPart = "";
+
+
    if (g_verbose)
        fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n",
                g_comment_start, all_only,
@@ -514,112 +546,28 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
            dumpCtx->tblidx = i;
            dumpCtx->oids = oids;
 
-           if (!dumpData)
+           if (!dumpData) /* Dump/restore using COPY */
+           {
                dumpFn = dumpClasses_nodumpData;
                /* 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;
                /* dumpClasses_dumpData(fout, classname); */
+               copyStmt = NULL;
+           }
 
            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);
        }
    }
 }
 
-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
 main(int argc, char **argv)
 {
@@ -634,20 +582,19 @@ main(int argc, char **argv)
    bool        oids = false;
    TableInfo  *tblinfo;
    int         numTables;
-   char        connect_string[512] = "";
-   char        tmp_string[128];
-   char        username[100];
-   char        password[100];
    bool        use_password = false;
    int         compressLevel = -1;
    bool        ignore_version = false;
-   int     plainText = 0;
-   int     outputClean = 0;
+   int         plainText = 0;
+   int         outputClean = 0;
+   int         outputBlobs = 0;
+
    RestoreOptions  *ropt;
 
 #ifdef HAVE_GETOPT_LONG
    static struct option long_options[] = {
        {"data-only", no_argument, NULL, 'a'},
+       {"blobs", no_argument, NULL, 'b' },
        {"clean", no_argument, NULL, 'c'},
        {"file", required_argument, NULL, 'f'},
        {"format", required_argument, NULL, 'F'},
@@ -686,6 +633,12 @@ main(int argc, char **argv)
    else
        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
    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)
            case 'a':           /* Dump data only */
                dataOnly = true;
                break;
+           case 'b':           /* Dump blobs */
+               outputBlobs = true;
+               break;
+
            case 'c':           /* clean (i.e., drop) schema prior to
                                 * create */
                outputClean = 1;
@@ -843,7 +800,12 @@ main(int argc, char **argv)
        case 'p':
        case 'P':
            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;
 
        default:
@@ -860,53 +822,13 @@ main(int argc, char **argv)
        exit(1);
    }
 
-   /* find database */
-   if (!(dbname = argv[optind]) &&
-       !(dbname = getenv("PGDATABASE")))
-   {
-       fprintf(stderr, "%s: no database name specified\n", progname);
-       exit(1);
-   }
+   /* Let the archiver know how noisy to be */
+   g_fout->verbose = g_verbose;
 
-   /* g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname); */
-   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);
-   }
+   dbname = argv[optind];
 
-   /* check for version mismatch */
-   check_database_version(ignore_version);
+   /* Open the database using the Archiver, so it knows about it. Errors mean death */
+   g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, use_password, ignore_version);
 
    /*
     * Start serializable transaction to dump consistent data
@@ -916,17 +838,15 @@ main(int argc, char **argv)
 
        res = PQexec(g_conn, "begin");
        if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-       {
-           fprintf(stderr, "BEGIN command failed.  Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
-           exit_nicely(g_conn);
-       }
+           exit_horribly(g_fout, "BEGIN command failed. Explanation from backend: '%s'.\n",
+               PQerrorMessage(g_conn));
+
        PQclear(res);
        res = PQexec(g_conn, "set transaction isolation level serializable");
        if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-       {
-           fprintf(stderr, "SET TRANSACTION command failed.  Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
-           exit_nicely(g_conn);
-       }
+           exit_horribly(g_fout, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n",
+               PQerrorMessage(g_conn));
+
        PQclear(res);
    }
 
@@ -941,7 +861,12 @@ main(int argc, char **argv)
    tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
 
    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
                                 * for performance */
@@ -951,6 +876,15 @@ main(int argc, char **argv)
        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) 
    {
        ropt = NewRestoreOptions();
@@ -973,6 +907,92 @@ main(int argc, char **argv)
    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:
  *   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)
                            target, checkForQuote(PQgetvalue(res, 0, i_description)));
 
        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,
            appendPQExpBuffer(q, ");\n");
 
        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 ***/
 
@@ -2629,7 +2649,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
                lancompiler);
 
        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(lancompiler);
@@ -2669,8 +2689,8 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
    PQExpBuffer fn = createPQExpBuffer();
    PQExpBuffer delqry = createPQExpBuffer();
    PQExpBuffer fnlist = createPQExpBuffer();
-   PQExpBuffer asPart = createPQExpBuffer();
    int         j;
+   PQExpBuffer asPart = createPQExpBuffer();
    char        func_lang[NAMEDATALEN + 1];
    PGresult   *res;
    int         nlangs;
@@ -2703,7 +2723,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
   
    i_lanname = PQfnumber(res, "lanname");
 
-   /*
+   /*
     * See backend/commands/define.c for details of how the 'AS' clause
     * is used.
     */
@@ -2751,7 +2771,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
                      asPart->data, func_lang);
 
    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 ***/
 
@@ -2870,7 +2890,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
                          sort2->data);
 
        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,
                          details->data);
 
        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 ***/
 
@@ -3096,7 +3116,7 @@ dumpACL(Archive *fout, TableInfo tbinfo)
 
    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,
 
            if (!dataOnly) {
                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);
            }
 
@@ -3468,7 +3488,7 @@ dumpIndices(Archive *fout, IndInfo *indinfo, int numIndices,
            /* Dump Index Comments */
 
            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);
            appendPQExpBuffer(q, "INDEX %s", id1->data);
@@ -3599,7 +3619,7 @@ setMaxOid(Archive *fout)
    pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n");
    pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n");
 
-   ArchiveEntry(fout, "0", "Max OID", "", NULL, sql, "","", NULL, NULL);
+   ArchiveEntry(fout, "0", "Max OID", "", NULL, sql, "", "", "", NULL, NULL);
 }
 
 /*
@@ -3750,7 +3770,7 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
    }
 
    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 */
 
@@ -3779,7 +3799,7 @@ dumpTriggers(Archive *fout, const char *tablename,
        for (j = 0; j < tblinfo[i].ntrig; j++)
        {
            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);
            dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid);
        }
@@ -3846,7 +3866,7 @@ dumpRules(Archive *fout, const char *tablename,
 
            ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
                            "RULE", NULL, PQgetvalue(res, i, i_definition),
-                           "", "", NULL, NULL);
+                           "", "", "", NULL, NULL);
 
            /* Dump rule comments */
 
index 7b1a17c3f70d63cfa45157eaec7f5f9420a90f79..b39d8f7802bee007b4800fa235606360b273f5fe 100644 (file)
-/*-------------------------------------------------------------------------\r
- *\r
- * pg_restore.c\r
- * pg_restore is an utility extracting postgres database definitions\r
- * from a backup archive created by pg_dump using the archiver \r
- * interface.\r
- *\r
- * pg_restore will read the backup archive and\r
- * dump out a script that reproduces\r
- * the schema of the database in terms of\r
- *       user-defined types\r
- *       user-defined functions\r
- *       tables\r
- *       indices\r
- *       aggregates\r
- *       operators\r
- *       ACL - grant/revoke\r
- *\r
- * the output script is SQL that is understood by PostgreSQL\r
- *\r
- * Basic process in a restore operation is:\r
- * \r
- *     Open the Archive and read the TOC.\r
- *     Set flags in TOC entries, and *maybe* reorder them.\r
- *     Generate script to stdout\r
- *     Exit\r
- *\r
- * Copyright (c) 2000, Philip Warner\r
- *      Rights are granted to use this software in any way so long\r
- *      as this notice is not removed.\r
- *\r
- * The author is not responsible for loss or damages that may\r
- * result from it's use.\r
- *\r
- *\r
- * IDENTIFICATION\r
- *\r
- * Modifications - 28-Jun-2000 - [email protected]\r
- *\r
- * Initial version. Command processing taken from original pg_dump.\r
- *\r
- *-------------------------------------------------------------------------\r
- */\r
-\r
-#include \r
-#include \r
-#include \r
-#include \r
-\r
-\r
-/*\r
-#include "postgres.h"\r
-#include "access/htup.h"\r
-#include "catalog/pg_type.h"\r
-#include "catalog/pg_language.h"\r
-#include "catalog/pg_index.h"\r
-#include "catalog/pg_trigger.h"\r
-#include "libpq-fe.h"\r
-*/\r
-\r
-#include "pg_backup.h"\r
-\r
-#ifndef HAVE_STRDUP\r
-#include "strdup.h"\r
-#endif\r
-\r
-#ifdef HAVE_TERMIOS_H\r
-#include \r
-#endif\r
-\r
-#ifdef HAVE_GETOPT_H \r
-#include \r
-#else\r
-#include \r
-#endif\r
-\r
-/* Forward decls */\r
-static void usage(const char *progname);\r
-static char* _cleanupName(char* name);\r
-\r
-typedef struct option optType;\r
-\r
-#ifdef HAVE_GETOPT_H\r
-struct option cmdopts[] = {    \r
-               { "clean", 0, NULL, 'c' },\r
-               { "data-only", 0, NULL, 'a' },\r
-               { "file", 1, NULL, 'f' },\r
-               { "format", 1, NULL, 'F' },\r
-               { "function", 2, NULL, 'p' },\r
-               { "index", 2, NULL, 'i'},\r
-               { "list", 0, NULL, 'l'},\r
-               { "no-acl", 0, NULL, 'x' },\r
-               { "oid-order", 0, NULL, 'o'},\r
-               { "orig-order", 0, NULL, 'O' },\r
-               { "rearrange", 0, NULL, 'r'},\r
-               { "schema-only", 0, NULL, 's' },\r
-               { "table", 2, NULL, 't'},\r
-               { "trigger", 2, NULL, 'T' },\r
-               { "use-list", 1, NULL, 'u'},\r
-               { "verbose", 0, NULL, 'v' },\r
-               { NULL, 0, NULL, 0}\r
-               };\r
-#endif\r
-\r
-int main(int argc, char **argv)\r
-{\r
-   RestoreOptions  *opts;\r
-   char        *progname;\r
-   int     c;\r
-   Archive*        AH;\r
-   char        *fileSpec = NULL;\r
-\r
-   opts = NewRestoreOptions();\r
-\r
-   progname = *argv;\r
-\r
-#ifdef HAVE_GETOPT_LONG\r
-   while ((c = getopt_long(argc, argv, "acf:F:i:loOp:st:T:u:vx", cmdopts, NULL)) != EOF)\r
-#else\r
-   while ((c = getopt(argc, argv, "acf:F:i:loOp:st:T:u:vx")) != -1)\r
-#endif\r
-   {\r
-       switch (c)\r
-       {\r
-           case 'a':           /* Dump data only */\r
-               opts->dataOnly = 1;\r
-               break;\r
-           case 'c':           /* clean (i.e., drop) schema prior to\r
-                                * create */\r
-               opts->dropSchema = 1;\r
-               break;\r
-           case 'f':           /* output file name */\r
-               opts->filename = strdup(optarg);\r
-               break;\r
-           case 'F':\r
-               if (strlen(optarg) != 0) \r
-                   opts->formatName = strdup(optarg);\r
-               break;\r
-           case 'o':\r
-               opts->oidOrder = 1;\r
-               break;\r
-           case 'O':\r
-               opts->origOrder = 1;\r
-               break;\r
-           case 'r':\r
-               opts->rearrange = 1;\r
-               break;\r
-\r
-           case 'p': /* Function */\r
-               opts->selTypes = 1;\r
-               opts->selFunction = 1;\r
-               opts->functionNames = _cleanupName(optarg);\r
-               break;\r
-           case 'i': /* Index */\r
-               opts->selTypes = 1;\r
-               opts->selIndex = 1;\r
-               opts->indexNames = _cleanupName(optarg);\r
-               break;\r
-           case 'T': /* Trigger */\r
-               opts->selTypes = 1;\r
-               opts->selTrigger = 1;\r
-               opts->triggerNames = _cleanupName(optarg);\r
-               break;\r
-           case 's':           /* dump schema only */\r
-               opts->schemaOnly = 1;\r
-               break;\r
-           case 't':           /* Dump data for this table only */\r
-               opts->selTypes = 1;\r
-               opts->selTable = 1;\r
-               opts->tableNames = _cleanupName(optarg);\r
-               break;\r
-           case 'l':           /* Dump the TOC summary */\r
-               opts->tocSummary = 1;\r
-               break;\r
-\r
-           case 'u':           /* input TOC summary file name */\r
-               opts->tocFile = strdup(optarg);\r
-               break;\r
-\r
-           case 'v':           /* verbose */\r
-               opts->verbose = 1;\r
-               break;\r
-           case 'x':           /* skip ACL dump */\r
-               opts->aclsSkip = 1;\r
-               break;\r
-           default:\r
-               usage(progname);\r
-               break;\r
-       }\r
-   }\r
-\r
-   if (optind < argc) {\r
-       fileSpec = argv[optind];\r
-   } else {\r
-       fileSpec = NULL;\r
-   }\r
-\r
-    if (opts->formatName) { \r
-\r
-   switch (opts->formatName[0]) {\r
-\r
-       case 'c':\r
-       case 'C':\r
-       opts->format = archCustom;\r
-       break;\r
-\r
-       case 'f':\r
-       case 'F':\r
-       opts->format = archFiles;\r
-       break;\r
-\r
-       default:\r
-       fprintf(stderr, "%s: Unknown archive format '%s', please specify 'f' or 'c'\n", progname, opts->formatName);\r
-       exit (1);\r
-   }\r
-    }\r
-\r
-    AH = OpenArchive(fileSpec, opts->format);\r
-\r
-    if (opts->tocFile)\r
-   SortTocFromFile(AH, opts);\r
-\r
-    if (opts->oidOrder)\r
-   SortTocByOID(AH);\r
-    else if (opts->origOrder)\r
-   SortTocByID(AH);\r
-\r
-    if (opts->rearrange) {\r
-   MoveToEnd(AH, "TABLE DATA");\r
-   MoveToEnd(AH, "INDEX");\r
-   MoveToEnd(AH, "TRIGGER");\r
-   MoveToEnd(AH, "RULE");\r
-   MoveToEnd(AH, "ACL");\r
-    }\r
-\r
-    if (opts->tocSummary) {\r
-   PrintTOCSummary(AH, opts);\r
-    } else {\r
-   RestoreArchive(AH, opts);\r
-    }\r
-\r
-    CloseArchive(AH);\r
-\r
-    return 1;\r
-}\r
-\r
-static void usage(const char *progname)\r
-{\r
-#ifdef HAVE_GETOPT_LONG\r
-   fprintf(stderr,\r
-   "usage:  %s [options] [backup file]\n"\r
-       "  -a, --data-only             \t dump out only the data, no schema\n"\r
-       "  -c, --clean                 \t clean(drop) schema prior to create\n"\r
-       "  -f filename                 \t script output filename\n"\r
-       "  -F, --format {c|f}          \t specify backup file format\n"\r
-       "  -p, --function[=name]       \t dump functions or named function\n"\r
-       "  -i, --index[=name]          \t dump indexes or named index\n"\r
-       "  -l, --list                  \t dump summarized TOC for this file\n"\r
-       "  -o, --oid-order             \t dump in oid order\n"\r
-       "  -O, --orig-order            \t dump in original dump order\n"\r
-       "  -r, --rearrange             \t rearrange output to put indexes etc at end\n"\r
-       "  -s, --schema-only           \t dump out only the schema, no data\n"\r
-       "  -t [table], --table[=table] \t dump for this table only\n"\r
-       "  -T, --trigger[=name]        \t dump triggers or named trigger\n"\r
-       "  -u, --use-list filename     \t use specified TOC for ordering output from this file\n"\r
-       "  -v                          \t verbose\n"\r
-       "  -x, --no-acl                \t skip dumping of ACLs (grant/revoke)\n"\r
-       , progname);\r
-#else\r
-   fprintf(stderr,\r
-   "usage:  %s [options] [backup file]\n"\r
-       "  -a                          \t dump out only the data, no schema\n"\r
-       "  -c                          \t clean(drop) schema prior to create\n"\r
-       "  -f filename NOT IMPLEMENTED \t script output filename\n"\r
-       "  -F           {c|f}          \t specify backup file format\n"\r
-       "  -p name                     \t dump functions or named function\n"\r
-       "  -i name                     \t dump indexes or named index\n"\r
-       "  -l                          \t dump summarized TOC for this file\n"\r
-       "  -o                          \t dump in oid order\n"\r
-       "  -O                          \t dump in original dump order\n"\r
-       "  -r                          \t rearrange output to put indexes etc at end\n"\r
-       "  -s                          \t dump out only the schema, no data\n"\r
-       "  -t name                     \t dump for this table only\n"\r
-       "  -T name                     \t dump triggers or named trigger\n"\r
-       "  -u filename                 \t use specified TOC for ordering output from this file\n"\r
-       "  -v                          \t verbose\n"\r
-       "  -x                          \t skip dumping of ACLs (grant/revoke)\n"\r
-       , progname);\r
-#endif\r
-   fprintf(stderr,\r
-           "\nIf [backup file] is not supplied, then standard input "\r
-           "is used.\n");\r
-   fprintf(stderr, "\n");\r
-\r
-   exit(1);\r
-}\r
-\r
-static char* _cleanupName(char* name)\r
-{\r
-    int        i;\r
-\r
-    if (!name)\r
-   return NULL;\r
-\r
-    if (strlen(name) == 0)\r
-   return NULL;\r
-\r
-    name = strdup(name);\r
-\r
-    if (name[0] == '"')\r
-    {\r
-   strcpy(name, &name[1]);\r
-   if (*(name + strlen(name) - 1) == '"')\r
-       *(name + strlen(name) - 1) = '\0';\r
-    }\r
-    /* otherwise, convert table name to lowercase... */\r
-    else\r
-    {\r
-   for (i = 0; name[i]; i++)\r
-       if (isascii((unsigned char) name[i]) && isupper(name[i]))\r
-       name[i] = tolower(name[i]);\r
-    }\r
-    return name;\r
-}\r
-\r
+/*-------------------------------------------------------------------------
+ *
+ * pg_restore.c
+ * pg_restore is an utility extracting postgres database definitions
+ * from a backup archive created by pg_dump using the archiver 
+ * interface.
+ *
+ * pg_restore will read the backup archive and
+ * dump out a script that reproduces
+ * the schema of the database in terms of
+ *       user-defined types
+ *       user-defined functions
+ *       tables
+ *       indices
+ *       aggregates
+ *       operators
+ *       ACL - grant/revoke
+ *
+ * the output script is SQL that is understood by PostgreSQL
+ *
+ * Basic process in a restore operation is:
+ * 
+ *     Open the Archive and read the TOC.
+ *     Set flags in TOC entries, and *maybe* reorder them.
+ *     Generate script to stdout
+ *     Exit
+ *
+ * Copyright (c) 2000, Philip Warner
+ *      Rights are granted to use this software in any way so long
+ *      as this notice is not removed.
+ *
+ * The author is not responsible for loss or damages that may
+ * result from it's use.
+ *
+ *
+ * IDENTIFICATION
+ *
+ * Modifications - 28-Jun-2000 - [email protected]
+ *
+ * Initial version. Command processing taken from original pg_dump.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+
+/*
+#include "postgres.h"
+#include "access/htup.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_trigger.h"
+#include "libpq-fe.h"
+*/
+
+#include "pg_backup.h"
+
+#ifndef HAVE_STRDUP
+#include "strdup.h"
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include 
+#endif
+
+#ifdef HAVE_GETOPT_H 
+#include 
+#else
+#include 
+#endif
+
+/* Forward decls */
+static void usage(const char *progname);
+static char* _cleanupName(char* name);
+
+typedef struct option optType;
+
+#ifdef HAVE_GETOPT_H
+struct option cmdopts[] = {    
+               { "clean", 0, NULL, 'c' },
+               { "data-only", 0, NULL, 'a' },
+               { "dbname", 1, NULL, 'd' },
+               { "file", 1, NULL, 'f' },
+               { "format", 1, NULL, 'F' },
+               { "function", 2, NULL, 'P' },
+               { "host", 1, NULL, 'h' },
+               { "ignore-version", 0, NULL, 'i'},
+               { "index", 2, NULL, 'I'},
+               { "list", 0, NULL, 'l'},
+               { "no-acl", 0, NULL, 'x' },
+               { "port", 1, NULL, 'p' },
+               { "oid-order", 0, NULL, 'o'},
+               { "orig-order", 0, NULL, 'O' },
+               { "password", 0, NULL, 'u' },
+               { "rearrange", 0, NULL, 'r'},
+               { "schema-only", 0, NULL, 's' },
+               { "table", 2, NULL, 't'},
+               { "trigger", 2, NULL, 'T' },
+               { "use-list", 1, NULL, 'U'},
+               { "verbose", 0, NULL, 'v' },
+               { NULL, 0, NULL, 0}
+               };
+#endif
+
+int main(int argc, char **argv)
+{
+   RestoreOptions  *opts;
+   char        *progname;
+   int     c;
+   Archive*        AH;
+   char        *fileSpec = NULL;
+
+   opts = NewRestoreOptions();
+
+   progname = *argv;
+
+#ifdef HAVE_GETOPT_LONG
+   while ((c = getopt_long(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx", cmdopts, NULL)) != EOF)
+#else
+   while ((c = getopt(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx")) != -1)
+#endif
+   {
+       switch (c)
+       {
+           case 'a':           /* Dump data only */
+               opts->dataOnly = 1;
+               break;
+           case 'c':           /* clean (i.e., drop) schema prior to
+                                * create */
+               opts->dropSchema = 1;
+               break;
+           case 'd':
+               if (strlen(optarg) != 0)
+               {
+                   opts->dbname = strdup(optarg);
+                   opts->useDB = 1;
+               }
+               break;
+           case 'f':           /* output file name */
+               opts->filename = strdup(optarg);
+               break;
+           case 'F':
+               if (strlen(optarg) != 0) 
+                   opts->formatName = strdup(optarg);
+               break;
+           case 'h':
+               if (strlen(optarg) != 0)
+                   opts->pghost = strdup(optarg);
+               break;
+           case 'i':
+               opts->ignoreVersion = 1;
+               break;
+           case 'o':
+               opts->oidOrder = 1;
+               break;
+           case 'O':
+               opts->origOrder = 1;
+               break;
+           case 'p':
+               if (strlen(optarg) != 0)
+                   opts->pgport = strdup(optarg);
+               break;
+           case 'r':
+               opts->rearrange = 1;
+               break;
+           case 'P': /* Function */
+               opts->selTypes = 1;
+               opts->selFunction = 1;
+               opts->functionNames = _cleanupName(optarg);
+               break;
+           case 'I': /* Index */
+               opts->selTypes = 1;
+               opts->selIndex = 1;
+               opts->indexNames = _cleanupName(optarg);
+               break;
+           case 'T': /* Trigger */
+               opts->selTypes = 1;
+               opts->selTrigger = 1;
+               opts->triggerNames = _cleanupName(optarg);
+               break;
+           case 's':           /* dump schema only */
+               opts->schemaOnly = 1;
+               break;
+           case 't':           /* Dump data for this table only */
+               opts->selTypes = 1;
+               opts->selTable = 1;
+               opts->tableNames = _cleanupName(optarg);
+               break;
+           case 'l':           /* Dump the TOC summary */
+               opts->tocSummary = 1;
+               break;
+
+           case 'u':
+               opts->requirePassword = 1;
+               break;
+
+           case 'U':           /* input TOC summary file name */
+               opts->tocFile = strdup(optarg);
+               break;
+
+           case 'v':           /* verbose */
+               opts->verbose = 1;
+               break;
+           case 'x':           /* skip ACL dump */
+               opts->aclsSkip = 1;
+               break;
+           default:
+               usage(progname);
+               break;
+       }
+   }
+
+   if (optind < argc) {
+       fileSpec = argv[optind];
+   } else {
+       fileSpec = NULL;
+   }
+
+    if (opts->formatName) { 
+
+   switch (opts->formatName[0]) {
+
+       case 'c':
+       case 'C':
+           opts->format = archCustom;
+           break;
+
+       case 'f':
+       case 'F':
+           opts->format = archFiles;
+           break;
+
+       case 't':
+       case 'T':
+           opts->format = archTar;
+           break;
+
+       default:
+           fprintf(stderr, "%s: Unknown archive format '%s', please specify 't' or 'c'\n",
+                       progname, opts->formatName);
+           exit (1);
+   }
+    }
+
+    AH = OpenArchive(fileSpec, opts->format);
+
+   /* Let the archiver know how noisy to be */
+   AH->verbose = opts->verbose;
+
+    if (opts->tocFile)
+       SortTocFromFile(AH, opts);
+
+    if (opts->oidOrder)
+       SortTocByOID(AH);
+    else if (opts->origOrder)
+       SortTocByID(AH);
+
+    if (opts->rearrange) {
+       MoveToStart(AH, "");
+       MoveToEnd(AH, "TABLE DATA");
+       MoveToEnd(AH, "BLOBS");
+       MoveToEnd(AH, "INDEX");
+       MoveToEnd(AH, "TRIGGER");
+       MoveToEnd(AH, "RULE");
+       MoveToEnd(AH, "ACL");
+    }
+
+    if (opts->tocSummary) {
+       PrintTOCSummary(AH, opts);
+    } else {
+       RestoreArchive(AH, opts);
+    }
+
+    CloseArchive(AH);
+
+    return 1;
+}
+
+static void usage(const char *progname)
+{
+#ifdef HAVE_GETOPT_LONG
+   fprintf(stderr,
+   "usage:  %s [options] [backup file]\n"
+       "  -a, --data-only             \t dump out only the data, no schema\n"
+       "  -d, --dbname          \t specify database name\n"
+       "  -c, --clean                 \t clean(drop) schema prior to create\n"
+       "  -f filename                 \t script output filename\n"
+       "  -F, --format {c|f}          \t specify backup file format\n"
+       "  -h, --host        \t server host name\n"
+       "  -i, --index[=name]          \t dump indexes or named index\n"
+       "  -l, --list                  \t dump summarized TOC for this file\n"
+       "  -o, --oid-order             \t dump in oid order\n"
+       "  -O, --orig-order            \t dump in original dump order\n"
+       "  -p, --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"
+       "  -s, --schema-only           \t dump out only the schema, no data\n"
+       "  -t [table], --table[=table] \t dump for this table only\n"
+       "  -T, --trigger[=name]        \t dump triggers or named trigger\n"
+       "  -u, --password              \t use password authentication\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"
+       , progname);
+
+#else
+   fprintf(stderr,
+   "usage:  %s [options] [backup file]\n"
+       "  -a                          \t dump out only the data, no schema\n"
+       "  -d,                   \t specify database name\n"
+       "  -c                          \t clean(drop) schema prior to create\n"
+       "  -f filename NOT IMPLEMENTED \t script output filename\n"
+       "  -F           {c|f}          \t specify backup file format\n"
+       "  -h,               \t server host name\n"
+       "  -i name                     \t dump indexes or named index\n"
+       "  -l                          \t dump summarized TOC for this file\n"
+       "  -o                          \t dump in oid order\n"
+       "  -O                          \t dump in original dump order\n"
+       "  -p                    \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"
+       "  -s                          \t dump out only the schema, no data\n"
+       "  -t name                     \t dump for this table only\n"
+       "  -T name                     \t dump triggers or named trigger\n"
+       "  -u                          \t use password authentication\n"
+       "  -U filename                 \t use specified TOC for ordering output from this file\n"
+       "  -v                          \t verbose\n"
+       "  -x                          \t skip dumping of ACLs (grant/revoke)\n"
+       , progname);
+#endif
+   fprintf(stderr,
+           "\nIf [backup file] is not supplied, then standard input "
+           "is used.\n");
+   fprintf(stderr, "\n");
+
+   exit(1);
+}
+
+static char* _cleanupName(char* name)
+{
+    int        i;
+
+    if (!name)
+   return NULL;
+
+    if (strlen(name) == 0)
+   return NULL;
+
+    name = strdup(name);
+
+    if (name[0] == '"')
+    {
+   strcpy(name, &name[1]);
+   if (*(name + strlen(name) - 1) == '"')
+       *(name + strlen(name) - 1) = '\0';
+    }
+    /* otherwise, convert table name to lowercase... */
+    else
+    {
+   for (i = 0; name[i]; i++)
+       if (isascii((unsigned char) name[i]) && isupper(name[i]))
+       name[i] = tolower(name[i]);
+    }
+    return name;
+}
+