mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
- Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore)
- Added code to dump 'Create Schema' statement (pg_dump) - Don't bother to disable/enable triggers if we don't have a superuser (pg_restore) - Cleaned up code for reconnecting to database. - Force a reconnect as superuser before enabling/disabling triggers. - Added & Removed --throttle (pg_dump) - Fixed minor bug in language dumping code: expbuffres were not being reset. - Fixed version number initialization in _allocAH (pg_backup_archiver.c) - Added second connection when restoring BLOBs to allow temp. table to survive (db reconnection causes temp tables to be lost).
This commit is contained in:
@ -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.21 2000/07/24 06:24:26 pjw Exp $
|
# $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.22 2000/08/01 15:51:44 pjw Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -20,6 +20,22 @@
|
|||||||
*
|
*
|
||||||
* Initial version.
|
* Initial version.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
* Modifications - 28-Jul-2000 - pjw@rhyme.com.au (1.45)
|
||||||
|
*
|
||||||
|
* Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore)
|
||||||
|
* Added code to dump 'Create Schema' statement (pg_dump)
|
||||||
|
* Don't bother to disable/enable triggers if we don't have a superuser (pg_restore)
|
||||||
|
* Cleaned up code for reconnecting to database.
|
||||||
|
* Force a reconnect as superuser before enabling/disabling triggers.
|
||||||
|
*
|
||||||
|
* Modifications - 31-Jul-2000 - pjw@rhyme.com.au (1.46, 1.47)
|
||||||
|
* Added & Removed --throttle (pg_dump)
|
||||||
|
* Fixed minor bug in language dumping code: expbuffres were not being reset.
|
||||||
|
* Fixed version number initialization in _allocAH (pg_backup_archiver.c)
|
||||||
|
* Added second connection when restoring BLOBs to allow temp. table to survive
|
||||||
|
* (db reconnection causes temp tables to be lost).
|
||||||
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -53,6 +69,10 @@ typedef struct _Archive {
|
|||||||
typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);
|
typedef int (*DataDumperPtr)(Archive* AH, char* oid, void* userArg);
|
||||||
|
|
||||||
typedef struct _restoreOptions {
|
typedef struct _restoreOptions {
|
||||||
|
int create; /* Issue commands to create the database */
|
||||||
|
int noOwner; /* Don't reconnect to database to match original object owner */
|
||||||
|
int noReconnect; /* Don't reconnect to database under any cirsumstances */
|
||||||
|
char *superuser; /* Username to use as superuser */
|
||||||
int dataOnly;
|
int dataOnly;
|
||||||
int dropSchema;
|
int dropSchema;
|
||||||
char *filename;
|
char *filename;
|
||||||
@ -84,9 +104,9 @@ typedef struct _restoreOptions {
|
|||||||
int ignoreVersion;
|
int ignoreVersion;
|
||||||
int requirePassword;
|
int requirePassword;
|
||||||
|
|
||||||
int *idWanted;
|
int *idWanted;
|
||||||
int limitToList;
|
int limitToList;
|
||||||
int compression;
|
int compression;
|
||||||
|
|
||||||
} RestoreOptions;
|
} RestoreOptions;
|
||||||
|
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
*
|
*
|
||||||
* Modifications - 28-Jun-2000 - pjw@rhyme.com.au
|
* Modifications - 28-Jun-2000 - pjw@rhyme.com.au
|
||||||
*
|
*
|
||||||
* Initial version.
|
* Initial version.
|
||||||
|
*
|
||||||
|
* Modifications - 31-Jul-2000 - pjw@rhyme.com.au (1.46, 1.47)
|
||||||
|
* Fixed version number initialization in _allocAH (pg_backup_archiver.c)
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -43,7 +46,9 @@ static int _tocSortCompareByIDNum(const void *p1, const void *p2);
|
|||||||
static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt,
|
static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt,
|
||||||
int compression, ArchiveMode mode);
|
int compression, ArchiveMode mode);
|
||||||
static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
|
static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
|
||||||
static void _reconnectAsOwner(ArchiveHandle* AH, TocEntry* te);
|
|
||||||
|
static void _reconnectAsOwner(ArchiveHandle* AH, const char *dbname, TocEntry* te);
|
||||||
|
static void _reconnectAsUser(ArchiveHandle* AH, const char *dbname, char *user);
|
||||||
|
|
||||||
static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt);
|
static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt);
|
||||||
static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
||||||
@ -58,7 +63,7 @@ static char *progname = "Archiver";
|
|||||||
static void _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap);
|
static void _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap);
|
||||||
|
|
||||||
static int _canRestoreBlobs(ArchiveHandle *AH);
|
static int _canRestoreBlobs(ArchiveHandle *AH);
|
||||||
|
static int _restoringToDB(ArchiveHandle *AH);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper functions.
|
* Wrapper functions.
|
||||||
@ -110,6 +115,9 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
|
|
||||||
AH->ropt = ropt;
|
AH->ropt = ropt;
|
||||||
|
|
||||||
|
if (ropt->create && ropt->noReconnect)
|
||||||
|
die_horribly(AH, "%s: --create and --no-reconnect are incompatible options\n",progname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we're using a DB connection, then connect it.
|
* If we're using a DB connection, then connect it.
|
||||||
*/
|
*/
|
||||||
@ -121,8 +129,25 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
|
|
||||||
ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport,
|
ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport,
|
||||||
ropt->requirePassword, ropt->ignoreVersion);
|
ropt->requirePassword, ropt->ignoreVersion);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no superuser was specified then see if the current user will do...
|
||||||
|
*/
|
||||||
|
if (!ropt->superuser)
|
||||||
|
{
|
||||||
|
if (UserIsSuperuser(AH, ConnectedUser(AH)))
|
||||||
|
ropt->superuser = strdup(ConnectedUser(AH));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ropt->superuser)
|
||||||
|
fprintf(stderr, "\n%s: ******** WARNING ******** \n"
|
||||||
|
" Data restoration may fail since any defined triggers\n"
|
||||||
|
" can not be disabled (no superuser username specified).\n"
|
||||||
|
" This is only a problem for restoration into a database\n"
|
||||||
|
" with triggers already defined.\n\n", progname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup the output file if necessary.
|
* Setup the output file if necessary.
|
||||||
*/
|
*/
|
||||||
@ -155,16 +180,20 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
/* Work out what, if anything, we want from this entry */
|
/* Work out what, if anything, we want from this entry */
|
||||||
reqs = _tocEntryRequired(te, ropt);
|
reqs = _tocEntryRequired(te, ropt);
|
||||||
|
|
||||||
/* Reconnect if necessary */
|
|
||||||
if (reqs != 0)
|
|
||||||
{
|
|
||||||
_reconnectAsOwner(AH, te);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (reqs & 1) != 0) /* We want the schema */
|
if ( (reqs & 1) != 0) /* We want the schema */
|
||||||
{
|
{
|
||||||
|
/* Reconnect if necessary */
|
||||||
|
_reconnectAsOwner(AH, "-", te);
|
||||||
|
|
||||||
ahlog(AH, 1, "Creating %s %s\n", te->desc, te->name);
|
ahlog(AH, 1, "Creating %s %s\n", te->desc, te->name);
|
||||||
_printTocEntry(AH, te, ropt);
|
_printTocEntry(AH, te, ropt);
|
||||||
|
|
||||||
|
/* If we created a DB, connect to it... */
|
||||||
|
if (strcmp(te->desc,"DATABASE") == 0)
|
||||||
|
{
|
||||||
|
ahlog(AH, 1, "Connecting to new DB '%s' as %s\n",te->name, te->owner);
|
||||||
|
_reconnectAsUser(AH, te->name, te->owner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -176,8 +205,6 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
die_horribly(AH, "%s: Unable to restore data from a compressed archive\n", progname);
|
die_horribly(AH, "%s: Unable to restore data from a compressed archive\n", progname);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ahlog(AH, 1, "Restoring data for %s \n", te->name);
|
|
||||||
|
|
||||||
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",
|
ahprintf(AH, "--\n-- Data for TOC Entry ID %d (OID %s) %s %s\n--\n\n",
|
||||||
te->id, te->oid, te->desc, te->name);
|
te->id, te->oid, te->desc, te->name);
|
||||||
|
|
||||||
@ -197,6 +224,10 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
|
|
||||||
_disableTriggers(AH, te, ropt);
|
_disableTriggers(AH, te, ropt);
|
||||||
|
|
||||||
|
/* Reconnect if necessary (_disableTriggers may have reconnected) */
|
||||||
|
_reconnectAsOwner(AH, "-", te);
|
||||||
|
|
||||||
|
ahlog(AH, 1, "Restoring data for %s \n", te->name);
|
||||||
|
|
||||||
/* If we have a copy statement, use it. As of V1.3, these are separate
|
/* 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
|
* to allow easy import from withing a database connection. Pre 1.3
|
||||||
@ -256,6 +287,12 @@ void RestoreArchive(Archive* AHX, RestoreOptions *ropt)
|
|||||||
{
|
{
|
||||||
PQfinish(AH->connection);
|
PQfinish(AH->connection);
|
||||||
AH->connection = NULL;
|
AH->connection = NULL;
|
||||||
|
|
||||||
|
if (AH->blobConnection)
|
||||||
|
{
|
||||||
|
PQfinish(AH->blobConnection);
|
||||||
|
AH->blobConnection = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,19 +311,89 @@ RestoreOptions* NewRestoreOptions(void)
|
|||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _canRestoreBlobs(ArchiveHandle *AH)
|
static int _restoringToDB(ArchiveHandle *AH)
|
||||||
{
|
{
|
||||||
return (AH->ropt->useDB && AH->connection);
|
return (AH->ropt->useDB && AH->connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _canRestoreBlobs(ArchiveHandle *AH)
|
||||||
|
{
|
||||||
|
return _restoringToDB(AH);
|
||||||
|
}
|
||||||
|
|
||||||
static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
static void _disableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
||||||
{
|
{
|
||||||
|
char *oldUser = NULL;
|
||||||
|
|
||||||
|
/* Can't do much if we're connected & don't have a superuser */
|
||||||
|
if (_restoringToDB(AH) && !ropt->superuser)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconnect as superuser if possible, since they are the only ones
|
||||||
|
* who can update pg_class...
|
||||||
|
*/
|
||||||
|
if (ropt->superuser)
|
||||||
|
{
|
||||||
|
/* If we're not allowing changes for ownership, then remember the user
|
||||||
|
* so we can change it back here. Otherwise, let _reconnectAsOwner
|
||||||
|
* do what it has to do.
|
||||||
|
*/
|
||||||
|
if (ropt->noOwner)
|
||||||
|
oldUser = strdup(ConnectedUser(AH));
|
||||||
|
_reconnectAsUser(AH, "-", ropt->superuser);
|
||||||
|
}
|
||||||
|
|
||||||
|
ahlog(AH, 1, "Disabling triggers\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable them. This is a hack. Needs to be done via an appropriate 'SET'
|
||||||
|
* command when one is available.
|
||||||
|
*/
|
||||||
ahprintf(AH, "-- Disable triggers\n");
|
ahprintf(AH, "-- Disable triggers\n");
|
||||||
ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" !~ '^pg_';\n\n");
|
ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" !~ '^pg_';\n\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the user connection from the start of this procedure
|
||||||
|
* if _reconnectAsOwner is disabled.
|
||||||
|
*/
|
||||||
|
if (ropt->noOwner && oldUser)
|
||||||
|
{
|
||||||
|
_reconnectAsUser(AH, "-", oldUser);
|
||||||
|
free(oldUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _enableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
static void _enableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
||||||
{
|
{
|
||||||
|
char *oldUser = NULL;
|
||||||
|
|
||||||
|
/* Can't do much if we're connected & don't have a superuser */
|
||||||
|
if (_restoringToDB(AH) && !ropt->superuser)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconnect as superuser if possible, since they are the only ones
|
||||||
|
* who can update pg_class...
|
||||||
|
*/
|
||||||
|
if (ropt->superuser)
|
||||||
|
{
|
||||||
|
/* If we're not allowing changes for ownership, then remember the user
|
||||||
|
* so we can change it back here. Otherwise, let _reconnectAsOwner
|
||||||
|
* do what it has to do
|
||||||
|
*/
|
||||||
|
if (ropt->noOwner)
|
||||||
|
oldUser = strdup(ConnectedUser(AH));
|
||||||
|
|
||||||
|
_reconnectAsUser(AH, "-", ropt->superuser);
|
||||||
|
}
|
||||||
|
|
||||||
|
ahlog(AH, 1, "Enabling triggers\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable them. This is a hack. Needs to be done via an appropriate 'SET'
|
||||||
|
* command when one is available.
|
||||||
|
*/
|
||||||
ahprintf(AH, "-- Enable triggers\n");
|
ahprintf(AH, "-- Enable triggers\n");
|
||||||
ahprintf(AH, "BEGIN TRANSACTION;\n");
|
ahprintf(AH, "BEGIN TRANSACTION;\n");
|
||||||
ahprintf(AH, "CREATE TEMP TABLE \"tr\" (\"tmp_relname\" name, \"tmp_reltriggers\" smallint);\n");
|
ahprintf(AH, "CREATE TEMP TABLE \"tr\" (\"tmp_relname\" name, \"tmp_reltriggers\" smallint);\n");
|
||||||
@ -298,8 +405,17 @@ static void _enableTriggers(ArchiveHandle *AH, TocEntry *te, RestoreOptions *rop
|
|||||||
"\"pg_class\".\"relname\" = TMP.\"tmp_relname\";\n");
|
"\"pg_class\".\"relname\" = TMP.\"tmp_relname\";\n");
|
||||||
ahprintf(AH, "DROP TABLE \"tr\";\n");
|
ahprintf(AH, "DROP TABLE \"tr\";\n");
|
||||||
ahprintf(AH, "COMMIT TRANSACTION;\n\n");
|
ahprintf(AH, "COMMIT TRANSACTION;\n\n");
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the user connection from the start of this procedure
|
||||||
|
* if _reconnectAsOwner is disabled.
|
||||||
|
*/
|
||||||
|
if (ropt->noOwner && oldUser)
|
||||||
|
{
|
||||||
|
_reconnectAsUser(AH, "-", oldUser);
|
||||||
|
free(oldUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
|
* This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
|
||||||
@ -394,6 +510,8 @@ void PrintTOCSummary(Archive* AHX, RestoreOptions *ropt)
|
|||||||
default:
|
default:
|
||||||
fmtName = "UNKNOWN";
|
fmtName = "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ahprintf(AH, "; Dump Version: %d.%d-%d\n", AH->vmaj, AH->vmin, AH->vrev);
|
||||||
ahprintf(AH, "; Format: %s\n;\n", fmtName);
|
ahprintf(AH, "; Format: %s\n;\n", fmtName);
|
||||||
|
|
||||||
ahprintf(AH, ";\n; Selected TOC Entries:\n;\n");
|
ahprintf(AH, ";\n; Selected TOC Entries:\n;\n");
|
||||||
@ -456,14 +574,14 @@ void StartRestoreBlob(ArchiveHandle* AH, int oid)
|
|||||||
AH->createdBlobXref = 1;
|
AH->createdBlobXref = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StartTransaction(AH);
|
||||||
|
|
||||||
loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
|
loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
|
||||||
if (loOid == 0)
|
if (loOid == 0)
|
||||||
die_horribly(AH, "%s: unable to create BLOB\n", progname);
|
die_horribly(AH, "%s: unable to create BLOB\n", progname);
|
||||||
|
|
||||||
ahlog(AH, 1, "Restoring BLOB oid %d as %d\n", oid, loOid);
|
ahlog(AH, 1, "Restoring BLOB oid %d as %d\n", oid, loOid);
|
||||||
|
|
||||||
StartTransaction(AH);
|
|
||||||
|
|
||||||
InsertBlobXref(AH, oid, loOid);
|
InsertBlobXref(AH, oid, loOid);
|
||||||
|
|
||||||
AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
|
AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
|
||||||
@ -829,6 +947,8 @@ static void _die_horribly(ArchiveHandle *AH, const char *fmt, va_list ap)
|
|||||||
if (AH)
|
if (AH)
|
||||||
if (AH->connection)
|
if (AH->connection)
|
||||||
PQfinish(AH->connection);
|
PQfinish(AH->connection);
|
||||||
|
if (AH->blobConnection)
|
||||||
|
PQfinish(AH->blobConnection);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -1113,6 +1233,7 @@ static ArchiveHandle* _allocAH(const char* FileSpec, const ArchiveFormat fmt,
|
|||||||
|
|
||||||
AH->vmaj = K_VERS_MAJOR;
|
AH->vmaj = K_VERS_MAJOR;
|
||||||
AH->vmin = K_VERS_MINOR;
|
AH->vmin = K_VERS_MINOR;
|
||||||
|
AH->vrev = K_VERS_REV;
|
||||||
|
|
||||||
AH->createDate = time(NULL);
|
AH->createDate = time(NULL);
|
||||||
|
|
||||||
@ -1299,6 +1420,9 @@ static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt)
|
|||||||
if (ropt->aclsSkip && strcmp(te->desc,"ACL") == 0)
|
if (ropt->aclsSkip && strcmp(te->desc,"ACL") == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!ropt->create && strcmp(te->desc,"DATABASE") == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Check if tablename only is wanted */
|
/* Check if tablename only is wanted */
|
||||||
if (ropt->selTypes)
|
if (ropt->selTypes)
|
||||||
{
|
{
|
||||||
@ -1351,20 +1475,32 @@ static int _tocEntryRequired(TocEntry* te, RestoreOptions *ropt)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _reconnectAsOwner(ArchiveHandle* AH, TocEntry* te)
|
static void _reconnectAsUser(ArchiveHandle* AH, const char *dbname, char *user)
|
||||||
{
|
{
|
||||||
if (te->owner && strlen(te->owner) != 0 && strcmp(AH->currUser, te->owner) != 0) {
|
if (AH->ropt && AH->ropt->noReconnect)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (user && strlen(user) != 0
|
||||||
|
&& ( (strcmp(AH->currUser, user) != 0) || (strcmp(dbname,"-") != 0)))
|
||||||
|
{
|
||||||
if (RestoringToDB(AH))
|
if (RestoringToDB(AH))
|
||||||
{
|
{
|
||||||
ReconnectDatabase(AH, te->owner);
|
ReconnectDatabase(AH, dbname, user);
|
||||||
/* todo pjw - ???? fix for db connection... */
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ahprintf(AH, "\\connect - %s\n", te->owner);
|
ahprintf(AH, "\\connect %s %s\n", dbname, user);
|
||||||
}
|
}
|
||||||
AH->currUser = te->owner;
|
AH->currUser = user;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _reconnectAsOwner(ArchiveHandle* AH, const char *dbname, TocEntry* te)
|
||||||
|
{
|
||||||
|
if (AH->ropt && AH->ropt->noOwner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_reconnectAsUser(AH, dbname, te->owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
|
static int _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
|
||||||
|
@ -59,7 +59,7 @@ typedef z_stream *z_streamp;
|
|||||||
|
|
||||||
#define K_VERS_MAJOR 1
|
#define K_VERS_MAJOR 1
|
||||||
#define K_VERS_MINOR 4
|
#define K_VERS_MINOR 4
|
||||||
#define K_VERS_REV 3
|
#define K_VERS_REV 8
|
||||||
|
|
||||||
/* Data block types */
|
/* Data block types */
|
||||||
#define BLK_DATA 1
|
#define BLK_DATA 1
|
||||||
@ -97,8 +97,7 @@ 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);
|
||||||
typedef void (*PrintExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
|
typedef void (*PrintExtraTocPtr) (struct _archiveHandle* AH, struct _tocEntry* te);
|
||||||
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 (*CustomOutPtr) (struct _archiveHandle* AH, const void* buf, int len);
|
||||||
|
|
||||||
@ -134,7 +133,7 @@ typedef struct _archiveHandle {
|
|||||||
char vrev;
|
char vrev;
|
||||||
int version; /* Conveniently formatted version */
|
int version; /* Conveniently formatted version */
|
||||||
|
|
||||||
int debugLevel; /* Not used. Intended for logging */
|
int debugLevel; /* Used for logging (currently only by --verbose) */
|
||||||
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 */
|
||||||
|
|
||||||
@ -158,15 +157,15 @@ typedef struct _archiveHandle {
|
|||||||
WriteDataPtr WriteDataPtr; /* Called to send some table data to the archive */
|
WriteDataPtr WriteDataPtr; /* Called to send some table data to the archive */
|
||||||
EndDataPtr EndDataPtr; /* Called when table data dump is finished */
|
EndDataPtr EndDataPtr; /* Called when table data dump is finished */
|
||||||
WriteBytePtr WriteBytePtr; /* Write a byte to output */
|
WriteBytePtr WriteBytePtr; /* Write a byte to output */
|
||||||
ReadBytePtr ReadBytePtr; /* */
|
ReadBytePtr ReadBytePtr; /* Read a byte from an archive */
|
||||||
WriteBufPtr WriteBufPtr;
|
WriteBufPtr WriteBufPtr; /* Write a buffer of output to the archive */
|
||||||
ReadBufPtr ReadBufPtr;
|
ReadBufPtr ReadBufPtr; /* Read a buffer of input from the archive */
|
||||||
ClosePtr ClosePtr; /* Close the archive */
|
ClosePtr ClosePtr; /* Close the archive */
|
||||||
WriteExtraTocPtr WriteExtraTocPtr; /* Write extra TOC entry data associated with */
|
WriteExtraTocPtr WriteExtraTocPtr; /* Write extra TOC entry data associated with */
|
||||||
/* the current archive format */
|
/* the current archive format */
|
||||||
ReadExtraTocPtr ReadExtraTocPtr; /* Read extr info associated with archie format */
|
ReadExtraTocPtr ReadExtraTocPtr; /* Read extr info associated with archie format */
|
||||||
PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */
|
PrintExtraTocPtr PrintExtraTocPtr; /* Extra TOC info for format */
|
||||||
PrintTocDataPtr PrintTocDataPtr;
|
PrintTocDataPtr PrintTocDataPtr;
|
||||||
|
|
||||||
StartBlobsPtr StartBlobsPtr;
|
StartBlobsPtr StartBlobsPtr;
|
||||||
EndBlobsPtr EndBlobsPtr;
|
EndBlobsPtr EndBlobsPtr;
|
||||||
@ -182,6 +181,7 @@ typedef struct _archiveHandle {
|
|||||||
char *pghost;
|
char *pghost;
|
||||||
char *pgport;
|
char *pgport;
|
||||||
PGconn *connection;
|
PGconn *connection;
|
||||||
|
PGconn *blobConnection; /* Connection for BLOB xref */
|
||||||
int connectToDB; /* Flag to indicate if direct DB connection is required */
|
int connectToDB; /* Flag to indicate if direct DB connection is required */
|
||||||
int pgCopyIn; /* Currently in libpq 'COPY IN' mode. */
|
int pgCopyIn; /* Currently in libpq 'COPY IN' mode. */
|
||||||
PQExpBuffer pgCopyBuf; /* Left-over data from incomplete lines in COPY IN */
|
PQExpBuffer pgCopyBuf; /* Left-over data from incomplete lines in COPY IN */
|
||||||
@ -265,7 +265,10 @@ 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);
|
||||||
extern int RestoringToDB(ArchiveHandle* AH);
|
extern int RestoringToDB(ArchiveHandle* AH);
|
||||||
extern int ReconnectDatabase(ArchiveHandle *AH, char *newUser);
|
extern int ReconnectDatabase(ArchiveHandle *AH, const char* dbname, char *newUser);
|
||||||
|
extern int UserIsSuperuser(ArchiveHandle *AH, char* user);
|
||||||
|
extern char* ConnectedUser(ArchiveHandle *AH);
|
||||||
|
extern int ConnectedUserIsSuperuser(ArchiveHandle *AH);
|
||||||
|
|
||||||
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, ...);
|
||||||
|
@ -33,8 +33,10 @@
|
|||||||
|
|
||||||
static const char *progname = "Archiver(db)";
|
static const char *progname = "Archiver(db)";
|
||||||
|
|
||||||
static void _prompt_for_password(char *username, char *password);
|
static void _prompt_for_password(char *username, char *password);
|
||||||
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
|
static void _check_database_version(ArchiveHandle *AH, bool ignoreVersion);
|
||||||
|
static PGconn* _connectDB(ArchiveHandle *AH, const char* newdbname, char *newUser);
|
||||||
|
static int _executeSqlCommand(ArchiveHandle* AH, PGconn *conn, PQExpBuffer qry, char *desc);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -131,7 +133,83 @@ _check_database_version(ArchiveHandle *AH, bool ignoreVersion)
|
|||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReconnectDatabase(ArchiveHandle *AH, char *newUser)
|
/*
|
||||||
|
* Check if a given user is a superuser.
|
||||||
|
*/
|
||||||
|
int UserIsSuperuser(ArchiveHandle *AH, char* user)
|
||||||
|
{
|
||||||
|
PQExpBuffer qry = createPQExpBuffer();
|
||||||
|
PGresult *res;
|
||||||
|
int i_usesuper;
|
||||||
|
int ntups;
|
||||||
|
int isSuper;
|
||||||
|
|
||||||
|
/* Get the superuser setting */
|
||||||
|
appendPQExpBuffer(qry, "select usesuper from pg_user where usename = '%s'", user);
|
||||||
|
res = PQexec(AH->connection, qry->data);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
die_horribly(AH, "%s: null result checking superuser status of %s.\n",
|
||||||
|
progname, user);
|
||||||
|
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
die_horribly(AH, "%s: Could not check superuser status of %s. Explanation from backend: %s\n",
|
||||||
|
progname, user, PQerrorMessage(AH->connection));
|
||||||
|
|
||||||
|
ntups = PQntuples(res);
|
||||||
|
|
||||||
|
if (ntups == 0)
|
||||||
|
isSuper = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i_usesuper = PQfnumber(res, "usesuper");
|
||||||
|
isSuper = (strcmp(PQgetvalue(res, 0, i_usesuper), "t") == 0);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return isSuper;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectedUserIsSuperuser(ArchiveHandle *AH)
|
||||||
|
{
|
||||||
|
return UserIsSuperuser(AH, PQuser(AH->connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ConnectedUser(ArchiveHandle *AH)
|
||||||
|
{
|
||||||
|
return PQuser(AH->connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconnect the DB associated with the archive handle
|
||||||
|
*/
|
||||||
|
int ReconnectDatabase(ArchiveHandle *AH, const char* newdbname, char *newUser)
|
||||||
|
{
|
||||||
|
PGconn *newConn;
|
||||||
|
char *dbname;
|
||||||
|
|
||||||
|
if (!newdbname || (strcmp(newdbname, "-") == 0) )
|
||||||
|
dbname = PQdb(AH->connection);
|
||||||
|
else
|
||||||
|
dbname = (char*)newdbname;
|
||||||
|
|
||||||
|
/* Let's see if the request is already satisfied */
|
||||||
|
if (strcmp(PQuser(AH->connection), newUser) == 0 && strcmp(newdbname, PQdb(AH->connection)) == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
newConn = _connectDB(AH, dbname, newUser);
|
||||||
|
|
||||||
|
PQfinish(AH->connection);
|
||||||
|
AH->connection = newConn;
|
||||||
|
strcpy(AH->username, newUser);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to the db again.
|
||||||
|
*/
|
||||||
|
static PGconn* _connectDB(ArchiveHandle *AH, const char* reqdb, char *requser)
|
||||||
{
|
{
|
||||||
int need_pass;
|
int need_pass;
|
||||||
PGconn *newConn;
|
PGconn *newConn;
|
||||||
@ -139,47 +217,55 @@ int ReconnectDatabase(ArchiveHandle *AH, char *newUser)
|
|||||||
char *pwparam = NULL;
|
char *pwparam = NULL;
|
||||||
int badPwd = 0;
|
int badPwd = 0;
|
||||||
int noPwd = 0;
|
int noPwd = 0;
|
||||||
|
char *newdb;
|
||||||
|
char *newuser;
|
||||||
|
|
||||||
ahlog(AH, 1, "Connecting as %s\n", newUser);
|
if (!reqdb || (strcmp(reqdb, "-") == 0) )
|
||||||
|
newdb = PQdb(AH->connection);
|
||||||
|
else
|
||||||
|
newdb = (char*)reqdb;
|
||||||
|
|
||||||
|
if (!requser || (strlen(requser) == 0))
|
||||||
|
newuser = PQuser(AH->connection);
|
||||||
|
else
|
||||||
|
newuser = (char*)requser;
|
||||||
|
|
||||||
|
ahlog(AH, 1, "Connecting to %s as %s\n", newdb, newuser);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
need_pass = false;
|
need_pass = false;
|
||||||
newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
|
newConn = PQsetdbLogin(PQhost(AH->connection), PQport(AH->connection),
|
||||||
NULL, NULL, PQdb(AH->connection),
|
NULL, NULL, newdb,
|
||||||
newUser, pwparam);
|
newuser, pwparam);
|
||||||
if (!newConn)
|
if (!newConn)
|
||||||
die_horribly(AH, "%s: Failed to reconnect (PQsetdbLogin failed).\n", progname);
|
die_horribly(AH, "%s: Failed to reconnect (PQsetdbLogin failed).\n", progname);
|
||||||
|
|
||||||
if (PQstatus(newConn) == CONNECTION_BAD)
|
if (PQstatus(newConn) == CONNECTION_BAD)
|
||||||
{
|
{
|
||||||
noPwd = (strcmp(PQerrorMessage(newConn), "fe_sendauth: no password supplied\n") == 0);
|
noPwd = (strcmp(PQerrorMessage(newConn), "fe_sendauth: no password supplied\n") == 0);
|
||||||
badPwd = (strncmp(PQerrorMessage(newConn), "Password authentication failed for user", 39)
|
badPwd = (strncmp(PQerrorMessage(newConn), "Password authentication failed for user", 39)
|
||||||
== 0);
|
== 0);
|
||||||
|
|
||||||
if (noPwd || badPwd)
|
if (noPwd || badPwd)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (badPwd)
|
if (badPwd)
|
||||||
fprintf(stderr, "Password incorrect\n");
|
fprintf(stderr, "Password incorrect\n");
|
||||||
|
|
||||||
fprintf(stderr, "Connecting to %s as %s\n", PQdb(AH->connection), newUser);
|
fprintf(stderr, "Connecting to %s as %s\n", PQdb(AH->connection), newuser);
|
||||||
|
|
||||||
need_pass = true;
|
need_pass = true;
|
||||||
_prompt_for_password(newUser, password);
|
_prompt_for_password(newuser, password);
|
||||||
pwparam = password;
|
pwparam = password;
|
||||||
}
|
|
||||||
else
|
|
||||||
die_horribly(AH, "%s: Could not reconnect. %s\n", progname, PQerrorMessage(newConn));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
die_horribly(AH, "%s: Could not reconnect. %s\n", progname, PQerrorMessage(newConn));
|
||||||
|
}
|
||||||
|
|
||||||
} while (need_pass);
|
} while (need_pass);
|
||||||
|
|
||||||
PQfinish(AH->connection);
|
return newConn;
|
||||||
AH->connection = newConn;
|
|
||||||
strcpy(AH->username, newUser);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -247,25 +333,46 @@ PGconn* ConnectDatabase(Archive *AHX,
|
|||||||
/* check for version mismatch */
|
/* check for version mismatch */
|
||||||
_check_database_version(AH, ignoreVersion);
|
_check_database_version(AH, ignoreVersion);
|
||||||
|
|
||||||
AH->currUser = PQuser(AH->connection);
|
/*
|
||||||
|
* AH->currUser = PQuser(AH->connection);
|
||||||
|
*
|
||||||
|
* Removed because it prevented an initial \connect
|
||||||
|
* when dumping to SQL in pg_dump.
|
||||||
|
*/
|
||||||
|
|
||||||
return AH->connection;
|
return AH->connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Public interface */
|
||||||
/* Convenience function to send a query. Monitors result to handle COPY statements */
|
/* Convenience function to send a query. Monitors result to handle COPY statements */
|
||||||
int ExecuteSqlCommand(ArchiveHandle* AH, PQExpBuffer qry, char *desc)
|
int ExecuteSqlCommand(ArchiveHandle* AH, PQExpBuffer qry, char *desc)
|
||||||
|
{
|
||||||
|
return _executeSqlCommand(AH, AH->connection, qry, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle command execution. This is used to execute a command on more than one connection,
|
||||||
|
* but the 'pgCopyIn' setting assumes the COPY commands are ONLY executed on the primary
|
||||||
|
* setting...an error will be raised otherwise.
|
||||||
|
*/
|
||||||
|
static int _executeSqlCommand(ArchiveHandle* AH, PGconn *conn, PQExpBuffer qry, char *desc)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
/* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
|
/* fprintf(stderr, "Executing: '%s'\n\n", qry->data); */
|
||||||
res = PQexec(AH->connection, qry->data);
|
res = PQexec(conn, qry->data);
|
||||||
if (!res)
|
if (!res)
|
||||||
die_horribly(AH, "%s: %s. No result from backend.\n", progname, desc);
|
die_horribly(AH, "%s: %s. No result from backend.\n", progname, desc);
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
if (PQresultStatus(res) == PGRES_COPY_IN)
|
if (PQresultStatus(res) == PGRES_COPY_IN)
|
||||||
|
{
|
||||||
|
if (conn != AH->connection)
|
||||||
|
die_horribly(AH, "%s: COPY command execute in non-primary connection.\n", progname);
|
||||||
|
|
||||||
AH->pgCopyIn = 1;
|
AH->pgCopyIn = 1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
die_horribly(AH, "%s: %s. Code = %d. Explanation from backend: '%s'.\n",
|
die_horribly(AH, "%s: %s. Code = %d. Explanation from backend: '%s'.\n",
|
||||||
progname, desc, PQresultStatus(res), PQerrorMessage(AH->connection));
|
progname, desc, PQresultStatus(res), PQerrorMessage(AH->connection));
|
||||||
@ -467,7 +574,7 @@ void FixupBlobRefs(ArchiveHandle *AH, char *tablename)
|
|||||||
" WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid "
|
" WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid "
|
||||||
" AND t.typname = 'oid' AND c.relname = '%s';", tablename);
|
" AND t.typname = 'oid' AND c.relname = '%s';", tablename);
|
||||||
|
|
||||||
res = PQexec(AH->connection, tblQry->data);
|
res = PQexec(AH->blobConnection, tblQry->data);
|
||||||
if (!res)
|
if (!res)
|
||||||
die_horribly(AH, "%s: could not find OID attrs of %s. Explanation from backend '%s'\n",
|
die_horribly(AH, "%s: could not find OID attrs of %s. Explanation from backend '%s'\n",
|
||||||
progname, tablename, PQerrorMessage(AH->connection));
|
progname, tablename, PQerrorMessage(AH->connection));
|
||||||
@ -493,7 +600,7 @@ void FixupBlobRefs(ArchiveHandle *AH, char *tablename)
|
|||||||
|
|
||||||
ahlog(AH, 10, " - sql = %s\n", tblQry->data);
|
ahlog(AH, 10, " - sql = %s\n", tblQry->data);
|
||||||
|
|
||||||
uRes = PQexec(AH->connection, tblQry->data);
|
uRes = PQexec(AH->blobConnection, tblQry->data);
|
||||||
if (!uRes)
|
if (!uRes)
|
||||||
die_horribly(AH, "%s: could not update attr %s of table %s. Explanation from backend '%s'\n",
|
die_horribly(AH, "%s: could not update attr %s of table %s. Explanation from backend '%s'\n",
|
||||||
progname, attr, tablename, PQerrorMessage(AH->connection));
|
progname, attr, tablename, PQerrorMessage(AH->connection));
|
||||||
@ -516,16 +623,22 @@ void CreateBlobXrefTable(ArchiveHandle* AH)
|
|||||||
{
|
{
|
||||||
PQExpBuffer qry = createPQExpBuffer();
|
PQExpBuffer qry = createPQExpBuffer();
|
||||||
|
|
||||||
|
/* IF we don't have a BLOB connection, then create one */
|
||||||
|
if (!AH->blobConnection)
|
||||||
|
{
|
||||||
|
AH->blobConnection = _connectDB(AH, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
ahlog(AH, 1, "Creating table for BLOBS xrefs\n");
|
ahlog(AH, 1, "Creating table for BLOBS xrefs\n");
|
||||||
|
|
||||||
appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid oid, newOid oid);", BLOB_XREF_TABLE);
|
appendPQExpBuffer(qry, "Create Temporary Table %s(oldOid oid, newOid oid);", BLOB_XREF_TABLE);
|
||||||
|
|
||||||
ExecuteSqlCommand(AH, qry, "can not create BLOB xref table '" BLOB_XREF_TABLE "'");
|
_executeSqlCommand(AH, AH->blobConnection, qry, "can not create BLOB xref table '" BLOB_XREF_TABLE "'");
|
||||||
|
|
||||||
resetPQExpBuffer(qry);
|
resetPQExpBuffer(qry);
|
||||||
|
|
||||||
appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE);
|
appendPQExpBuffer(qry, "Create Unique Index %s_ix on %s(oldOid)", BLOB_XREF_TABLE, BLOB_XREF_TABLE);
|
||||||
ExecuteSqlCommand(AH, qry, "can not create index on BLOB xref table '" BLOB_XREF_TABLE "'");
|
_executeSqlCommand(AH, AH->blobConnection, qry, "can not create index on BLOB xref table '" BLOB_XREF_TABLE "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertBlobXref(ArchiveHandle* AH, int old, int new)
|
void InsertBlobXref(ArchiveHandle* AH, int old, int new)
|
||||||
@ -534,7 +647,7 @@ void InsertBlobXref(ArchiveHandle* AH, int old, int new)
|
|||||||
|
|
||||||
appendPQExpBuffer(qry, "Insert Into %s(oldOid, newOid) Values (%d, %d);", BLOB_XREF_TABLE, old, new);
|
appendPQExpBuffer(qry, "Insert Into %s(oldOid, newOid) Values (%d, %d);", BLOB_XREF_TABLE, old, new);
|
||||||
|
|
||||||
ExecuteSqlCommand(AH, qry, "can not create BLOB xref entry");
|
_executeSqlCommand(AH, AH->blobConnection, qry, "can not create BLOB xref entry");
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartTransaction(ArchiveHandle* AH)
|
void StartTransaction(ArchiveHandle* AH)
|
||||||
|
@ -55,7 +55,7 @@ static void _EndBlobs(ArchiveHandle* AH, TocEntry* te);
|
|||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
//typedef gzFile ThingFile;
|
/* typedef gzFile ThingFile; */
|
||||||
typedef FILE ThingFile;
|
typedef FILE ThingFile;
|
||||||
#else
|
#else
|
||||||
typedef FILE ThingFile;
|
typedef FILE ThingFile;
|
||||||
@ -159,7 +159,7 @@ void InitArchiveFmt_Tar(ArchiveHandle* AH)
|
|||||||
ctx->tarFHpos = 0;
|
ctx->tarFHpos = 0;
|
||||||
|
|
||||||
/* Make unbuffered since we will dup() it, and the buffers screw each other */
|
/* Make unbuffered since we will dup() it, and the buffers screw each other */
|
||||||
//setvbuf(ctx->tarFH, NULL, _IONBF, 0);
|
/* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
|
||||||
|
|
||||||
ctx->hasSeek = (fseek(ctx->tarFH, 0, SEEK_CUR) == 0);
|
ctx->hasSeek = (fseek(ctx->tarFH, 0, SEEK_CUR) == 0);
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ void InitArchiveFmt_Tar(ArchiveHandle* AH)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Make unbuffered since we will dup() it, and the buffers screw each other */
|
/* Make unbuffered since we will dup() it, and the buffers screw each other */
|
||||||
//setvbuf(ctx->tarFH, NULL, _IONBF, 0);
|
/* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
|
||||||
|
|
||||||
ctx->tarFHpos = 0;
|
ctx->tarFHpos = 0;
|
||||||
|
|
||||||
@ -487,7 +487,7 @@ static int _WriteData(ArchiveHandle* AH, const void* data, int dLen)
|
|||||||
|
|
||||||
tarWrite((void*)data, dLen, tctx->TH);
|
tarWrite((void*)data, dLen, tctx->TH);
|
||||||
|
|
||||||
//GZWRITE((void*)data, 1, dLen, tctx->TH->FH);
|
/* GZWRITE((void*)data, 1, dLen, tctx->TH->FH); */
|
||||||
|
|
||||||
return dLen;
|
return dLen;
|
||||||
}
|
}
|
||||||
@ -764,6 +764,7 @@ static void _CloseArchive(ArchiveHandle* AH)
|
|||||||
ropt = NewRestoreOptions();
|
ropt = NewRestoreOptions();
|
||||||
ropt->dropSchema = 1;
|
ropt->dropSchema = 1;
|
||||||
ropt->compression = 0;
|
ropt->compression = 0;
|
||||||
|
ropt->superuser = PQuser(AH->connection);
|
||||||
|
|
||||||
savVerbose = AH->public.verbose;
|
savVerbose = AH->public.verbose;
|
||||||
AH->public.verbose = 0;
|
AH->public.verbose = 0;
|
||||||
@ -1116,10 +1117,10 @@ static void _tarWriteHeader(TAR_MEMBER* th)
|
|||||||
sprintf(&h[297], "%.31s", ""); /* How do I get group reliably? Do I need to? */
|
sprintf(&h[297], "%.31s", ""); /* How do I get group reliably? Do I need to? */
|
||||||
|
|
||||||
/* Maj Dev 8 */
|
/* Maj Dev 8 */
|
||||||
// sprintf(&h[329], "%8o", 0);
|
/* sprintf(&h[329], "%8o", 0); */
|
||||||
|
|
||||||
/* Min Dev */
|
/* Min Dev */
|
||||||
// sprintf(&h[337], "%8o", 0);
|
/* sprintf(&h[337], "%8o", 0); */
|
||||||
|
|
||||||
|
|
||||||
while ( (sum = _tarChecksum(h)) != lastSum)
|
while ( (sum = _tarChecksum(h)) != lastSum)
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.161 2000/07/24 06:24:26 pjw Exp $
|
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.162 2000/08/01 15:51:44 pjw Exp $
|
||||||
*
|
*
|
||||||
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
|
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
|
||||||
*
|
*
|
||||||
@ -66,6 +66,19 @@
|
|||||||
* - Support for BLOB output.
|
* - Support for BLOB output.
|
||||||
* - Sort archive by OID, put some items at end (out of OID order)
|
* - Sort archive by OID, put some items at end (out of OID order)
|
||||||
*
|
*
|
||||||
|
* Modifications - 28-Jul-2000 - pjw@rhyme.com.au (1.45)
|
||||||
|
*
|
||||||
|
* Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore)
|
||||||
|
* Added code to dump 'Create Schema' statement (pg_dump)
|
||||||
|
* Don't bother to disable/enable triggers if we don't have a superuser (pg_restore)
|
||||||
|
* Cleaned up code for reconnecting to database.
|
||||||
|
* Force a reconnect as superuser before enabling/disabling triggers.
|
||||||
|
*
|
||||||
|
* Modifications - 31-Jul-2000 - pjw@rhyme.com.au (1.46, 1.47)
|
||||||
|
* Added & Removed --throttle (pg_dump)
|
||||||
|
* Fixed minor bug in language dumping code: expbuffres were not being reset.
|
||||||
|
* Fixed version number initialization in _allocAH (pg_backup_archiver.c)
|
||||||
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -118,7 +131,7 @@ 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*);
|
static int dumpBlobs(Archive *AH, char*, void*);
|
||||||
|
static int dumpDatabase(Archive *AH);
|
||||||
|
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
extern int optind,
|
extern int optind,
|
||||||
@ -163,6 +176,7 @@ help(const char *progname)
|
|||||||
puts(
|
puts(
|
||||||
" -a, --data-only dump out only the data, not the schema\n"
|
" -a, --data-only dump out only the data, not the schema\n"
|
||||||
" -c, --clean clean (drop) schema prior to create\n"
|
" -c, --clean clean (drop) schema prior to create\n"
|
||||||
|
" -C, --create output commands to create database\n"
|
||||||
" -d, --inserts dump data as INSERT, rather than COPY, commands\n"
|
" -d, --inserts dump data as INSERT, rather than COPY, commands\n"
|
||||||
" -D, --attribute-inserts dump data as INSERT commands with attribute names\n"
|
" -D, --attribute-inserts dump data as INSERT commands with attribute names\n"
|
||||||
" -f, --file specify output file name\n"
|
" -f, --file specify output file name\n"
|
||||||
@ -172,8 +186,11 @@ help(const char *progname)
|
|||||||
" -n, --no-quotes suppress most quotes around identifiers\n"
|
" -n, --no-quotes suppress most quotes around identifiers\n"
|
||||||
" -N, --quotes enable most quotes around identifiers\n"
|
" -N, --quotes enable most quotes around identifiers\n"
|
||||||
" -o, --oids dump object ids (oids)\n"
|
" -o, --oids dump object ids (oids)\n"
|
||||||
|
" -O, --no-owner don't output \\connect commands in plain text format\n"
|
||||||
" -p, --port <port> server port number\n"
|
" -p, --port <port> server port number\n"
|
||||||
|
" -R, --no-reconnect disable ALL reconnections to the database in plain text format\n"
|
||||||
" -s, --schema-only dump out only the schema, no data\n"
|
" -s, --schema-only dump out only the schema, no data\n"
|
||||||
|
" -S, --superuser <name> specify the superuser username to use in plain text format\n"
|
||||||
" -t, --table <table> dump for this table only\n"
|
" -t, --table <table> dump for this table only\n"
|
||||||
" -u, --password use password authentication\n"
|
" -u, --password use password authentication\n"
|
||||||
" -v, --verbose verbose\n"
|
" -v, --verbose verbose\n"
|
||||||
@ -184,6 +201,7 @@ help(const char *progname)
|
|||||||
puts(
|
puts(
|
||||||
" -a dump out only the data, no schema\n"
|
" -a dump out only the data, no schema\n"
|
||||||
" -c clean (drop) schema prior to create\n"
|
" -c clean (drop) schema prior to create\n"
|
||||||
|
" -C output commands to create database\n"
|
||||||
" -d dump data as INSERT, rather than COPY, commands\n"
|
" -d dump data as INSERT, rather than COPY, commands\n"
|
||||||
" -D dump data as INSERT commands with attribute names\n"
|
" -D dump data as INSERT commands with attribute names\n"
|
||||||
" -f specify output file name\n"
|
" -f specify output file name\n"
|
||||||
@ -193,8 +211,11 @@ help(const char *progname)
|
|||||||
" -n suppress most quotes around identifiers\n"
|
" -n suppress most quotes around identifiers\n"
|
||||||
" -N enable most quotes around identifiers\n"
|
" -N enable most quotes around identifiers\n"
|
||||||
" -o dump object ids (oids)\n"
|
" -o dump object ids (oids)\n"
|
||||||
|
" -O don't output \\connect commands in plain text format\n"
|
||||||
" -p <port> server port number\n"
|
" -p <port> server port number\n"
|
||||||
|
" -R disable ALL reconnections to the database in plain text format\n"
|
||||||
" -s dump out only the schema, no data\n"
|
" -s dump out only the schema, no data\n"
|
||||||
|
" -S <name> specify the superuser username to use in plain text format\n"
|
||||||
" -t <table> dump for this table only\n"
|
" -t <table> dump for this table only\n"
|
||||||
" -u use password authentication\n"
|
" -u use password authentication\n"
|
||||||
" -v verbose\n"
|
" -v verbose\n"
|
||||||
@ -264,6 +285,7 @@ isViewRule(char *relname)
|
|||||||
* - this routine is called by the Archiver when it wants the table
|
* - this routine is called by the Archiver when it wants the table
|
||||||
* to be dumped.
|
* to be dumped.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
||||||
{
|
{
|
||||||
@ -277,6 +299,9 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
|||||||
bool copydone;
|
bool copydone;
|
||||||
char copybuf[COPYBUFSIZ];
|
char copybuf[COPYBUFSIZ];
|
||||||
|
|
||||||
|
if (g_verbose)
|
||||||
|
fprintf(stderr, "%s dumping out the contents of table %s\n", g_comment_start, classname);
|
||||||
|
|
||||||
if (oids == true)
|
if (oids == true)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -325,6 +350,7 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
copydone = false;
|
copydone = false;
|
||||||
|
|
||||||
while (!copydone)
|
while (!copydone)
|
||||||
{
|
{
|
||||||
ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
|
ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
|
||||||
@ -350,6 +376,46 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* THROTTLE:
|
||||||
|
*
|
||||||
|
* There was considerable discussion in late July, 2000 regarding slowing down
|
||||||
|
* pg_dump when backing up large tables. Users with both slow & fast (muti-processor)
|
||||||
|
* machines experienced performance degradation when doing a backup.
|
||||||
|
*
|
||||||
|
* Initial attempts based on sleeping for a number of ms for each ms of work were deemed
|
||||||
|
* too complex, then a simple 'sleep in each loop' implementation was suggested. The latter
|
||||||
|
* failed because the loop was too tight. Finally, the following was implemented:
|
||||||
|
*
|
||||||
|
* If throttle is non-zero, then
|
||||||
|
* See how long since the last sleep.
|
||||||
|
* Work out how long to sleep (based on ratio).
|
||||||
|
* If sleep is more than 100ms, then
|
||||||
|
* sleep
|
||||||
|
* reset timer
|
||||||
|
* EndIf
|
||||||
|
* EndIf
|
||||||
|
*
|
||||||
|
* where the throttle value was the number of ms to sleep per ms of work. The calculation was
|
||||||
|
* done in each loop.
|
||||||
|
*
|
||||||
|
* Most of the hard work is done in the backend, and this solution still did not work
|
||||||
|
* particularly well: on slow machines, the ratio was 50:1, and on medium paced machines, 1:1,
|
||||||
|
* and on fast multi-processor machines, it had little or no effect, for reasons that were unclear.
|
||||||
|
*
|
||||||
|
* Further discussion ensued, and the proposal was dropped.
|
||||||
|
*
|
||||||
|
* For those people who want this feature, it can be implemented using gettimeofday in each
|
||||||
|
* loop, calculating the time since last sleep, multiplying that by the sleep ratio, then
|
||||||
|
* if the result is more than a preset 'minimum sleep time' (say 100ms), call the 'select'
|
||||||
|
* function to sleep for a subsecond period ie.
|
||||||
|
*
|
||||||
|
* select(0, NULL, NULL, NULL, &tvi);
|
||||||
|
*
|
||||||
|
* This will return after the interval specified in the structure tvi. Fianally, call
|
||||||
|
* gettimeofday again to save the 'last sleep time'.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
archprintf(fout, "\\.\n");
|
archprintf(fout, "\\.\n");
|
||||||
}
|
}
|
||||||
@ -366,11 +432,10 @@ dumpClasses_nodumpData(Archive *fout, char* oid, void *dctxv)
|
|||||||
exit_nicely(g_conn);
|
exit_nicely(g_conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
|
dumpClasses_dumpData(Archive *fout, char* oid, void *dctxv)
|
||||||
{
|
{
|
||||||
@ -498,7 +563,7 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
|
|||||||
|
|
||||||
|
|
||||||
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 preparing to dump out the contents of %s %d table%s/sequence%s %s\n",
|
||||||
g_comment_start, all_only,
|
g_comment_start, all_only,
|
||||||
(onlytable == NULL) ? numTables : 1,
|
(onlytable == NULL) ? numTables : 1,
|
||||||
(onlytable == NULL) ? "s" : "", (onlytable == NULL) ? "s" : "",
|
(onlytable == NULL) ? "s" : "", (onlytable == NULL) ? "s" : "",
|
||||||
@ -536,7 +601,7 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
|
|||||||
if (!onlytable || (!strcmp(classname, onlytable)))
|
if (!onlytable || (!strcmp(classname, onlytable)))
|
||||||
{
|
{
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
fprintf(stderr, "%s dumping out the contents of Table '%s' %s\n",
|
fprintf(stderr, "%s preparing to dump out the contents of Table '%s' %s\n",
|
||||||
g_comment_start, classname, g_comment_end);
|
g_comment_start, classname, g_comment_end);
|
||||||
|
|
||||||
/* becomeUser(fout, tblinfo[i].usename); */
|
/* becomeUser(fout, tblinfo[i].usename); */
|
||||||
@ -587,7 +652,11 @@ main(int argc, char **argv)
|
|||||||
bool ignore_version = false;
|
bool ignore_version = false;
|
||||||
int plainText = 0;
|
int plainText = 0;
|
||||||
int outputClean = 0;
|
int outputClean = 0;
|
||||||
|
int outputCreate = 0;
|
||||||
int outputBlobs = 0;
|
int outputBlobs = 0;
|
||||||
|
int outputNoOwner = 0;
|
||||||
|
int outputNoReconnect = 0;
|
||||||
|
char *outputSuperuser = NULL;
|
||||||
|
|
||||||
RestoreOptions *ropt;
|
RestoreOptions *ropt;
|
||||||
|
|
||||||
@ -596,17 +665,21 @@ main(int argc, char **argv)
|
|||||||
{"data-only", no_argument, NULL, 'a'},
|
{"data-only", no_argument, NULL, 'a'},
|
||||||
{"blobs", no_argument, NULL, 'b' },
|
{"blobs", no_argument, NULL, 'b' },
|
||||||
{"clean", no_argument, NULL, 'c'},
|
{"clean", no_argument, NULL, 'c'},
|
||||||
|
{"create", no_argument, NULL, 'C'},
|
||||||
{"file", required_argument, NULL, 'f'},
|
{"file", required_argument, NULL, 'f'},
|
||||||
{"format", required_argument, NULL, 'F'},
|
{"format", required_argument, NULL, 'F'},
|
||||||
{"inserts", no_argument, NULL, 'd'},
|
{"inserts", no_argument, NULL, 'd'},
|
||||||
{"attribute-inserts", no_argument, NULL, 'D'},
|
{"attribute-inserts", no_argument, NULL, 'D'},
|
||||||
{"host", required_argument, NULL, 'h'},
|
{"host", required_argument, NULL, 'h'},
|
||||||
{"ignore-version", no_argument, NULL, 'i'},
|
{"ignore-version", no_argument, NULL, 'i'},
|
||||||
|
{"no-reconnect", no_argument, NULL, 'R'},
|
||||||
{"no-quotes", no_argument, NULL, 'n'},
|
{"no-quotes", no_argument, NULL, 'n'},
|
||||||
{"quotes", no_argument, NULL, 'N'},
|
{"quotes", no_argument, NULL, 'N'},
|
||||||
{"oids", no_argument, NULL, 'o'},
|
{"oids", no_argument, NULL, 'o'},
|
||||||
|
{"no-owner", no_argument, NULL, 'O'},
|
||||||
{"port", required_argument, NULL, 'p'},
|
{"port", required_argument, NULL, 'p'},
|
||||||
{"schema-only", no_argument, NULL, 's'},
|
{"schema-only", no_argument, NULL, 's'},
|
||||||
|
{"superuser", required_argument, NULL, 'S'},
|
||||||
{"table", required_argument, NULL, 't'},
|
{"table", required_argument, NULL, 't'},
|
||||||
{"password", no_argument, NULL, 'u'},
|
{"password", no_argument, NULL, 'u'},
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
{"verbose", no_argument, NULL, 'v'},
|
||||||
@ -641,10 +714,11 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#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, "acCdDf:F:h:inNoOp:sS:t:uvxzZ:V?", long_options, &optindex)) != -1)
|
||||||
#else
|
#else
|
||||||
while ((c = getopt(argc, argv, "acdDf:F:h:inNop:st:uvxzZ:V?-")) != -1)
|
while ((c = getopt(argc, argv, "acCdDf:F:h:inNoOp:sS:t:uvxzZ:V?-")) != -1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
@ -660,6 +734,11 @@ main(int argc, char **argv)
|
|||||||
outputClean = 1;
|
outputClean = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'C': /* Create DB */
|
||||||
|
|
||||||
|
outputCreate = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'd': /* dump data as proper insert strings */
|
case 'd': /* dump data as proper insert strings */
|
||||||
dumpData = true;
|
dumpData = true;
|
||||||
break;
|
break;
|
||||||
@ -690,12 +769,21 @@ main(int argc, char **argv)
|
|||||||
case 'o': /* Dump oids */
|
case 'o': /* Dump oids */
|
||||||
oids = true;
|
oids = true;
|
||||||
break;
|
break;
|
||||||
|
case 'O': /* Don't reconnect to match owner */
|
||||||
|
outputNoOwner = 1;
|
||||||
|
break;
|
||||||
case 'p': /* server port */
|
case 'p': /* server port */
|
||||||
pgport = optarg;
|
pgport = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'R': /* No reconnect */
|
||||||
|
outputNoReconnect = 1;
|
||||||
|
break;
|
||||||
case 's': /* dump schema only */
|
case 's': /* dump schema only */
|
||||||
schemaOnly = true;
|
schemaOnly = true;
|
||||||
break;
|
break;
|
||||||
|
case 'S': /* Username for superuser in plain text output */
|
||||||
|
outputSuperuser = strdup(optarg);
|
||||||
|
break;
|
||||||
case 't': /* Dump data for this table only */
|
case 't': /* Dump data for this table only */
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -852,6 +940,10 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
g_last_builtin_oid = findLastBuiltinOid();
|
g_last_builtin_oid = findLastBuiltinOid();
|
||||||
|
|
||||||
|
/* Dump the database definition */
|
||||||
|
if (!dataOnly)
|
||||||
|
dumpDatabase(g_fout);
|
||||||
|
|
||||||
if (oids == true)
|
if (oids == true)
|
||||||
setMaxOid(g_fout);
|
setMaxOid(g_fout);
|
||||||
|
|
||||||
@ -878,6 +970,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
/* Now sort the output nicely */
|
/* Now sort the output nicely */
|
||||||
SortTocByOID(g_fout);
|
SortTocByOID(g_fout);
|
||||||
|
MoveToStart(g_fout, "DATABASE");
|
||||||
MoveToEnd(g_fout, "TABLE DATA");
|
MoveToEnd(g_fout, "TABLE DATA");
|
||||||
MoveToEnd(g_fout, "BLOBS");
|
MoveToEnd(g_fout, "BLOBS");
|
||||||
MoveToEnd(g_fout, "INDEX");
|
MoveToEnd(g_fout, "INDEX");
|
||||||
@ -890,6 +983,15 @@ main(int argc, char **argv)
|
|||||||
ropt->filename = (char*)filename;
|
ropt->filename = (char*)filename;
|
||||||
ropt->dropSchema = outputClean;
|
ropt->dropSchema = outputClean;
|
||||||
ropt->aclsSkip = aclsSkip;
|
ropt->aclsSkip = aclsSkip;
|
||||||
|
ropt->superuser = outputSuperuser;
|
||||||
|
ropt->create = outputCreate;
|
||||||
|
ropt->noOwner = outputNoOwner;
|
||||||
|
ropt->noReconnect = outputNoReconnect;
|
||||||
|
|
||||||
|
if (outputSuperuser)
|
||||||
|
ropt->superuser = outputSuperuser;
|
||||||
|
else
|
||||||
|
ropt->superuser = PQuser(g_conn);
|
||||||
|
|
||||||
if (compressLevel == -1)
|
if (compressLevel == -1)
|
||||||
ropt->compression = 0;
|
ropt->compression = 0;
|
||||||
@ -906,6 +1008,60 @@ main(int argc, char **argv)
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dumpDatabase:
|
||||||
|
* dump the database definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
dumpDatabase(Archive *AH)
|
||||||
|
{
|
||||||
|
PQExpBuffer dbQry = createPQExpBuffer();
|
||||||
|
PQExpBuffer delQry = createPQExpBuffer();
|
||||||
|
PQExpBuffer creaQry = createPQExpBuffer();
|
||||||
|
PGresult *res;
|
||||||
|
int ntups;
|
||||||
|
int i_dba;
|
||||||
|
|
||||||
|
if (g_verbose)
|
||||||
|
fprintf(stderr, "%s saving database definition\n", g_comment_start);
|
||||||
|
|
||||||
|
/* Get the dba */
|
||||||
|
appendPQExpBuffer(dbQry, "select pg_get_userbyid(datdba) as dba from pg_database"
|
||||||
|
" where datname = '%s'", PQdb(g_conn));
|
||||||
|
|
||||||
|
res = PQexec(g_conn, dbQry->data);
|
||||||
|
if (!res ||
|
||||||
|
PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "getDatabase(): SELECT failed. Explanation from backend: '%s'.\n",
|
||||||
|
PQerrorMessage(g_conn));
|
||||||
|
exit_nicely(g_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ntups = PQntuples(res);
|
||||||
|
|
||||||
|
if (ntups != 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "getDatabase(): SELECT returned %d databases.\n", ntups);
|
||||||
|
exit_nicely(g_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPQExpBuffer(creaQry, "Create Database \"%s\";\n", PQdb(g_conn));
|
||||||
|
appendPQExpBuffer(delQry, "Drop Database \"%s\";\n", PQdb(g_conn));
|
||||||
|
i_dba = PQfnumber(res, "dba");
|
||||||
|
|
||||||
|
ArchiveEntry(AH, "0" /* OID */, PQdb(g_conn) /* Name */, "DATABASE", NULL,
|
||||||
|
creaQry->data /* Create */, delQry->data /*Del*/,
|
||||||
|
"" /* Copy */, PQgetvalue(res, 0, i_dba) /*Owner*/,
|
||||||
|
NULL /* Dumper */, NULL /* Dumper Arg */);
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dumpBlobs:
|
* dumpBlobs:
|
||||||
* dump all blobs
|
* dump all blobs
|
||||||
@ -2652,6 +2808,9 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
|
|||||||
|
|
||||||
free(lanname);
|
free(lanname);
|
||||||
free(lancompiler);
|
free(lancompiler);
|
||||||
|
|
||||||
|
resetPQExpBuffer(defqry);
|
||||||
|
resetPQExpBuffer(delqry);
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
@ -37,7 +37,15 @@
|
|||||||
*
|
*
|
||||||
* 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.
|
||||||
|
*
|
||||||
|
* Modifications - 28-Jul-2000 - pjw@rhyme.com.au (1.45)
|
||||||
|
*
|
||||||
|
* Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore)
|
||||||
|
* Added code to dump 'Create Schema' statement (pg_dump)
|
||||||
|
* Don't bother to disable/enable triggers if we don't have a superuser (pg_restore)
|
||||||
|
* Cleaned up code for reconnecting to database.
|
||||||
|
* Force a reconnect as superuser before enabling/disabling triggers.
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -83,6 +91,7 @@ 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' },
|
||||||
|
{ "create", 0, NULL, 'C' },
|
||||||
{ "data-only", 0, NULL, 'a' },
|
{ "data-only", 0, NULL, 'a' },
|
||||||
{ "dbname", 1, NULL, 'd' },
|
{ "dbname", 1, NULL, 'd' },
|
||||||
{ "file", 1, NULL, 'f' },
|
{ "file", 1, NULL, 'f' },
|
||||||
@ -93,12 +102,15 @@ struct option cmdopts[] = {
|
|||||||
{ "index", 2, NULL, 'I'},
|
{ "index", 2, NULL, 'I'},
|
||||||
{ "list", 0, NULL, 'l'},
|
{ "list", 0, NULL, 'l'},
|
||||||
{ "no-acl", 0, NULL, 'x' },
|
{ "no-acl", 0, NULL, 'x' },
|
||||||
|
{ "no-owner", 0, NULL, 'O'},
|
||||||
|
{ "no-reconnect", 0, NULL, 'R' },
|
||||||
{ "port", 1, NULL, 'p' },
|
{ "port", 1, NULL, 'p' },
|
||||||
{ "oid-order", 0, NULL, 'o'},
|
{ "oid-order", 0, NULL, 'o'},
|
||||||
{ "orig-order", 0, NULL, 'O' },
|
{ "orig-order", 0, NULL, 'N'},
|
||||||
{ "password", 0, NULL, 'u' },
|
{ "password", 0, NULL, 'u' },
|
||||||
{ "rearrange", 0, NULL, 'r'},
|
{ "rearrange", 0, NULL, 'r'},
|
||||||
{ "schema-only", 0, NULL, 's' },
|
{ "schema-only", 0, NULL, 's' },
|
||||||
|
{ "superuser", 1, NULL, 'S' },
|
||||||
{ "table", 2, NULL, 't'},
|
{ "table", 2, NULL, 't'},
|
||||||
{ "trigger", 2, NULL, 'T' },
|
{ "trigger", 2, NULL, 'T' },
|
||||||
{ "use-list", 1, NULL, 'U'},
|
{ "use-list", 1, NULL, 'U'},
|
||||||
@ -120,9 +132,9 @@ int main(int argc, char **argv)
|
|||||||
progname = *argv;
|
progname = *argv;
|
||||||
|
|
||||||
#ifdef HAVE_GETOPT_LONG
|
#ifdef HAVE_GETOPT_LONG
|
||||||
while ((c = getopt_long(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx", cmdopts, NULL)) != EOF)
|
while ((c = getopt_long(argc, argv, "acCd:f:F:h:i:lNoOp:rRsSt:T:uU:vx", cmdopts, NULL)) != EOF)
|
||||||
#else
|
#else
|
||||||
while ((c = getopt(argc, argv, "acd:f:F:h:i:loOp:st:T:u:U:vx")) != -1)
|
while ((c = getopt(argc, argv, "acCd:f:F:h:i:lNoOp:rRsSt:T:uU:vx")) != -1)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
@ -134,6 +146,9 @@ int main(int argc, char **argv)
|
|||||||
* create */
|
* create */
|
||||||
opts->dropSchema = 1;
|
opts->dropSchema = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
opts->create = 1;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
if (strlen(optarg) != 0)
|
if (strlen(optarg) != 0)
|
||||||
{
|
{
|
||||||
@ -155,11 +170,14 @@ int main(int argc, char **argv)
|
|||||||
case 'i':
|
case 'i':
|
||||||
opts->ignoreVersion = 1;
|
opts->ignoreVersion = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
opts->origOrder = 1;
|
||||||
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
opts->oidOrder = 1;
|
opts->oidOrder = 1;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
opts->origOrder = 1;
|
opts->noOwner = 1;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (strlen(optarg) != 0)
|
if (strlen(optarg) != 0)
|
||||||
@ -168,6 +186,9 @@ int main(int argc, char **argv)
|
|||||||
case 'r':
|
case 'r':
|
||||||
opts->rearrange = 1;
|
opts->rearrange = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'R':
|
||||||
|
opts->noReconnect = 1;
|
||||||
|
break;
|
||||||
case 'P': /* Function */
|
case 'P': /* Function */
|
||||||
opts->selTypes = 1;
|
opts->selTypes = 1;
|
||||||
opts->selFunction = 1;
|
opts->selFunction = 1;
|
||||||
@ -186,6 +207,10 @@ int main(int argc, char **argv)
|
|||||||
case 's': /* dump schema only */
|
case 's': /* dump schema only */
|
||||||
opts->schemaOnly = 1;
|
opts->schemaOnly = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'S': /* Superuser username */
|
||||||
|
if (strlen(optarg) != 0)
|
||||||
|
opts->superuser = strdup(optarg);
|
||||||
|
break;
|
||||||
case 't': /* Dump data for this table only */
|
case 't': /* Dump data for this table only */
|
||||||
opts->selTypes = 1;
|
opts->selTypes = 1;
|
||||||
opts->selTable = 1;
|
opts->selTable = 1;
|
||||||
@ -270,6 +295,9 @@ int main(int argc, char **argv)
|
|||||||
MoveToEnd(AH, "ACL");
|
MoveToEnd(AH, "ACL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Database MUST be at start */
|
||||||
|
MoveToStart(AH, "DATABASE");
|
||||||
|
|
||||||
if (opts->tocSummary) {
|
if (opts->tocSummary) {
|
||||||
PrintTOCSummary(AH, opts);
|
PrintTOCSummary(AH, opts);
|
||||||
} else {
|
} else {
|
||||||
@ -289,17 +317,21 @@ static void usage(const char *progname)
|
|||||||
" -a, --data-only \t dump out only the data, no schema\n"
|
" -a, --data-only \t dump out only the data, no schema\n"
|
||||||
" -d, --dbname <name> \t specify database name\n"
|
" -d, --dbname <name> \t specify database name\n"
|
||||||
" -c, --clean \t clean(drop) schema prior to create\n"
|
" -c, --clean \t clean(drop) schema prior to create\n"
|
||||||
|
" -C, --create \t output commands to create the database\n"
|
||||||
" -f filename \t script output filename\n"
|
" -f filename \t script output filename\n"
|
||||||
" -F, --format {c|f} \t specify backup file format\n"
|
" -F, --format {c|f} \t specify backup file format\n"
|
||||||
" -h, --host <hostname> \t server host name\n"
|
" -h, --host <hostname> \t server host name\n"
|
||||||
" -i, --index[=name] \t dump indexes or named index\n"
|
" -i, --index[=name] \t dump indexes or named index\n"
|
||||||
" -l, --list \t dump summarized TOC for this file\n"
|
" -l, --list \t dump summarized TOC for this file\n"
|
||||||
|
" -N, --orig-order \t dump in original dump order\n"
|
||||||
" -o, --oid-order \t dump in oid order\n"
|
" -o, --oid-order \t dump in oid order\n"
|
||||||
" -O, --orig-order \t dump in original dump order\n"
|
" -O, --no-owner \t don't output reconnect to database to match object owner\n"
|
||||||
" -p, --port <port> \t server port number\n"
|
" -p, --port <port> \t server port number\n"
|
||||||
" -P, --function[=name] \t dump functions or named function\n"
|
" -P, --function[=name] \t dump functions or named function\n"
|
||||||
" -r, --rearrange \t rearrange output to put indexes etc at end\n"
|
" -r, --rearrange \t rearrange output to put indexes etc at end\n"
|
||||||
|
" -R, --no-reconnect \t disallow ALL reconnections to the database\n"
|
||||||
" -s, --schema-only \t dump out only the schema, no data\n"
|
" -s, --schema-only \t dump out only the schema, no data\n"
|
||||||
|
" -S, --superuser <name> \t specify the superuser username to use in disabling triggers\n"
|
||||||
" -t [table], --table[=table] \t dump for this table only\n"
|
" -t [table], --table[=table] \t dump for this table only\n"
|
||||||
" -T, --trigger[=name] \t dump triggers or named trigger\n"
|
" -T, --trigger[=name] \t dump triggers or named trigger\n"
|
||||||
" -u, --password \t use password authentication\n"
|
" -u, --password \t use password authentication\n"
|
||||||
@ -312,19 +344,23 @@ static void usage(const char *progname)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage: %s [options] [backup file]\n"
|
"usage: %s [options] [backup file]\n"
|
||||||
" -a \t dump out only the data, no schema\n"
|
" -a \t dump out only the data, no schema\n"
|
||||||
" -d, <name> \t specify database name\n"
|
" -d <name> \t specify database name\n"
|
||||||
" -c \t clean(drop) schema prior to create\n"
|
" -c \t clean(drop) schema prior to create\n"
|
||||||
" -f filename NOT IMPLEMENTED \t script output filename\n"
|
" -C \t output commands to create the database\n"
|
||||||
" -F {c|f} \t specify backup file format\n"
|
" -f filename \t script output filename\n"
|
||||||
" -h, <hostname> \t server host name\n"
|
" -F {c|f} \t specify backup file format\n"
|
||||||
|
" -h <hostname> \t server host name\n"
|
||||||
" -i name \t dump indexes or named index\n"
|
" -i name \t dump indexes or named index\n"
|
||||||
" -l \t dump summarized TOC for this file\n"
|
" -l \t dump summarized TOC for this file\n"
|
||||||
|
" -N \t dump in original dump order\n"
|
||||||
" -o \t dump in oid order\n"
|
" -o \t dump in oid order\n"
|
||||||
" -O \t dump in original dump order\n"
|
" -O \t don't output reconnect to database to match object owner\n"
|
||||||
" -p <port> \t server port number\n"
|
" -p <port> \t server port number\n"
|
||||||
" -P name \t dump functions or named function\n"
|
" -P name \t dump functions or named function\n"
|
||||||
" -r \t rearrange output to put indexes etc at end\n"
|
" -r \t rearrange output to put indexes etc at end\n"
|
||||||
|
" -R \t disallow ALL reconnections to the database\n"
|
||||||
" -s \t dump out only the schema, no data\n"
|
" -s \t dump out only the schema, no data\n"
|
||||||
|
" -S <name> \t specify the superuser username to use in disabling triggers\n"
|
||||||
" -t name \t dump for this table only\n"
|
" -t name \t dump for this table only\n"
|
||||||
" -T name \t dump triggers or named trigger\n"
|
" -T name \t dump triggers or named trigger\n"
|
||||||
" -u \t use password authentication\n"
|
" -u \t use password authentication\n"
|
||||||
|
Reference in New Issue
Block a user