1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-25 21:42:33 +03:00

- Support for BLOB output from pg_dump and input via pg_restore

- Support for direct DB connection in pg_restore
- Fixes in support for --insert flag
- pg_dump now outputs in modified OID order
- various other bug fixes
This commit is contained in:
Philip Warner 2000-07-21 11:40:08 +00:00
parent 0143d391c6
commit e8f69be054
9 changed files with 2922 additions and 1772 deletions

View File

@ -4,7 +4,7 @@
# #
# Copyright (c) 1994, Regents of the University of California # Copyright (c) 1994, Regents of the University of California
# #
# $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.19 2000/07/04 19:52:00 petere Exp $ # $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.20 2000/07/21 11:40:08 pjw Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -12,8 +12,8 @@ subdir = src/bin/pg_dump
top_builddir = ../../.. top_builddir = ../../..
include ../../Makefile.global include ../../Makefile.global
OBJS= pg_backup_archiver.o pg_backup_custom.o pg_backup_files.o \ OBJS= pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o pg_backup_files.o \
pg_backup_plain_text.o $(STRDUP) pg_backup_null.o pg_backup_tar.o $(STRDUP)
CFLAGS+= -I$(LIBPQDIR) CFLAGS+= -I$(LIBPQDIR)
LIBS+= -lz LIBS+= -lz

View File

@ -1,17 +1,23 @@
Notes on pg_dump Notes on pg_dump
================ ================
pg_dump, by default, still outputs text files. 1. pg_dump, by default, still outputs text files.
pg_dumpall forces all pg_dump output to be text, since it also outputs text into the same output stream. 2. pg_dumpall forces all pg_dump output to be text, since it also outputs text into the same output stream.
The plain text output format can not be used as input into pg_restore. 3. The plain text output format can not be used as input into pg_restore.
4. pg_dump now dumps the items in a modified OID order to try to improve relaibility of default restores.
To dump a database into the next custom format, type: To dump a database into the next custom format, type:
pg_dump <db-name> -Fc > <backup-file> pg_dump <db-name> -Fc > <backup-file>
or, in TAR format
pg_dump <db-name> -Ft > <backup-file>
To restore, try To restore, try
To list contents: To list contents:
@ -53,7 +59,37 @@ or, simply:
pg_restore backup.bck --use=toc.lis | psql newdbname pg_restore backup.bck --use=toc.lis | psql newdbname
Philip Warner, 3-Jul-2000 BLOBs
=====
To dump blobs you must use the custom archive format (-Fc) or TAR format (-Ft), and specify the
--blobs qualifier to the pg_dump command.
To restore blobs you must use a direct database connection (--db=db-to-restore-to).
eg.
pg_dump --blob -Fc db-to-backup -f backup.bck
pg_restore backup.bck --db=db-to-restore-into
TAR
===
The TAR archive that pg_dump creates currently has a blank username & group for the files,
but should be otherwise valid. It also includes a 'restore.sql' script which is there for
the benefit of humans. It is never used by pg_restore.
Note: the TAR format archive can only be used as input into pg_restore if it is in TAR form.
(ie. you should not extract the files then expect pg_restore to work).
You can extract, edit, and tar the files again, and it should work, but the 'toc'
file should go at the start, the data files be in the order they are used, and
the BLOB files at the end.
Philip Warner, 16-Jul-2000
pjw@rhyme.com.au pjw@rhyme.com.au

View File

@ -1,125 +1,154 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* pg_backup.h * pg_backup.h
* *
* Public interface to the pg_dump archiver routines. * Public interface to the pg_dump archiver routines.
* *
* See the headers to pg_restore for more details. * See the headers to pg_restore for more details.
* *
* Copyright (c) 2000, Philip Warner * Copyright (c) 2000, Philip Warner
* Rights are granted to use this software in any way so long * Rights are granted to use this software in any way so long
* as this notice is not removed. * as this notice is not removed.
* *
* The author is not responsible for loss or damages that may * The author is not responsible for loss or damages that may
* result from it's use. * result from it's use.
* *
* *
* IDENTIFICATION * IDENTIFICATION
* *
* Modifications - 28-Jun-2000 - pjw@rhyme.com.au * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
* *
* Initial version. * Initial version.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef PG_BACKUP__ #ifndef PG_BACKUP__
#include "config.h" #include "config.h"
#include "c.h" #include "c.h"
#define PG_BACKUP__ #define PG_BACKUP__
typedef enum _archiveFormat { #include "postgres.h"
archUnknown = 0, #include "libpq-fe.h"
archCustom = 1,
archFiles = 2, typedef enum _archiveFormat {
archTar = 3, archUnknown = 0,
archPlainText = 4 archCustom = 1,
} ArchiveFormat; archFiles = 2,
archTar = 3,
/* archNull = 4
* We may want to have so user-readbale data, but in the mean } ArchiveFormat;
* time this gives us some abstraction and type checking.
*/ /*
typedef struct _Archive { * We may want to have so user-readbale data, but in the mean
/* Nothing here */ * time this gives us some abstraction and type checking.
} Archive; */
typedef struct _Archive {
typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg); int verbose;
/* The rest is private */
typedef struct _restoreOptions { } Archive;
int dataOnly;
int dropSchema; typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);
char *filename;
int schemaOnly; typedef struct _restoreOptions {
int verbose; int dataOnly;
int aclsSkip; int dropSchema;
int tocSummary; char *filename;
char *tocFile; int schemaOnly;
int oidOrder; int verbose;
int origOrder; int aclsSkip;
int rearrange; int tocSummary;
int format; char *tocFile;
char *formatName; int oidOrder;
int origOrder;
int selTypes; int rearrange;
int selIndex; int format;
int selFunction; char *formatName;
int selTrigger;
int selTable; int selTypes;
char *indexNames; int selIndex;
char *functionNames; int selFunction;
char *tableNames; int selTrigger;
char *triggerNames; int selTable;
char *indexNames;
int *idWanted; char *functionNames;
int limitToList; char *tableNames;
int compression; char *triggerNames;
} RestoreOptions; int useDB;
char *dbname;
/* char *pgport;
* Main archiver interface. char *pghost;
*/ int ignoreVersion;
int requirePassword;
/* Called to add a TOC entry */
extern void ArchiveEntry(Archive* AH, const char* oid, const char* name, int *idWanted;
const char* desc, const char* (deps[]), const char* defn, int limitToList;
const char* dropStmt, const char* owner, int compression;
DataDumperPtr dumpFn, void* dumpArg);
} RestoreOptions;
/* Called to write *data* to the archive */
extern int WriteData(Archive* AH, const void* data, int dLen); /*
* Main archiver interface.
extern void CloseArchive(Archive* AH); */
extern void RestoreArchive(Archive* AH, RestoreOptions *ropt); extern void exit_horribly(Archive *AH, const char *fmt, ...);
/* Open an existing archive */ /* Lets the archibe know we have a DB connection to shutdown if it dies */
extern Archive* OpenArchive(const char* FileSpec, ArchiveFormat fmt);
PGconn* ConnectDatabase(Archive *AH,
/* Create a new archive */ const char* dbname,
extern Archive* CreateArchive(const char* FileSpec, ArchiveFormat fmt, int compression); const char* pghost,
const char* pgport,
/* The --list option */ const int reqPwd,
extern void PrintTOCSummary(Archive* AH, RestoreOptions *ropt); const int ignoreVersion);
extern RestoreOptions* NewRestoreOptions(void);
/* Called to add a TOC entry */
/* Rearrange TOC entries */ extern void ArchiveEntry(Archive* AH, const char* oid, const char* name,
extern void MoveToStart(Archive* AH, char *oType); const char* desc, const char* (deps[]), const char* defn,
extern void MoveToEnd(Archive* AH, char *oType); const char* dropStmt, const char* copyStmt, const char* owner,
extern void SortTocByOID(Archive* AH); DataDumperPtr dumpFn, void* dumpArg);
extern void SortTocByID(Archive* AH);
extern void SortTocFromFile(Archive* AH, RestoreOptions *ropt); /* Called to write *data* to the archive */
extern int WriteData(Archive* AH, const void* data, int dLen);
/* Convenience functions used only when writing DATA */
extern int archputs(const char *s, Archive* AH); //extern int StartBlobs(Archive* AH);
extern int archputc(const char c, Archive* AH); //extern int EndBlobs(Archive* AH);
extern int archprintf(Archive* AH, const char *fmt, ...); extern int StartBlob(Archive* AH, int oid);
extern int EndBlob(Archive* AH, int oid);
#endif
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

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,10 @@
#define __PG_BACKUP_ARCHIVE__ #define __PG_BACKUP_ARCHIVE__
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include "postgres.h"
#include "pqexpbuffer.h"
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
#include <zlib.h> #include <zlib.h>
@ -51,15 +55,23 @@ typedef z_stream *z_streamp;
#endif #endif
#include "pg_backup.h" #include "pg_backup.h"
#include "libpq-fe.h"
#define K_VERS_MAJOR 1 #define K_VERS_MAJOR 1
#define K_VERS_MINOR 2 #define K_VERS_MINOR 4
#define K_VERS_REV 2 #define K_VERS_REV 3
/* Data block types */
#define BLK_DATA 1
#define BLK_BLOB 2
#define BLK_BLOBS 3
/* Some important version numbers (checked in code) */ /* Some important version numbers (checked in code) */
#define K_VERS_1_0 (( (1 * 256 + 0) * 256 + 0) * 256 + 0) #define K_VERS_1_0 (( (1 * 256 + 0) * 256 + 0) * 256 + 0)
#define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) #define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) /* Allow No ZLIB */
#define K_VERS_MAX (( (1 * 256 + 2) * 256 + 255) * 256 + 0) #define K_VERS_1_3 (( (1 * 256 + 3) * 256 + 0) * 256 + 0) /* BLOBs */
#define K_VERS_1_4 (( (1 * 256 + 4) * 256 + 0) * 256 + 0) /* Date & name in header */
#define K_VERS_MAX (( (1 * 256 + 4) * 256 + 255) * 256 + 0)
struct _archiveHandle; struct _archiveHandle;
struct _tocEntry; struct _tocEntry;
@ -72,10 +84,15 @@ typedef void (*StartDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef int (*WriteDataPtr) (struct _archiveHandle* AH, const void* data, int dLen); typedef int (*WriteDataPtr) (struct _archiveHandle* AH, const void* data, int dLen);
typedef void (*EndDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te); typedef void (*EndDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef 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 (*ReadBytePtr) (struct _archiveHandle* AH);
typedef int (*WriteBufPtr) (struct _archiveHandle* AH, const void* c, int len); typedef int (*WriteBufPtr) (struct _archiveHandle* AH, const void* c, int len);
typedef int (*ReadBufPtr) (struct _archiveHandle* AH, void* buf, int len); typedef int (*ReadBufPtr) (struct _archiveHandle* AH, void* buf, int len);
typedef void (*SaveArchivePtr) (struct _archiveHandle* AH); typedef void (*SaveArchivePtr) (struct _archiveHandle* AH);
typedef void (*WriteExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* te); typedef void (*WriteExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
typedef void (*ReadExtraTocPtr) (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, typedef void (*PrintTocDataPtr) (struct _archiveHandle* AH, struct _tocEntry* te,
RestoreOptions *ropt); RestoreOptions *ropt);
typedef int (*CustomOutPtr) (struct _archiveHandle* AH, const void* buf, int len);
typedef int (*TocSortCompareFn) (const void* te1, const void *te2); typedef int (*TocSortCompareFn) (const void* te1, const void *te2);
typedef enum _archiveMode { typedef enum _archiveMode {
@ -95,16 +114,44 @@ typedef struct _outputContext {
int gzOut; int gzOut;
} OutputContext; } OutputContext;
typedef enum {
SQL_SCAN = 0,
SQL_IN_SQL_COMMENT,
SQL_IN_EXT_COMMENT,
SQL_IN_QUOTE} sqlparseState;
typedef struct {
int backSlash;
sqlparseState state;
char lastChar;
char quoteChar;
} sqlparseInfo;
typedef struct _archiveHandle { typedef struct _archiveHandle {
Archive public; /* Public part of archive */
char vmaj; /* Version of file */ char vmaj; /* Version of file */
char vmin; char vmin;
char vrev; char vrev;
int version; /* Conveniently formatted version */ int version; /* Conveniently formatted version */
int debugLevel; /* Not used. Intended for logging */
int intSize; /* Size of an integer in the archive */ int intSize; /* Size of an integer in the archive */
ArchiveFormat format; /* Archive format */ ArchiveFormat format; /* Archive format */
sqlparseInfo sqlparse;
PQExpBuffer sqlBuf;
time_t createDate; /* Date archive created */
/*
* Fields used when discovering header.
* A format can always get the previous read bytes from here...
*/
int readHeader; /* Used if file header has been read already */ int readHeader; /* Used if file header has been read already */
char *lookahead; /* Buffer used when reading header to discover format */
int lookaheadSize; /* Size of allocated buffer */
int lookaheadLen; /* Length of data in lookahead */
int lookaheadPos; /* Current read position in lookahead buffer */
ArchiveEntryPtr ArchiveEntryPtr; /* Called for each metadata object */ ArchiveEntryPtr ArchiveEntryPtr; /* Called for each metadata object */
StartDataPtr StartDataPtr; /* Called when table data is about to be dumped */ StartDataPtr StartDataPtr; /* Called when table data is about to be dumped */
@ -121,11 +168,33 @@ typedef struct _archiveHandle {
PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */ PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */
PrintTocDataPtr PrintTocDataPtr; PrintTocDataPtr PrintTocDataPtr;
int lastID; /* Last internal ID for a TOC entry */ StartBlobsPtr StartBlobsPtr;
char* fSpec; /* Archive File Spec */ EndBlobsPtr EndBlobsPtr;
FILE *FH; /* General purpose file handle */ StartBlobPtr StartBlobPtr;
void *OF; EndBlobPtr EndBlobPtr;
int gzOut; /* Output file */
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 */ struct _tocEntry* toc; /* List of TOC entries */
int tocCount; /* Number of TOC entries */ int tocCount; /* Number of TOC entries */
@ -135,6 +204,7 @@ typedef struct _archiveHandle {
ArchiveMode mode; /* File mode - r or w */ ArchiveMode mode; /* File mode - r or w */
void* formatData; /* Header data specific to file format */ void* formatData; /* Header data specific to file format */
RestoreOptions *ropt; /* Used to check restore options in ahwrite etc */
} ArchiveHandle; } ArchiveHandle;
typedef struct _tocEntry { typedef struct _tocEntry {
@ -148,6 +218,7 @@ typedef struct _tocEntry {
char* desc; char* desc;
char* defn; char* defn;
char* dropStmt; char* dropStmt;
char* copyStmt;
char* owner; char* owner;
char** depOid; char** depOid;
int printed; /* Indicates if entry defn has been dumped */ int printed; /* Indicates if entry defn has been dumped */
@ -159,7 +230,8 @@ typedef struct _tocEntry {
} TocEntry; } TocEntry;
extern void die_horribly(const char *fmt, ...); /* Used everywhere */
extern void die_horribly(ArchiveHandle *AH, const char *fmt, ...);
extern void WriteTOC(ArchiveHandle* AH); extern void WriteTOC(ArchiveHandle* AH);
extern void ReadTOC(ArchiveHandle* AH); extern void ReadTOC(ArchiveHandle* AH);
@ -175,19 +247,27 @@ extern int TocIDRequired(ArchiveHandle* AH, int id, RestoreOptions *ropt);
* Mandatory routines for each supported format * Mandatory routines for each supported format
*/ */
extern int WriteInt(ArchiveHandle* AH, int i); extern int WriteInt(ArchiveHandle* AH, int i);
extern int ReadInt(ArchiveHandle* AH); extern int ReadInt(ArchiveHandle* AH);
extern char* ReadStr(ArchiveHandle* AH); extern char* ReadStr(ArchiveHandle* AH);
extern int WriteStr(ArchiveHandle* AH, char* s); extern int WriteStr(ArchiveHandle* AH, char* s);
extern void InitArchiveFmt_Custom(ArchiveHandle* AH); extern void StartRestoreBlob(ArchiveHandle* AH, int oid);
extern void InitArchiveFmt_Files(ArchiveHandle* AH); extern void EndRestoreBlob(ArchiveHandle* AH, int oid);
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 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 ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle* AH);
int ahprintf(ArchiveHandle* AH, const char *fmt, ...); int ahprintf(ArchiveHandle* AH, const char *fmt, ...);
void ahlog(ArchiveHandle* AH, int level, const char *fmt, ...);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,303 +1,483 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* pg_backup_files.c * pg_backup_files.c
* *
* This file is copied from the 'custom' format file, but dumps data into * This file is copied from the 'custom' format file, but dumps data into
* separate files, and the TOC into the 'main' file. * separate files, and the TOC into the 'main' file.
* *
* IT IS FOR DEMONSTRATION PURPOSES ONLY. * IT IS FOR DEMONSTRATION PURPOSES ONLY.
* *
* (and could probably be used as a basis for writing a tar file) * (and could probably be used as a basis for writing a tar file)
* *
* See the headers to pg_restore for more details. * See the headers to pg_restore for more details.
* *
* Copyright (c) 2000, Philip Warner * Copyright (c) 2000, Philip Warner
* Rights are granted to use this software in any way so long * Rights are granted to use this software in any way so long
* as this notice is not removed. * as this notice is not removed.
* *
* The author is not responsible for loss or damages that may * The author is not responsible for loss or damages that may
* result from it's use. * result from it's use.
* *
* *
* IDENTIFICATION * IDENTIFICATION
* *
* Modifications - 28-Jun-2000 - pjw@rhyme.com.au * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
* *
* Initial version. * Initial version.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "pg_backup.h" #include "pg_backup.h"
#include "pg_backup_archiver.h" #include "pg_backup_archiver.h"
static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te); static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);
static void _StartData(ArchiveHandle* AH, TocEntry* te); static void _StartData(ArchiveHandle* AH, TocEntry* te);
static int _WriteData(ArchiveHandle* AH, const void* data, int dLen); static int _WriteData(ArchiveHandle* AH, const void* data, int dLen);
static void _EndData(ArchiveHandle* AH, TocEntry* te); static void _EndData(ArchiveHandle* AH, TocEntry* te);
static int _WriteByte(ArchiveHandle* AH, const int i); static int _WriteByte(ArchiveHandle* AH, const int i);
static int _ReadByte(ArchiveHandle* ); static int _ReadByte(ArchiveHandle* );
static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len); static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len);
static int _ReadBuf(ArchiveHandle* AH, void* buf, int len); static int _ReadBuf(ArchiveHandle* AH, void* buf, int len);
static void _CloseArchive(ArchiveHandle* AH); static void _CloseArchive(ArchiveHandle* AH);
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt); static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te); static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te); static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te); static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);
static void _StartBlobs(ArchiveHandle* AH, TocEntry* te);
typedef struct { static void _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid);
int hasSeek; static void _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid);
int filePos; static void _EndBlobs(ArchiveHandle* AH, TocEntry* te);
} lclContext;
#define K_STD_BUF_SIZE 1024
typedef struct {
#ifdef HAVE_LIBZ typedef struct {
gzFile *FH; int hasSeek;
#else int filePos;
FILE *FH; FILE *blobToc;
#endif } lclContext;
char *filename;
} lclTocEntry; typedef struct {
#ifdef HAVE_LIBZ
/* gzFile *FH;
* Initializer #else
*/ FILE *FH;
void InitArchiveFmt_Files(ArchiveHandle* AH) #endif
{ char *filename;
lclContext* ctx; } lclTocEntry;
/* Assuming static functions, this can be copied for each format. */ static char* progname = "Archiver(files)";
AH->ArchiveEntryPtr = _ArchiveEntry; static void _LoadBlobs(ArchiveHandle* AH, RestoreOptions *ropt);
AH->StartDataPtr = _StartData; static void _getBlobTocEntry(ArchiveHandle* AH, int *oid, char *fname);
AH->WriteDataPtr = _WriteData;
AH->EndDataPtr = _EndData; /*
AH->WriteBytePtr = _WriteByte; * Initializer
AH->ReadBytePtr = _ReadByte; */
AH->WriteBufPtr = _WriteBuf; void InitArchiveFmt_Files(ArchiveHandle* AH)
AH->ReadBufPtr = _ReadBuf; {
AH->ClosePtr = _CloseArchive; lclContext* ctx;
AH->PrintTocDataPtr = _PrintTocData;
AH->ReadExtraTocPtr = _ReadExtraToc; /* Assuming static functions, this can be copied for each format. */
AH->WriteExtraTocPtr = _WriteExtraToc; AH->ArchiveEntryPtr = _ArchiveEntry;
AH->PrintExtraTocPtr = _PrintExtraToc; AH->StartDataPtr = _StartData;
AH->WriteDataPtr = _WriteData;
/* AH->EndDataPtr = _EndData;
* Set up some special context used in compressing data. AH->WriteBytePtr = _WriteByte;
*/ AH->ReadBytePtr = _ReadByte;
ctx = (lclContext*)malloc(sizeof(lclContext)); AH->WriteBufPtr = _WriteBuf;
AH->formatData = (void*)ctx; AH->ReadBufPtr = _ReadBuf;
ctx->filePos = 0; AH->ClosePtr = _CloseArchive;
AH->PrintTocDataPtr = _PrintTocData;
/* AH->ReadExtraTocPtr = _ReadExtraToc;
* Now open the TOC file AH->WriteExtraTocPtr = _WriteExtraToc;
*/ AH->PrintExtraTocPtr = _PrintExtraToc;
if (AH->mode == archModeWrite) {
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { AH->StartBlobsPtr = _StartBlobs;
AH->FH = fopen(AH->fSpec, PG_BINARY_W); AH->StartBlobPtr = _StartBlob;
} else { AH->EndBlobPtr = _EndBlob;
AH->FH = stdout; AH->EndBlobsPtr = _EndBlobs;
}
ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0); /*
* Set up some special context used in compressing data.
if (AH->compression < 0 || AH->compression > 9) { */
AH->compression = Z_DEFAULT_COMPRESSION; ctx = (lclContext*)malloc(sizeof(lclContext));
} AH->formatData = (void*)ctx;
ctx->filePos = 0;
} else { /*
if (AH->fSpec && strcmp(AH->fSpec,"") != 0) { * Now open the TOC file
AH->FH = fopen(AH->fSpec, PG_BINARY_R); */
} else { if (AH->mode == archModeWrite) {
AH->FH = stdin;
} fprintf(stderr, "\n*************************************************************\n"
ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0); "* WARNING: This format is for demonstration purposes. It is *\n"
"* not intended for general use. Files will be dumped *\n"
ReadHead(AH); "* into the current working directory. *\n"
ReadToc(AH); "***************************************************************\n\n");
fclose(AH->FH); /* Nothing else in the file... */
} if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
AH->FH = fopen(AH->fSpec, PG_BINARY_W);
} } else {
AH->FH = stdout;
/* }
* - Start a new TOC entry ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
* Setup the output file name.
*/ if (AH->compression < 0 || AH->compression > 9) {
static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) AH->compression = Z_DEFAULT_COMPRESSION;
{ }
lclTocEntry* ctx;
char fn[1024];
} else { /* Read Mode */
ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
if (te->dataDumper) { if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
#ifdef HAVE_LIBZ AH->FH = fopen(AH->fSpec, PG_BINARY_R);
if (AH->compression == 0) { } else {
sprintf(fn, "%d.dat", te->id); AH->FH = stdin;
} else { }
sprintf(fn, "%d.dat.gz", te->id); ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
}
#else ReadHead(AH);
sprintf(fn, "%d.dat", te->id); ReadToc(AH);
#endif fclose(AH->FH); /* Nothing else in the file... */
ctx->filename = strdup(fn); }
} else {
ctx->filename = NULL; }
ctx->FH = NULL;
} /*
te->formatData = (void*)ctx; * - Start a new TOC entry
} * Setup the output file name.
*/
static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te) static void _ArchiveEntry(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* ctx = (lclTocEntry*)te->formatData; lclTocEntry* ctx;
char fn[K_STD_BUF_SIZE];
if (ctx->filename) {
WriteStr(AH, ctx->filename); ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
} else { if (te->dataDumper) {
WriteStr(AH, ""); #ifdef HAVE_LIBZ
} if (AH->compression == 0) {
} sprintf(fn, "%d.dat", te->id);
} else {
static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te) sprintf(fn, "%d.dat.gz", te->id);
{ }
lclTocEntry* ctx = (lclTocEntry*)te->formatData; #else
sprintf(fn, "%d.dat", te->id);
if (ctx == NULL) { #endif
ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry)); ctx->filename = strdup(fn);
te->formatData = (void*)ctx; } else {
} ctx->filename = NULL;
ctx->FH = NULL;
ctx->filename = ReadStr(AH); }
if (strlen(ctx->filename) == 0) { te->formatData = (void*)ctx;
free(ctx->filename); }
ctx->filename = NULL;
} static void _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)
ctx->FH = NULL; {
} lclTocEntry* ctx = (lclTocEntry*)te->formatData;
static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te) if (ctx->filename) {
{ WriteStr(AH, ctx->filename);
lclTocEntry* ctx = (lclTocEntry*)te->formatData; } else {
WriteStr(AH, "");
ahprintf(AH, "-- File: %s\n", ctx->filename); }
} }
static void _StartData(ArchiveHandle* AH, TocEntry* te) static void _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)
{ {
lclTocEntry* tctx = (lclTocEntry*)te->formatData; lclTocEntry* ctx = (lclTocEntry*)te->formatData;
char fmode[10];
if (ctx == NULL) {
sprintf(fmode, "wb%d", AH->compression); ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
te->formatData = (void*)ctx;
#ifdef HAVE_LIBZ }
tctx->FH = gzopen(tctx->filename, fmode);
#else ctx->filename = ReadStr(AH);
tctx->FH = fopen(tctx->filename, PG_BINARY_W); if (strlen(ctx->filename) == 0) {
#endif free(ctx->filename);
} ctx->filename = NULL;
}
static int _WriteData(ArchiveHandle* AH, const void* data, int dLen) ctx->FH = NULL;
{ }
lclTocEntry* tctx = (lclTocEntry*)AH->currToc->formatData;
static void _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)
GZWRITE((void*)data, 1, dLen, tctx->FH); {
lclTocEntry* ctx = (lclTocEntry*)te->formatData;
return dLen;
} ahprintf(AH, "-- File: %s\n", ctx->filename);
}
static void _EndData(ArchiveHandle* AH, TocEntry* te)
{ static void _StartData(ArchiveHandle* AH, TocEntry* te)
lclTocEntry* tctx = (lclTocEntry*) te->formatData; {
lclTocEntry* tctx = (lclTocEntry*)te->formatData;
/* Close the file */ char fmode[10];
GZCLOSE(tctx->FH);
tctx->FH = NULL; sprintf(fmode, "wb%d", AH->compression);
}
#ifdef HAVE_LIBZ
/* tctx->FH = gzopen(tctx->filename, fmode);
* Print data for a given TOC entry #else
*/ tctx->FH = fopen(tctx->filename, PG_BINARY_W);
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt) #endif
{ }
lclTocEntry* tctx = (lclTocEntry*) te->formatData;
char buf[4096]; static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
int cnt; {
lclTocEntry* tctx = (lclTocEntry*)AH->currToc->formatData;
if (!tctx->filename)
return; GZWRITE((void*)data, 1, dLen, tctx->FH);
#ifdef HAVE_LIBZ return dLen;
AH->FH = gzopen(tctx->filename,"rb"); }
#else
AH->FH = fopen(tctx->filename,PG_BINARY_R); static void _EndData(ArchiveHandle* AH, TocEntry* te)
#endif {
lclTocEntry* tctx = (lclTocEntry*) te->formatData;
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",
te->id, te->oid, te->desc, te->name); /* Close the file */
GZCLOSE(tctx->FH);
while ( (cnt = GZREAD(buf, 1, 4096, AH->FH)) > 0) { tctx->FH = NULL;
ahwrite(buf, 1, cnt, AH); }
}
/*
GZCLOSE(AH->FH); * Print data for a given file
*/
ahprintf(AH, "\n\n"); static void _PrintFileData(ArchiveHandle* AH, char *filename, RestoreOptions *ropt)
} {
char buf[4096];
static int _WriteByte(ArchiveHandle* AH, const int i) int cnt;
{
lclContext* ctx = (lclContext*)AH->formatData; if (!filename)
int res; return;
res = fputc(i, AH->FH); #ifdef HAVE_LIBZ
if (res != EOF) { AH->FH = gzopen(filename,"rb");
ctx->filePos += 1; #else
} AH->FH = fopen(filename,PG_BINARY_R);
return res; #endif
}
while ( (cnt = GZREAD(buf, 1, 4095, AH->FH)) > 0) {
static int _ReadByte(ArchiveHandle* AH) buf[cnt] = '\0';
{ ahwrite(buf, 1, cnt, AH);
lclContext* ctx = (lclContext*)AH->formatData; }
int res;
GZCLOSE(AH->FH);
res = fgetc(AH->FH); }
if (res != EOF) {
ctx->filePos += 1;
} /*
return res; * Print data for a given TOC entry
} */
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
static int _WriteBuf(ArchiveHandle* AH, const void* buf, int len) {
{ lclTocEntry* tctx = (lclTocEntry*) te->formatData;
lclContext* ctx = (lclContext*)AH->formatData;
int res; if (!tctx->filename)
res = fwrite(buf, 1, len, AH->FH); return;
ctx->filePos += res;
return res; if (strcmp(te->desc, "BLOBS") == 0)
} _LoadBlobs(AH, ropt);
else
static int _ReadBuf(ArchiveHandle* AH, void* buf, int len) {
{ _PrintFileData(AH, tctx->filename, ropt);
lclContext* ctx = (lclContext*)AH->formatData; }
int res; }
res = fread(buf, 1, len, AH->FH);
ctx->filePos += res; static void _getBlobTocEntry(ArchiveHandle* AH, int *oid, char fname[K_STD_BUF_SIZE])
return res; {
} lclContext* ctx = (lclContext*)AH->formatData;
char blobTe[K_STD_BUF_SIZE];
static void _CloseArchive(ArchiveHandle* AH) int fpos;
{ int eos;
if (AH->mode == archModeWrite) {
WriteHead(AH); if (fgets(&blobTe[0], K_STD_BUF_SIZE - 1, ctx->blobToc) != NULL)
WriteToc(AH); {
fclose(AH->FH); *oid = atoi(blobTe);
WriteDataChunks(AH);
} fpos = strcspn(blobTe, " ");
AH->FH = NULL; 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);
}

View File

@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.159 2000/07/17 03:05:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.160 2000/07/21 11:40:08 pjw Exp $
* *
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
* *
@ -61,12 +61,19 @@
* - Added a -Z option for compression level on compressed formats * - Added a -Z option for compression level on compressed formats
* - Restored '-f' in usage output * - Restored '-f' in usage output
* *
*------------------------------------------------------------------------- *
* Modifications - 17-Jul-2000 - Philip Warner pjw@rhyme.com.au
* - Support for BLOB output.
* - Sort archive by OID, put some items at end (out of OID order)
*
*-------------------------------------------------------------------------
*/ */
#include <unistd.h> /* for getopt() */ #include <unistd.h> /* for getopt() */
#include <ctype.h> #include <ctype.h>
#include "pg_backup.h"
#include "postgres.h" #include "postgres.h"
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
@ -84,6 +91,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "libpq-fe.h" #include "libpq-fe.h"
#include <libpq/libpq-fs.h>
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#include "strdup.h" #include "strdup.h"
#endif #endif
@ -109,6 +117,9 @@ static void setMaxOid(Archive *fout);
static void AddAcl(char *aclbuf, const char *keyword); static void AddAcl(char *aclbuf, const char *keyword);
static char *GetPrivileges(const char *s); static char *GetPrivileges(const char *s);
static int dumpBlobs(Archive *AH, char*, void*);
extern char *optarg; extern char *optarg;
extern int optind, extern int optind,
opterr; opterr;
@ -268,14 +279,26 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
if (oids == true) if (oids == true)
{ {
archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n", /*
fmtId(classname, force_quotes)); * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
* fmtId(classname, force_quotes));
*
* - Not used as of V1.3 (needs to be in ArchiveEntry call)
*
*/
sprintf(query, "COPY %s WITH OIDS TO stdout;\n", sprintf(query, "COPY %s WITH OIDS TO stdout;\n",
fmtId(classname, force_quotes)); fmtId(classname, force_quotes));
} }
else else
{ {
archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes)); /*
*archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, force_quotes));
*
* - Not used as of V1.3 (needs to be in ArchiveEntry call)
*
*/
sprintf(query, "COPY %s TO stdout;\n", fmtId(classname, force_quotes)); sprintf(query, "COPY %s TO stdout;\n", fmtId(classname, force_quotes));
} }
res = PQexec(g_conn, query); res = PQexec(g_conn, query);
@ -452,19 +475,28 @@ dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
*/ */
static void static void
dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
const char *onlytable, const bool oids) const char *onlytable, const bool oids, const bool force_quotes)
{ {
int i; int i;
char *all_only; char *all_only;
DataDumperPtr dumpFn; DataDumperPtr dumpFn;
DumpContext *dumpCtx; DumpContext *dumpCtx;
char *oidsPart;
char copyBuf[512];
char *copyStmt;
if (onlytable == NULL) if (onlytable == NULL)
all_only = "all"; all_only = "all";
else else
all_only = "only"; all_only = "only";
if (oids == true)
oidsPart = "WITH OIDS ";
else
oidsPart = "";
if (g_verbose) if (g_verbose)
fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n", fprintf(stderr, "%s dumping out the contents of %s %d table%s/sequence%s %s\n",
g_comment_start, all_only, g_comment_start, all_only,
@ -514,112 +546,28 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
dumpCtx->tblidx = i; dumpCtx->tblidx = i;
dumpCtx->oids = oids; dumpCtx->oids = oids;
if (!dumpData) if (!dumpData) /* Dump/restore using COPY */
{
dumpFn = dumpClasses_nodumpData; dumpFn = dumpClasses_nodumpData;
/* dumpClasses_nodumpData(fout, classname, oids); */ /* dumpClasses_nodumpData(fout, classname, oids); */
else sprintf(copyBuf, "COPY %s %s FROM stdin;\n", fmtId(tblinfo[i].relname, force_quotes),
oidsPart);
copyStmt = copyBuf;
}
else /* Restore using INSERT */
{
dumpFn = dumpClasses_dumpData; dumpFn = dumpClasses_dumpData;
/* dumpClasses_dumpData(fout, classname); */ /* dumpClasses_dumpData(fout, classname); */
copyStmt = NULL;
}
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false), ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
"TABLE DATA", NULL, "", "", tblinfo[i].usename, "TABLE DATA", NULL, "", "", copyStmt, tblinfo[i].usename,
dumpFn, dumpCtx); dumpFn, dumpCtx);
} }
} }
} }
static void
prompt_for_password(char *username, char *password)
{
char buf[512];
int length;
#ifdef HAVE_TERMIOS_H
struct termios t_orig,
t;
#endif
fprintf(stderr, "Username: ");
fflush(stderr);
fgets(username, 100, stdin);
length = strlen(username);
/* skip rest of the line */
if (length > 0 && username[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && username[length - 1] == '\n')
username[length - 1] = '\0';
#ifdef HAVE_TERMIOS_H
tcgetattr(0, &t);
t_orig = t;
t.c_lflag &= ~ECHO;
tcsetattr(0, TCSADRAIN, &t);
#endif
fprintf(stderr, "Password: ");
fflush(stderr);
fgets(password, 100, stdin);
#ifdef HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &t_orig);
#endif
length = strlen(password);
/* skip rest of the line */
if (length > 0 && password[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && password[length - 1] == '\n')
password[length - 1] = '\0';
fprintf(stderr, "\n\n");
}
static void
check_database_version(bool ignoreVersion)
{
PGresult *res;
double myversion;
const char *remoteversion_str;
double remoteversion;
myversion = strtod(PG_VERSION, NULL);
res = PQexec(g_conn, "SELECT version()");
if (!res ||
PQresultStatus(res) != PGRES_TUPLES_OK ||
PQntuples(res) != 1)
{
fprintf(stderr, "check_database_version(): command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
remoteversion_str = PQgetvalue(res, 0, 0);
remoteversion = strtod(remoteversion_str + 11, NULL);
if (myversion != remoteversion)
{
fprintf(stderr, "Database version: %s\npg_dump version: %s\n",
remoteversion_str, PG_VERSION);
if (ignoreVersion)
fprintf(stderr, "Proceeding despite version mismatch.\n");
else
{
fprintf(stderr, "Aborting because of version mismatch.\n"
"Use --ignore-version if you think it's safe to proceed anyway.\n");
exit_nicely(g_conn);
}
}
PQclear(res);
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -634,20 +582,19 @@ main(int argc, char **argv)
bool oids = false; bool oids = false;
TableInfo *tblinfo; TableInfo *tblinfo;
int numTables; int numTables;
char connect_string[512] = "";
char tmp_string[128];
char username[100];
char password[100];
bool use_password = false; bool use_password = false;
int compressLevel = -1; int compressLevel = -1;
bool ignore_version = false; bool ignore_version = false;
int plainText = 0; int plainText = 0;
int outputClean = 0; int outputClean = 0;
int outputBlobs = 0;
RestoreOptions *ropt; RestoreOptions *ropt;
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static struct option long_options[] = { static struct option long_options[] = {
{"data-only", no_argument, NULL, 'a'}, {"data-only", no_argument, NULL, 'a'},
{"blobs", no_argument, NULL, 'b' },
{"clean", no_argument, NULL, 'c'}, {"clean", no_argument, NULL, 'c'},
{"file", required_argument, NULL, 'f'}, {"file", required_argument, NULL, 'f'},
{"format", required_argument, NULL, 'F'}, {"format", required_argument, NULL, 'F'},
@ -686,6 +633,12 @@ main(int argc, char **argv)
else else
progname = strrchr(argv[0], SEP_CHAR) + 1; progname = strrchr(argv[0], SEP_CHAR) + 1;
/* Set defaulty options based on progname */
if (strcmp(progname, "pg_backup") == 0)
{
format = "c";
outputBlobs = 1;
}
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?", long_options, &optindex)) != -1) while ((c = getopt_long(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?", long_options, &optindex)) != -1)
@ -698,6 +651,10 @@ main(int argc, char **argv)
case 'a': /* Dump data only */ case 'a': /* Dump data only */
dataOnly = true; dataOnly = true;
break; break;
case 'b': /* Dump blobs */
outputBlobs = true;
break;
case 'c': /* clean (i.e., drop) schema prior to case 'c': /* clean (i.e., drop) schema prior to
* create */ * create */
outputClean = 1; outputClean = 1;
@ -843,7 +800,12 @@ main(int argc, char **argv)
case 'p': case 'p':
case 'P': case 'P':
plainText = 1; plainText = 1;
g_fout = CreateArchive(filename, archPlainText, 0); g_fout = CreateArchive(filename, archNull, 0);
break;
case 't':
case 'T':
g_fout = CreateArchive(filename, archTar, compressLevel);
break; break;
default: default:
@ -860,53 +822,13 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
/* find database */ /* Let the archiver know how noisy to be */
if (!(dbname = argv[optind]) && g_fout->verbose = g_verbose;
!(dbname = getenv("PGDATABASE")))
{
fprintf(stderr, "%s: no database name specified\n", progname);
exit(1);
}
/* g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname); */ dbname = argv[optind];
if (pghost != NULL)
{
sprintf(tmp_string, "host=%s ", pghost);
strcat(connect_string, tmp_string);
}
if (pgport != NULL)
{
sprintf(tmp_string, "port=%s ", pgport);
strcat(connect_string, tmp_string);
}
if (dbname != NULL)
{
sprintf(tmp_string, "dbname=%s ", dbname);
strcat(connect_string, tmp_string);
}
if (use_password)
{
prompt_for_password(username, password);
strcat(connect_string, "authtype=password ");
sprintf(tmp_string, "user=%s ", username);
strcat(connect_string, tmp_string);
sprintf(tmp_string, "password=%s ", password);
strcat(connect_string, tmp_string);
MemSet(tmp_string, 0, sizeof(tmp_string));
MemSet(password, 0, sizeof(password));
}
g_conn = PQconnectdb(connect_string);
MemSet(connect_string, 0, sizeof(connect_string));
/* check to see that the backend connection was successfully made */
if (PQstatus(g_conn) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
fprintf(stderr, "%s\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* check for version mismatch */ /* Open the database using the Archiver, so it knows about it. Errors mean death */
check_database_version(ignore_version); g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, use_password, ignore_version);
/* /*
* Start serializable transaction to dump consistent data * Start serializable transaction to dump consistent data
@ -916,17 +838,15 @@ main(int argc, char **argv)
res = PQexec(g_conn, "begin"); res = PQexec(g_conn, "begin");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ exit_horribly(g_fout, "BEGIN command failed. Explanation from backend: '%s'.\n",
fprintf(stderr, "BEGIN command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn)); PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
PQclear(res); PQclear(res);
res = PQexec(g_conn, "set transaction isolation level serializable"); res = PQexec(g_conn, "set transaction isolation level serializable");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ exit_horribly(g_fout, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n",
fprintf(stderr, "SET TRANSACTION command failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn)); PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
PQclear(res); PQclear(res);
} }
@ -941,7 +861,12 @@ main(int argc, char **argv)
tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly); tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
if (!schemaOnly) if (!schemaOnly)
dumpClasses(tblinfo, numTables, g_fout, tablename, oids); {
dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes);
}
if (outputBlobs)
ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0);
if (!dataOnly) /* dump indexes and triggers at the end if (!dataOnly) /* dump indexes and triggers at the end
* for performance */ * for performance */
@ -951,6 +876,15 @@ main(int argc, char **argv)
dumpRules(g_fout, tablename, tblinfo, numTables); dumpRules(g_fout, tablename, tblinfo, numTables);
} }
/* Now sort the output nicely */
SortTocByOID(g_fout);
MoveToEnd(g_fout, "TABLE DATA");
MoveToEnd(g_fout, "BLOBS");
MoveToEnd(g_fout, "INDEX");
MoveToEnd(g_fout, "TRIGGER");
MoveToEnd(g_fout, "RULE");
MoveToEnd(g_fout, "ACL");
if (plainText) if (plainText)
{ {
ropt = NewRestoreOptions(); ropt = NewRestoreOptions();
@ -973,6 +907,92 @@ main(int argc, char **argv)
exit(0); exit(0);
} }
/*
* dumpBlobs:
* dump all blobs
*
*/
#define loBufSize 16384
#define loFetchSize 1000
static int
dumpBlobs(Archive *AH, char* junkOid, void *junkVal)
{
PQExpBuffer oidQry = createPQExpBuffer();
PQExpBuffer oidFetchQry = createPQExpBuffer();
PGresult *res;
int i;
int loFd;
char buf[loBufSize];
int cnt;
int blobOid;
if (g_verbose)
fprintf(stderr, "%s saving BLOBs\n", g_comment_start);
/* Cursor to get all BLOB tables */
appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
res = PQexec(g_conn, oidQry->data);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "dumpBlobs(): Declare Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* Fetch for cursor */
appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
do {
/* Do a fetch */
PQclear(res);
res = PQexec(g_conn, oidFetchQry->data);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "dumpBlobs(): Fetch Cursor failed. Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
/* Process the tuples, if any */
for (i = 0; i < PQntuples(res); i++)
{
blobOid = atoi(PQgetvalue(res, i, 0));
/* Open the BLOB */
loFd = lo_open(g_conn, blobOid, INV_READ);
if (loFd == -1)
{
fprintf(stderr, "dumpBlobs(): Could not open large object. "
"Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
StartBlob(AH, blobOid);
/* Now read it in chunks, sending data to archive */
do {
cnt = lo_read(g_conn, loFd, buf, loBufSize);
if (cnt < 0) {
fprintf(stderr, "dumpBlobs(): Error reading large object. "
" Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
exit_nicely(g_conn);
}
WriteData(AH, buf, cnt);
} while (cnt > 0);
lo_close(g_conn, loFd);
EndBlob(AH, blobOid);
}
} while (PQntuples(res) > 0);
return 1;
}
/* /*
* getTypes: * getTypes:
* read all base types in the system catalogs and return them in the * read all base types in the system catalogs and return them in the
@ -2409,7 +2429,7 @@ dumpComment(Archive *fout, const char *target, const char *oid)
target, checkForQuote(PQgetvalue(res, 0, i_description))); target, checkForQuote(PQgetvalue(res, 0, i_description)));
ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/, ArchiveEntry(fout, oid, target, "COMMENT", NULL, query->data, "" /*Del*/,
"" /*Owner*/, NULL, NULL); "" /* Copy */, "" /*Owner*/, NULL, NULL);
} }
@ -2542,7 +2562,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
appendPQExpBuffer(q, ");\n"); appendPQExpBuffer(q, ");\n");
ArchiveEntry(fout, tinfo[i].oid, fmtId(tinfo[i].typname, force_quotes), "TYPE", NULL, ArchiveEntry(fout, tinfo[i].oid, fmtId(tinfo[i].typname, force_quotes), "TYPE", NULL,
q->data, delq->data, tinfo[i].usename, NULL, NULL); q->data, delq->data, "", tinfo[i].usename, NULL, NULL);
/*** Dump Type Comments ***/ /*** Dump Type Comments ***/
@ -2629,7 +2649,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
lancompiler); lancompiler);
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE", ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
NULL, defqry->data, delqry->data, "", NULL, NULL); NULL, defqry->data, delqry->data, "", "", NULL, NULL);
free(lanname); free(lanname);
free(lancompiler); free(lancompiler);
@ -2669,8 +2689,8 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
PQExpBuffer fn = createPQExpBuffer(); PQExpBuffer fn = createPQExpBuffer();
PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer();
PQExpBuffer fnlist = createPQExpBuffer(); PQExpBuffer fnlist = createPQExpBuffer();
PQExpBuffer asPart = createPQExpBuffer();
int j; int j;
PQExpBuffer asPart = createPQExpBuffer();
char func_lang[NAMEDATALEN + 1]; char func_lang[NAMEDATALEN + 1];
PGresult *res; PGresult *res;
int nlangs; int nlangs;
@ -2703,7 +2723,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
i_lanname = PQfnumber(res, "lanname"); i_lanname = PQfnumber(res, "lanname");
/* /*
* See backend/commands/define.c for details of how the 'AS' clause * See backend/commands/define.c for details of how the 'AS' clause
* is used. * is used.
*/ */
@ -2751,7 +2771,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
asPart->data, func_lang); asPart->data, func_lang);
ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data, ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data,
finfo[i].usename, NULL, NULL); "", finfo[i].usename, NULL, NULL);
/*** Dump Function Comments ***/ /*** Dump Function Comments ***/
@ -2870,7 +2890,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
sort2->data); sort2->data);
ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL, ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL,
q->data, delq->data, oprinfo[i].usename, NULL, NULL); q->data, delq->data, "", oprinfo[i].usename, NULL, NULL);
} }
} }
@ -2927,7 +2947,7 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
details->data); details->data);
ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL, ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
q->data, delq->data, agginfo[i].usename, NULL, NULL); q->data, delq->data, "", agginfo[i].usename, NULL, NULL);
/*** Dump Aggregate Comments ***/ /*** Dump Aggregate Comments ***/
@ -3096,7 +3116,7 @@ dumpACL(Archive *fout, TableInfo tbinfo)
free(aclbuf); free(aclbuf);
ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", NULL, NULL); ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "ACL", NULL, sql, "", "", "", NULL, NULL);
} }
@ -3274,7 +3294,7 @@ dumpTables(Archive *fout, TableInfo *tblinfo, int numTables,
if (!dataOnly) { if (!dataOnly) {
ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false), ArchiveEntry(fout, tblinfo[i].oid, fmtId(tblinfo[i].relname, false),
"TABLE", NULL, q->data, delq->data, tblinfo[i].usename, "TABLE", NULL, q->data, delq->data, "", tblinfo[i].usename,
NULL, NULL); NULL, NULL);
} }
@ -3468,7 +3488,7 @@ dumpIndices(Archive *fout, IndInfo *indinfo, int numIndices,
/* Dump Index Comments */ /* Dump Index Comments */
ArchiveEntry(fout, tblinfo[tableInd].oid, id1->data, "INDEX", NULL, q->data, delq->data, ArchiveEntry(fout, tblinfo[tableInd].oid, id1->data, "INDEX", NULL, q->data, delq->data,
tblinfo[tableInd].usename, NULL, NULL); "", tblinfo[tableInd].usename, NULL, NULL);
resetPQExpBuffer(q); resetPQExpBuffer(q);
appendPQExpBuffer(q, "INDEX %s", id1->data); appendPQExpBuffer(q, "INDEX %s", id1->data);
@ -3599,7 +3619,7 @@ setMaxOid(Archive *fout)
pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n"); pos = pos + snprintf(sql+pos, 1024-pos, "\\.\n");
pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n"); pos = pos + snprintf(sql+pos, 1024-pos, "DROP TABLE pg_dump_oid;\n");
ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "","", NULL, NULL); ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "", "", "", NULL, NULL);
} }
/* /*
@ -3750,7 +3770,7 @@ dumpSequence(Archive *fout, TableInfo tbinfo)
} }
ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE", NULL, ArchiveEntry(fout, tbinfo.oid, fmtId(tbinfo.relname, force_quotes), "SEQUENCE", NULL,
query->data, delqry->data, tbinfo.usename, NULL, NULL); query->data, delqry->data, "", tbinfo.usename, NULL, NULL);
/* Dump Sequence Comments */ /* Dump Sequence Comments */
@ -3779,7 +3799,7 @@ dumpTriggers(Archive *fout, const char *tablename,
for (j = 0; j < tblinfo[i].ntrig; j++) for (j = 0; j < tblinfo[i].ntrig; j++)
{ {
ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname, ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname,
"TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "",
tblinfo[i].usename, NULL, NULL); tblinfo[i].usename, NULL, NULL);
dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid); dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid);
} }
@ -3846,7 +3866,7 @@ dumpRules(Archive *fout, const char *tablename,
ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename), ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
"RULE", NULL, PQgetvalue(res, i, i_definition), "RULE", NULL, PQgetvalue(res, i, i_definition),
"", "", NULL, NULL); "", "", "", NULL, NULL);
/* Dump rule comments */ /* Dump rule comments */

View File

@ -1,325 +1,371 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* pg_restore.c * pg_restore.c
* pg_restore is an utility extracting postgres database definitions * pg_restore is an utility extracting postgres database definitions
* from a backup archive created by pg_dump using the archiver * from a backup archive created by pg_dump using the archiver
* interface. * interface.
* *
* pg_restore will read the backup archive and * pg_restore will read the backup archive and
* dump out a script that reproduces * dump out a script that reproduces
* the schema of the database in terms of * the schema of the database in terms of
* user-defined types * user-defined types
* user-defined functions * user-defined functions
* tables * tables
* indices * indices
* aggregates * aggregates
* operators * operators
* ACL - grant/revoke * ACL - grant/revoke
* *
* the output script is SQL that is understood by PostgreSQL * the output script is SQL that is understood by PostgreSQL
* *
* Basic process in a restore operation is: * Basic process in a restore operation is:
* *
* Open the Archive and read the TOC. * Open the Archive and read the TOC.
* Set flags in TOC entries, and *maybe* reorder them. * Set flags in TOC entries, and *maybe* reorder them.
* Generate script to stdout * Generate script to stdout
* Exit * Exit
* *
* Copyright (c) 2000, Philip Warner * Copyright (c) 2000, Philip Warner
* Rights are granted to use this software in any way so long * Rights are granted to use this software in any way so long
* as this notice is not removed. * as this notice is not removed.
* *
* The author is not responsible for loss or damages that may * The author is not responsible for loss or damages that may
* result from it's use. * result from it's use.
* *
* *
* IDENTIFICATION * IDENTIFICATION
* *
* Modifications - 28-Jun-2000 - pjw@rhyme.com.au * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
* *
* Initial version. Command processing taken from original pg_dump. * Initial version. Command processing taken from original pg_dump.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
/* /*
#include "postgres.h" #include "postgres.h"
#include "access/htup.h" #include "access/htup.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_index.h" #include "catalog/pg_index.h"
#include "catalog/pg_trigger.h" #include "catalog/pg_trigger.h"
#include "libpq-fe.h" #include "libpq-fe.h"
*/ */
#include "pg_backup.h" #include "pg_backup.h"
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#include "strdup.h" #include "strdup.h"
#endif #endif
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
#include <termios.h> #include <termios.h>
#endif #endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
/* Forward decls */ /* Forward decls */
static void usage(const char *progname); static void usage(const char *progname);
static char* _cleanupName(char* name); static char* _cleanupName(char* name);
typedef struct option optType; typedef struct option optType;
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
struct option cmdopts[] = { struct option cmdopts[] = {
{ "clean", 0, NULL, 'c' }, { "clean", 0, NULL, 'c' },
{ "data-only", 0, NULL, 'a' }, { "data-only", 0, NULL, 'a' },
{ "file", 1, NULL, 'f' }, { "dbname", 1, NULL, 'd' },
{ "format", 1, NULL, 'F' }, { "file", 1, NULL, 'f' },
{ "function", 2, NULL, 'p' }, { "format", 1, NULL, 'F' },
{ "index", 2, NULL, 'i'}, { "function", 2, NULL, 'P' },
{ "list", 0, NULL, 'l'}, { "host", 1, NULL, 'h' },
{ "no-acl", 0, NULL, 'x' }, { "ignore-version", 0, NULL, 'i'},
{ "oid-order", 0, NULL, 'o'}, { "index", 2, NULL, 'I'},
{ "orig-order", 0, NULL, 'O' }, { "list", 0, NULL, 'l'},
{ "rearrange", 0, NULL, 'r'}, { "no-acl", 0, NULL, 'x' },
{ "schema-only", 0, NULL, 's' }, { "port", 1, NULL, 'p' },
{ "table", 2, NULL, 't'}, { "oid-order", 0, NULL, 'o'},
{ "trigger", 2, NULL, 'T' }, { "orig-order", 0, NULL, 'O' },
{ "use-list", 1, NULL, 'u'}, { "password", 0, NULL, 'u' },
{ "verbose", 0, NULL, 'v' }, { "rearrange", 0, NULL, 'r'},
{ NULL, 0, NULL, 0} { "schema-only", 0, NULL, 's' },
}; { "table", 2, NULL, 't'},
#endif { "trigger", 2, NULL, 'T' },
{ "use-list", 1, NULL, 'U'},
int main(int argc, char **argv) { "verbose", 0, NULL, 'v' },
{ { NULL, 0, NULL, 0}
RestoreOptions *opts; };
char *progname; #endif
int c;
Archive* AH; int main(int argc, char **argv)
char *fileSpec = NULL; {
RestoreOptions *opts;
opts = NewRestoreOptions(); char *progname;
int c;
progname = *argv; Archive* AH;
char *fileSpec = NULL;
#ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "acf:F:i:loOp:st:T:u:vx", cmdopts, NULL)) != EOF) opts = NewRestoreOptions();
#else
while ((c = getopt(argc, argv, "acf:F:i:loOp:st:T:u:vx")) != -1) progname = *argv;
#endif
{ #ifdef HAVE_GETOPT_LONG
switch (c) while ((c = getopt_long(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx", cmdopts, NULL)) != EOF)
{ #else
case 'a': /* Dump data only */ while ((c = getopt(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx")) != -1)
opts->dataOnly = 1; #endif
break; {
case 'c': /* clean (i.e., drop) schema prior to switch (c)
* create */ {
opts->dropSchema = 1; case 'a': /* Dump data only */
break; opts->dataOnly = 1;
case 'f': /* output file name */ break;
opts->filename = strdup(optarg); case 'c': /* clean (i.e., drop) schema prior to
break; * create */
case 'F': opts->dropSchema = 1;
if (strlen(optarg) != 0) break;
opts->formatName = strdup(optarg); case 'd':
break; if (strlen(optarg) != 0)
case 'o': {
opts->oidOrder = 1; opts->dbname = strdup(optarg);
break; opts->useDB = 1;
case 'O': }
opts->origOrder = 1; break;
break; case 'f': /* output file name */
case 'r': opts->filename = strdup(optarg);
opts->rearrange = 1; break;
break; case 'F':
if (strlen(optarg) != 0)
case 'p': /* Function */ opts->formatName = strdup(optarg);
opts->selTypes = 1; break;
opts->selFunction = 1; case 'h':
opts->functionNames = _cleanupName(optarg); if (strlen(optarg) != 0)
break; opts->pghost = strdup(optarg);
case 'i': /* Index */ break;
opts->selTypes = 1; case 'i':
opts->selIndex = 1; opts->ignoreVersion = 1;
opts->indexNames = _cleanupName(optarg); break;
break; case 'o':
case 'T': /* Trigger */ opts->oidOrder = 1;
opts->selTypes = 1; break;
opts->selTrigger = 1; case 'O':
opts->triggerNames = _cleanupName(optarg); opts->origOrder = 1;
break; break;
case 's': /* dump schema only */ case 'p':
opts->schemaOnly = 1; if (strlen(optarg) != 0)
break; opts->pgport = strdup(optarg);
case 't': /* Dump data for this table only */ break;
opts->selTypes = 1; case 'r':
opts->selTable = 1; opts->rearrange = 1;
opts->tableNames = _cleanupName(optarg); break;
break; case 'P': /* Function */
case 'l': /* Dump the TOC summary */ opts->selTypes = 1;
opts->tocSummary = 1; opts->selFunction = 1;
break; opts->functionNames = _cleanupName(optarg);
break;
case 'u': /* input TOC summary file name */ case 'I': /* Index */
opts->tocFile = strdup(optarg); opts->selTypes = 1;
break; opts->selIndex = 1;
opts->indexNames = _cleanupName(optarg);
case 'v': /* verbose */ break;
opts->verbose = 1; case 'T': /* Trigger */
break; opts->selTypes = 1;
case 'x': /* skip ACL dump */ opts->selTrigger = 1;
opts->aclsSkip = 1; opts->triggerNames = _cleanupName(optarg);
break; break;
default: case 's': /* dump schema only */
usage(progname); opts->schemaOnly = 1;
break; break;
} case 't': /* Dump data for this table only */
} opts->selTypes = 1;
opts->selTable = 1;
if (optind < argc) { opts->tableNames = _cleanupName(optarg);
fileSpec = argv[optind]; break;
} else { case 'l': /* Dump the TOC summary */
fileSpec = NULL; opts->tocSummary = 1;
} break;
if (opts->formatName) { case 'u':
opts->requirePassword = 1;
switch (opts->formatName[0]) { break;
case 'c': case 'U': /* input TOC summary file name */
case 'C': opts->tocFile = strdup(optarg);
opts->format = archCustom; break;
break;
case 'v': /* verbose */
case 'f': opts->verbose = 1;
case 'F': break;
opts->format = archFiles; case 'x': /* skip ACL dump */
break; opts->aclsSkip = 1;
break;
default: default:
fprintf(stderr, "%s: Unknown archive format '%s', please specify 'f' or 'c'\n", progname, opts->formatName); usage(progname);
exit (1); break;
} }
} }
AH = OpenArchive(fileSpec, opts->format); if (optind < argc) {
fileSpec = argv[optind];
if (opts->tocFile) } else {
SortTocFromFile(AH, opts); fileSpec = NULL;
}
if (opts->oidOrder)
SortTocByOID(AH); if (opts->formatName) {
else if (opts->origOrder)
SortTocByID(AH); switch (opts->formatName[0]) {
if (opts->rearrange) { case 'c':
MoveToEnd(AH, "TABLE DATA"); case 'C':
MoveToEnd(AH, "INDEX"); opts->format = archCustom;
MoveToEnd(AH, "TRIGGER"); break;
MoveToEnd(AH, "RULE");
MoveToEnd(AH, "ACL"); case 'f':
} case 'F':
opts->format = archFiles;
if (opts->tocSummary) { break;
PrintTOCSummary(AH, opts);
} else { case 't':
RestoreArchive(AH, opts); case 'T':
} opts->format = archTar;
break;
CloseArchive(AH);
default:
return 1; fprintf(stderr, "%s: Unknown archive format '%s', please specify 't' or 'c'\n",
} progname, opts->formatName);
exit (1);
static void usage(const char *progname) }
{ }
#ifdef HAVE_GETOPT_LONG
fprintf(stderr, AH = OpenArchive(fileSpec, opts->format);
"usage: %s [options] [backup file]\n"
" -a, --data-only \t dump out only the data, no schema\n" /* Let the archiver know how noisy to be */
" -c, --clean \t clean(drop) schema prior to create\n" AH->verbose = opts->verbose;
" -f filename \t script output filename\n"
" -F, --format {c|f} \t specify backup file format\n" if (opts->tocFile)
" -p, --function[=name] \t dump functions or named function\n" SortTocFromFile(AH, opts);
" -i, --index[=name] \t dump indexes or named index\n"
" -l, --list \t dump summarized TOC for this file\n" if (opts->oidOrder)
" -o, --oid-order \t dump in oid order\n" SortTocByOID(AH);
" -O, --orig-order \t dump in original dump order\n" else if (opts->origOrder)
" -r, --rearrange \t rearrange output to put indexes etc at end\n" SortTocByID(AH);
" -s, --schema-only \t dump out only the schema, no data\n"
" -t [table], --table[=table] \t dump for this table only\n" if (opts->rearrange) {
" -T, --trigger[=name] \t dump triggers or named trigger\n" MoveToStart(AH, "<Init>");
" -u, --use-list filename \t use specified TOC for ordering output from this file\n" MoveToEnd(AH, "TABLE DATA");
" -v \t verbose\n" MoveToEnd(AH, "BLOBS");
" -x, --no-acl \t skip dumping of ACLs (grant/revoke)\n" MoveToEnd(AH, "INDEX");
, progname); MoveToEnd(AH, "TRIGGER");
#else MoveToEnd(AH, "RULE");
fprintf(stderr, MoveToEnd(AH, "ACL");
"usage: %s [options] [backup file]\n" }
" -a \t dump out only the data, no schema\n"
" -c \t clean(drop) schema prior to create\n" if (opts->tocSummary) {
" -f filename NOT IMPLEMENTED \t script output filename\n" PrintTOCSummary(AH, opts);
" -F {c|f} \t specify backup file format\n" } else {
" -p name \t dump functions or named function\n" RestoreArchive(AH, opts);
" -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" CloseArchive(AH);
" -O \t dump in original dump order\n"
" -r \t rearrange output to put indexes etc at end\n" return 1;
" -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" static void usage(const char *progname)
" -u filename \t use specified TOC for ordering output from this file\n" {
" -v \t verbose\n" #ifdef HAVE_GETOPT_LONG
" -x \t skip dumping of ACLs (grant/revoke)\n" fprintf(stderr,
, progname); "usage: %s [options] [backup file]\n"
#endif " -a, --data-only \t dump out only the data, no schema\n"
fprintf(stderr, " -d, --dbname <name> \t specify database name\n"
"\nIf [backup file] is not supplied, then standard input " " -c, --clean \t clean(drop) schema prior to create\n"
"is used.\n"); " -f filename \t script output filename\n"
fprintf(stderr, "\n"); " -F, --format {c|f} \t specify backup file format\n"
" -h, --host <hostname> \t server host name\n"
exit(1); " -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"
static char* _cleanupName(char* name) " -O, --orig-order \t dump in original dump order\n"
{ " -p, --port <port> \t server port number\n"
int i; " -P, --function[=name] \t dump functions or named function\n"
" -r, --rearrange \t rearrange output to put indexes etc at end\n"
if (!name) " -s, --schema-only \t dump out only the schema, no data\n"
return NULL; " -t [table], --table[=table] \t dump for this table only\n"
" -T, --trigger[=name] \t dump triggers or named trigger\n"
if (strlen(name) == 0) " -u, --password \t use password authentication\n"
return NULL; " -U, --use-list filename \t use specified TOC for ordering output from this file\n"
" -v, --verbose \t verbose\n"
name = strdup(name); " -x, --no-acl \t skip dumping of ACLs (grant/revoke)\n"
, progname);
if (name[0] == '"')
{ #else
strcpy(name, &name[1]); fprintf(stderr,
if (*(name + strlen(name) - 1) == '"') "usage: %s [options] [backup file]\n"
*(name + strlen(name) - 1) = '\0'; " -a \t dump out only the data, no schema\n"
} " -d, <name> \t specify database name\n"
/* otherwise, convert table name to lowercase... */ " -c \t clean(drop) schema prior to create\n"
else " -f filename NOT IMPLEMENTED \t script output filename\n"
{ " -F {c|f} \t specify backup file format\n"
for (i = 0; name[i]; i++) " -h, <hostname> \t server host name\n"
if (isascii((unsigned char) name[i]) && isupper(name[i])) " -i name \t dump indexes or named index\n"
name[i] = tolower(name[i]); " -l \t dump summarized TOC for this file\n"
} " -o \t dump in oid order\n"
return name; " -O \t dump in original dump order\n"
} " -p <port> \t server port number\n"
" -P name \t dump functions or named function\n"
" -r \t rearrange output to put indexes etc at end\n"
" -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;
}