2000-07-04 16:25:28 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_backup_archiver.c
|
|
|
|
*
|
|
|
|
* Private implementation of the archiver routines.
|
|
|
|
*
|
|
|
|
* See the headers to pg_restore for more details.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2000, Philip Warner
|
|
|
|
* Rights are granted to use this software in any way so long
|
2001-03-22 05:01:46 +01:00
|
|
|
* as this notice is not removed.
|
2000-07-04 16:25:28 +02:00
|
|
|
*
|
|
|
|
* The author is not responsible for loss or damages that may
|
2002-08-10 18:57:32 +02:00
|
|
|
* result from its use.
|
2000-07-04 16:25:28 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2005-03-18 18:32:55 +01:00
|
|
|
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.106 2005/03/18 17:32:55 tgl Exp $
|
2002-01-18 18:13:51 +01:00
|
|
|
*
|
2000-07-04 16:25:28 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "pg_backup.h"
|
2002-08-18 11:36:26 +02:00
|
|
|
#include "pg_dump.h"
|
2000-07-04 16:25:28 +02:00
|
|
|
#include "pg_backup_archiver.h"
|
2000-07-21 13:40:08 +02:00
|
|
|
#include "pg_backup_db.h"
|
2002-08-27 20:57:26 +02:00
|
|
|
#include "dumputils.h"
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2002-02-11 01:18:20 +01:00
|
|
|
#include <ctype.h>
|
2002-08-27 20:57:26 +02:00
|
|
|
#include <unistd.h>
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2005-01-26 20:44:43 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
#include <io.h>
|
|
|
|
#endif
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
#include "pqexpbuffer.h"
|
|
|
|
#include "libpq/libpq-fs.h"
|
2002-08-18 11:36:26 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
const char *progname;
|
2005-01-25 23:44:31 +01:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
static char *modulename = gettext_noop("archiver");
|
|
|
|
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
|
|
|
|
const int compression, ArchiveMode mode);
|
2005-01-23 01:03:54 +01:00
|
|
|
static void _getObjectDescription(PQExpBuffer buf, TocEntry *te,
|
|
|
|
ArchiveHandle *AH);
|
2004-08-13 23:37:28 +02:00
|
|
|
static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData, bool acl_pass);
|
2004-07-13 05:00:17 +02:00
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
static void fixPriorBlobRefs(ArchiveHandle *AH, TocEntry *blobte,
|
2004-08-29 07:07:03 +02:00
|
|
|
RestoreOptions *ropt);
|
2004-02-24 04:35:19 +01:00
|
|
|
static void _doSetFixedOutputState(ArchiveHandle *AH);
|
2002-08-18 11:36:26 +02:00
|
|
|
static void _doSetSessionAuth(ArchiveHandle *AH, const char *user);
|
2004-03-24 04:06:08 +01:00
|
|
|
static void _doSetWithOids(ArchiveHandle *AH, const bool withOids);
|
2004-09-10 22:05:18 +02:00
|
|
|
static void _reconnectToDB(ArchiveHandle *AH, const char *dbname);
|
2003-09-24 00:48:53 +02:00
|
|
|
static void _becomeUser(ArchiveHandle *AH, const char *user);
|
|
|
|
static void _becomeOwner(ArchiveHandle *AH, TocEntry *te);
|
2002-05-11 00:36:27 +02:00
|
|
|
static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
|
2004-11-06 20:36:02 +01:00
|
|
|
static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
|
2000-07-24 08:24:26 +02:00
|
|
|
|
2005-01-25 23:44:31 +01:00
|
|
|
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls);
|
2001-03-22 05:01:46 +01:00
|
|
|
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
|
|
|
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
|
2003-12-06 04:00:16 +01:00
|
|
|
static TocEntry *getTocEntryByDumpId(ArchiveHandle *AH, DumpId id);
|
2001-03-22 05:01:46 +01:00
|
|
|
static void _moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te);
|
|
|
|
static int _discoverArchiveFormat(ArchiveHandle *AH);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
static void _write_msg(const char *modulename, const char *fmt, va_list ap);
|
|
|
|
static void _die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt, va_list ap);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static int _canRestoreBlobs(ArchiveHandle *AH);
|
|
|
|
static int _restoringToDB(ArchiveHandle *AH);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
|
2000-07-04 16:25:28 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Wrapper functions.
|
|
|
|
*
|
|
|
|
* The objective it to make writing new formats and dumpers as simple
|
|
|
|
* as possible, if necessary at the expense of extra function calls etc.
|
2000-07-04 16:25:28 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* Create a new archive */
|
|
|
|
/* Public */
|
2001-10-25 07:50:21 +02:00
|
|
|
Archive *
|
2001-03-22 05:01:46 +01:00
|
|
|
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
|
|
|
|
const int compression)
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = _allocAH(FileSpec, fmt, compression, archModeWrite);
|
|
|
|
|
|
|
|
return (Archive *) AH;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Open an existing archive */
|
|
|
|
/* Public */
|
2001-10-25 07:50:21 +02:00
|
|
|
Archive *
|
2001-03-22 05:01:46 +01:00
|
|
|
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, archModeRead);
|
|
|
|
|
|
|
|
return (Archive *) AH;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
CloseArchive(Archive *AHX)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int res = 0;
|
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
|
|
|
|
|
|
|
(*AH->ClosePtr) (AH);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Close the output */
|
|
|
|
if (AH->gzOut)
|
2001-01-12 05:32:07 +01:00
|
|
|
res = GZCLOSE(AH->OF);
|
2001-03-22 05:01:46 +01:00
|
|
|
else if (AH->OF != stdout)
|
2001-01-12 05:32:07 +01:00
|
|
|
res = fclose(AH->OF);
|
|
|
|
|
|
|
|
if (res != 0)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "could not close output archive file\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
RestoreArchive(Archive *AHX, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
|
|
|
TocEntry *te = AH->toc->next;
|
2001-11-04 05:05:36 +01:00
|
|
|
teReqs reqs;
|
2001-03-22 05:01:46 +01:00
|
|
|
OutputContext sav;
|
2001-05-12 03:03:59 +02:00
|
|
|
bool defnDumped;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->ropt = ropt;
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->stage = STAGE_INITIALIZING;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-10-23 23:26:44 +02:00
|
|
|
/*
|
|
|
|
* Check for nonsensical option combinations.
|
|
|
|
*
|
|
|
|
* NB: create+dropSchema is useless because if you're creating the DB,
|
|
|
|
* there's no need to drop individual items in it. Moreover, if we
|
2001-10-25 07:50:21 +02:00
|
|
|
* tried to do that then we'd issue the drops in the database
|
|
|
|
* initially connected to, not the one we will create, which is very
|
|
|
|
* bad...
|
2001-10-23 23:26:44 +02:00
|
|
|
*/
|
|
|
|
if (ropt->create && ropt->dropSchema)
|
|
|
|
die_horribly(AH, modulename, "-C and -c are incompatible options\n");
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
|
|
|
* If we're using a DB connection, then connect it.
|
|
|
|
*/
|
|
|
|
if (ropt->useDB)
|
|
|
|
{
|
2001-07-03 22:21:50 +02:00
|
|
|
ahlog(AH, 1, "connecting to database for restore\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->version < K_VERS_1_3)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "direct database connections are not supported in pre-1.3 archives\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-04-25 09:03:20 +02:00
|
|
|
/* XXX Should get this from the archive */
|
|
|
|
AHX->minRemoteVersion = 070100;
|
|
|
|
AHX->maxRemoteVersion = 999999;
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
ConnectDatabase(AHX, ropt->dbname,
|
|
|
|
ropt->pghost, ropt->pgport, ropt->username,
|
2001-03-22 05:01:46 +01:00
|
|
|
ropt->requirePassword, ropt->ignoreVersion);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're talking to the DB directly, don't send comments since
|
|
|
|
* they obscure SQL when displaying errors
|
|
|
|
*/
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->noTocComments = 1;
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
2001-03-06 05:08:04 +01:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Work out if we have an implied data-only restore. This can happen
|
|
|
|
* if the dump was data only or if the user has used a toc list to
|
2001-03-22 05:01:46 +01:00
|
|
|
* exclude all of the schema data. All we do is look for schema
|
|
|
|
* entries - if none are found then we set the dataOnly flag.
|
2001-03-06 05:08:04 +01:00
|
|
|
*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We could scan for wanted TABLE entries, but that is not the same as
|
2001-03-06 05:08:04 +01:00
|
|
|
* dataOnly. At this stage, it seems unnecessary (6-Mar-2001).
|
2001-03-22 05:01:46 +01:00
|
|
|
*/
|
|
|
|
if (!ropt->dataOnly)
|
|
|
|
{
|
2005-01-25 23:44:31 +01:00
|
|
|
int impliedDataOnly = 1;
|
|
|
|
|
|
|
|
for (te = AH->toc->next; te != AH->toc; te = te->next)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2005-01-25 23:44:31 +01:00
|
|
|
reqs = _tocEntryRequired(te, ropt, true);
|
2001-11-04 05:05:36 +01:00
|
|
|
if ((reqs & REQ_SCHEMA) != 0)
|
2001-03-22 05:01:46 +01:00
|
|
|
{ /* It's schema, and it's wanted */
|
2001-03-06 05:08:04 +01:00
|
|
|
impliedDataOnly = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (impliedDataOnly)
|
|
|
|
{
|
|
|
|
ropt->dataOnly = impliedDataOnly;
|
2001-07-03 22:21:50 +02:00
|
|
|
ahlog(AH, 1, "implied data-only restore\n");
|
2001-03-06 05:08:04 +01:00
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2001-03-06 05:08:04 +01:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Setup the output file if necessary.
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
if (ropt->filename || ropt->compression)
|
2000-07-21 13:40:08 +02:00
|
|
|
sav = SetOutput(AH, ropt->filename, ropt->compression);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2002-08-18 11:36:26 +02:00
|
|
|
ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2004-02-24 04:35:19 +01:00
|
|
|
/*
|
|
|
|
* Establish important parameter values right away.
|
|
|
|
*/
|
|
|
|
_doSetFixedOutputState(AH);
|
|
|
|
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->stage = STAGE_PROCESSING;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
|
|
|
* Drop the items at the start, in reverse order
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
if (ropt->dropSchema)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
te = AH->toc->prev;
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->currentTE = te;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
2005-01-25 23:44:31 +01:00
|
|
|
reqs = _tocEntryRequired(te, ropt, false /* needn't drop ACLs */);
|
2001-11-04 05:05:36 +01:00
|
|
|
if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt)
|
2001-07-03 22:21:50 +02:00
|
|
|
{
|
|
|
|
/* We want the schema */
|
2002-07-04 17:35:07 +02:00
|
|
|
ahlog(AH, 1, "dropping %s %s\n", te->desc, te->tag);
|
2002-05-11 00:36:27 +02:00
|
|
|
/* Select owner and schema as necessary */
|
2003-09-24 00:48:53 +02:00
|
|
|
_becomeOwner(AH, te);
|
2002-05-11 00:36:27 +02:00
|
|
|
_selectOutputSchema(AH, te->namespace);
|
2001-01-27 13:35:29 +01:00
|
|
|
/* Drop it */
|
2000-07-21 13:40:08 +02:00
|
|
|
ahprintf(AH, "%s", te->dropStmt);
|
|
|
|
}
|
|
|
|
te = te->prev;
|
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
2005-01-25 23:44:31 +01:00
|
|
|
* Now process each non-ACL TOC entry
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
te = AH->toc->next;
|
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->currentTE = te;
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/* Work out what, if anything, we want from this entry */
|
2004-07-13 05:00:17 +02:00
|
|
|
reqs = _tocEntryRequired(te, ropt, false);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-04-25 09:03:20 +02:00
|
|
|
/* Dump any relevant dump warnings to stderr */
|
|
|
|
if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
if (!ropt->dataOnly && te->defn != NULL && strlen(te->defn) != 0)
|
2001-06-27 23:21:37 +02:00
|
|
|
write_msg(modulename, "warning from original dump file: %s\n", te->defn);
|
|
|
|
else if (te->copyStmt != NULL && strlen(te->copyStmt) != 0)
|
|
|
|
write_msg(modulename, "warning from original dump file: %s\n", te->copyStmt);
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
2001-04-25 09:03:20 +02:00
|
|
|
|
2001-05-12 03:03:59 +02:00
|
|
|
defnDumped = false;
|
|
|
|
|
2001-11-04 05:05:36 +01:00
|
|
|
if ((reqs & REQ_SCHEMA) != 0) /* We want the schema */
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2002-07-04 17:35:07 +02:00
|
|
|
ahlog(AH, 1, "creating %s %s\n", te->desc, te->tag);
|
2004-03-24 04:06:08 +01:00
|
|
|
|
2004-07-13 05:00:17 +02:00
|
|
|
_printTocEntry(AH, te, ropt, false, false);
|
2001-05-12 03:03:59 +02:00
|
|
|
defnDumped = true;
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
/* If we created a DB, connect to it... */
|
2001-03-22 05:01:46 +01:00
|
|
|
if (strcmp(te->desc, "DATABASE") == 0)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
2004-09-10 22:05:18 +02:00
|
|
|
ahlog(AH, 1, "connecting to new database \"%s\"\n", te->tag);
|
|
|
|
_reconnectToDB(AH, te->tag);
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
2001-05-12 03:03:59 +02:00
|
|
|
* If we have a data component, then process it
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-11-04 05:05:36 +01:00
|
|
|
if ((reqs & REQ_DATA) != 0)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
|
|
|
* hadDumper will be set if there is genuine data component
|
|
|
|
* for this node. Otherwise, we need to check the defn field
|
|
|
|
* for statements that need to be executed in data-only
|
|
|
|
* restores.
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-10-25 07:50:21 +02:00
|
|
|
if (te->hadDumper)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
/*
|
2001-05-12 03:03:59 +02:00
|
|
|
* If we can output the data, then restore it.
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->PrintTocDataPtr !=NULL && (reqs & REQ_DATA) != 0)
|
2001-05-12 03:03:59 +02:00
|
|
|
{
|
|
|
|
#ifndef HAVE_LIBZ
|
|
|
|
if (AH->compression != 0)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "cannot restore from compressed archive (not configured for compression support)\n");
|
2001-05-12 03:03:59 +02:00
|
|
|
#endif
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2004-07-13 05:00:17 +02:00
|
|
|
_printTocEntry(AH, te, ropt, true, false);
|
2001-05-12 03:03:59 +02:00
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Maybe we can't do BLOBS, so check if this node is
|
|
|
|
* for BLOBS
|
2001-05-12 03:03:59 +02:00
|
|
|
*/
|
2003-03-09 20:38:52 +01:00
|
|
|
if ((strcmp(te->desc, "BLOBS") == 0) &&
|
|
|
|
!_canRestoreBlobs(AH))
|
2001-05-12 03:03:59 +02:00
|
|
|
{
|
|
|
|
ahprintf(AH, "--\n-- SKIPPED \n--\n\n");
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* This is a bit nasty - we assume, for the
|
|
|
|
* moment, that if a custom output is used, then
|
|
|
|
* we don't want warnings.
|
2001-05-12 03:03:59 +02:00
|
|
|
*/
|
|
|
|
if (!AH->CustomOutPtr)
|
2003-07-23 10:47:41 +02:00
|
|
|
write_msg(modulename, "WARNING: skipping large-object restoration\n");
|
2001-05-12 03:03:59 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_disableTriggersIfNecessary(AH, te, ropt);
|
|
|
|
|
2003-09-24 00:48:53 +02:00
|
|
|
/* Select owner and schema as necessary */
|
|
|
|
_becomeOwner(AH, te);
|
2002-05-11 00:36:27 +02:00
|
|
|
_selectOutputSchema(AH, te->namespace);
|
2001-05-12 03:03:59 +02:00
|
|
|
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 1, "restoring data for table \"%s\"\n", te->tag);
|
2001-05-12 03:03:59 +02:00
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* If we have a copy statement, use it. As of
|
|
|
|
* V1.3, these are separate to allow easy import
|
|
|
|
* from withing a database connection. Pre 1.3
|
|
|
|
* archives can not use DB connections and are
|
|
|
|
* sent to output only.
|
2001-05-12 03:03:59 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* For V1.3+, the table data MUST have a copy
|
|
|
|
* statement so that we can go into appropriate
|
|
|
|
* mode with libpq.
|
2001-05-12 03:03:59 +02:00
|
|
|
*/
|
|
|
|
if (te->copyStmt && strlen(te->copyStmt) > 0)
|
|
|
|
ahprintf(AH, te->copyStmt);
|
|
|
|
|
|
|
|
(*AH->PrintTocDataPtr) (AH, te, ropt);
|
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
/*
|
|
|
|
* If we just restored blobs, fix references in
|
|
|
|
* previously-loaded tables; otherwise, if we
|
|
|
|
* previously restored blobs, fix references in
|
2004-08-29 07:07:03 +02:00
|
|
|
* this table. Note that in standard cases the
|
|
|
|
* BLOBS entry comes after all TABLE DATA entries,
|
|
|
|
* but we should cope with other orders in case
|
|
|
|
* the user demands reordering.
|
2004-03-03 22:28:55 +01:00
|
|
|
*/
|
|
|
|
if (strcmp(te->desc, "BLOBS") == 0)
|
|
|
|
fixPriorBlobRefs(AH, te, ropt);
|
|
|
|
else if (AH->createdBlobXref &&
|
|
|
|
strcmp(te->desc, "TABLE DATA") == 0)
|
|
|
|
{
|
|
|
|
ahlog(AH, 1, "fixing up large-object cross-reference for \"%s\"\n", te->tag);
|
|
|
|
FixupBlobRefs(AH, te);
|
|
|
|
}
|
|
|
|
|
2001-05-12 03:03:59 +02:00
|
|
|
_enableTriggersIfNecessary(AH, te, ropt);
|
|
|
|
}
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
|
|
|
else if (!defnDumped)
|
|
|
|
{
|
|
|
|
/* If we haven't already dumped the defn part, do so now */
|
2002-07-04 17:35:07 +02:00
|
|
|
ahlog(AH, 1, "executing %s %s\n", te->desc, te->tag);
|
2004-07-13 05:00:17 +02:00
|
|
|
_printTocEntry(AH, te, ropt, false, false);
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
te = te->next;
|
2004-08-29 07:07:03 +02:00
|
|
|
} /* end loop over TOC entries */
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2004-07-13 05:00:17 +02:00
|
|
|
/*
|
|
|
|
* Scan TOC again to output ownership commands and ACLs
|
|
|
|
*/
|
|
|
|
te = AH->toc->next;
|
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->currentTE = te;
|
|
|
|
|
2004-07-13 05:00:17 +02:00
|
|
|
/* Work out what, if anything, we want from this entry */
|
|
|
|
reqs = _tocEntryRequired(te, ropt, true);
|
|
|
|
|
|
|
|
if ((reqs & REQ_SCHEMA) != 0) /* We want the schema */
|
|
|
|
{
|
2004-10-22 18:04:35 +02:00
|
|
|
ahlog(AH, 1, "setting owner and privileges for %s %s\n",
|
2004-08-13 23:37:28 +02:00
|
|
|
te->desc, te->tag);
|
2004-07-13 05:00:17 +02:00
|
|
|
_printTocEntry(AH, te, ropt, false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
te = te->next;
|
|
|
|
}
|
|
|
|
|
2005-03-18 18:32:55 +01:00
|
|
|
ahprintf(AH, "--\n-- PostgreSQL database dump complete\n--\n\n");
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
2004-03-03 22:28:55 +01:00
|
|
|
* Clean up & we're done.
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
AH->stage = STAGE_FINALIZING;
|
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
if (ropt->filename || ropt->compression)
|
|
|
|
ResetOutput(AH, sav);
|
|
|
|
|
|
|
|
if (ropt->useDB)
|
|
|
|
{
|
|
|
|
PQfinish(AH->connection);
|
|
|
|
AH->connection = NULL;
|
|
|
|
|
|
|
|
if (AH->blobConnection)
|
|
|
|
{
|
|
|
|
PQfinish(AH->blobConnection);
|
|
|
|
AH->blobConnection = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After restoring BLOBS, fix all blob references in previously-restored
|
2004-08-29 07:07:03 +02:00
|
|
|
* tables. (Normally, the BLOBS entry should appear after all TABLE DATA
|
2004-03-03 22:28:55 +01:00
|
|
|
* entries, so this will in fact handle all blob references.)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fixPriorBlobRefs(ArchiveHandle *AH, TocEntry *blobte, RestoreOptions *ropt)
|
|
|
|
{
|
|
|
|
TocEntry *te;
|
|
|
|
teReqs reqs;
|
|
|
|
|
|
|
|
if (AH->createdBlobXref)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2000-08-05 11:57:46 +02:00
|
|
|
/* NULL parameter means disable ALL user triggers */
|
2001-03-06 05:08:04 +01:00
|
|
|
_disableTriggersIfNecessary(AH, NULL, ropt);
|
2000-08-05 11:57:46 +02:00
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
for (te = AH->toc->next; te != blobte; te = te->next)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
|
|
|
if (strcmp(te->desc, "TABLE DATA") == 0)
|
|
|
|
{
|
2004-07-13 05:00:17 +02:00
|
|
|
reqs = _tocEntryRequired(te, ropt, false);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-11-05 18:46:40 +01:00
|
|
|
if ((reqs & REQ_DATA) != 0) /* We loaded the data */
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 1, "fixing up large-object cross-reference for \"%s\"\n", te->tag);
|
2002-05-11 00:36:27 +02:00
|
|
|
FixupBlobRefs(AH, te);
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-08-05 11:57:46 +02:00
|
|
|
|
|
|
|
/* NULL parameter means enable ALL user triggers */
|
2001-03-06 05:08:04 +01:00
|
|
|
_enableTriggersIfNecessary(AH, NULL, ropt);
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
|
|
|
* Allocate a new RestoreOptions block.
|
|
|
|
* This is mainly so we can initialize it, but also for future expansion,
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
RestoreOptions *
|
|
|
|
NewRestoreOptions(void)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
RestoreOptions *opts;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
opts = (RestoreOptions *) calloc(1, sizeof(RestoreOptions));
|
2000-07-04 16:25:28 +02:00
|
|
|
|
|
|
|
opts->format = archUnknown;
|
2001-04-25 09:03:20 +02:00
|
|
|
opts->suppressDumpWarnings = false;
|
2004-08-20 06:20:23 +02:00
|
|
|
opts->exit_on_error = false;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
/*
|
|
|
|
* Returns true if we're restoring directly to the database (and
|
|
|
|
* aren't just making a psql script that can do the restoration).
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static int
|
|
|
|
_restoringToDB(ArchiveHandle *AH)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
return (AH->ropt->useDB && AH->connection);
|
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static int
|
|
|
|
_canRestoreBlobs(ArchiveHandle *AH)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
|
|
|
return _restoringToDB(AH);
|
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
|
|
|
_disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2002-05-11 00:36:27 +02:00
|
|
|
/* This hack is only needed in a data-only restore */
|
|
|
|
if (!ropt->dataOnly || !ropt->disable_triggers)
|
2000-08-01 17:51:45 +02:00
|
|
|
return;
|
|
|
|
|
2003-03-09 20:38:52 +01:00
|
|
|
/* Don't do it for the BLOBS TocEntry, either */
|
|
|
|
if (te && strcmp(te->desc, "BLOBS") == 0)
|
|
|
|
return;
|
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Become superuser if possible, since they are the only ones who can
|
2004-08-29 07:07:03 +02:00
|
|
|
* update pg_class. If -S was not given, assume the initial user
|
|
|
|
* identity is a superuser.
|
2000-08-01 17:51:45 +02:00
|
|
|
*/
|
2003-09-24 00:48:53 +02:00
|
|
|
_becomeUser(AH, ropt->superuser);
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2001-07-03 22:21:50 +02:00
|
|
|
ahlog(AH, 1, "disabling triggers\n");
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Disable them. This is a hack. Needs to be done via an appropriate
|
|
|
|
* 'SET' command when one is available.
|
2000-08-01 17:51:45 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
ahprintf(AH, "-- Disable triggers\n");
|
2000-08-05 11:57:46 +02:00
|
|
|
|
|
|
|
/*
|
2002-05-29 03:38:56 +02:00
|
|
|
* Just update the AFFECTED table, if known. Otherwise update all
|
|
|
|
* non-system tables.
|
2000-08-05 11:57:46 +02:00
|
|
|
*/
|
2002-07-04 17:35:07 +02:00
|
|
|
if (te && te->tag && strlen(te->tag) > 0)
|
2002-05-29 03:38:56 +02:00
|
|
|
ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 "
|
|
|
|
"WHERE oid = '%s'::pg_catalog.regclass;\n\n",
|
2002-08-18 11:36:26 +02:00
|
|
|
fmtId(te->tag));
|
2000-08-05 11:57:46 +02:00
|
|
|
else
|
2002-05-29 03:38:56 +02:00
|
|
|
ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 FROM pg_catalog.pg_namespace "
|
2002-05-11 00:36:27 +02:00
|
|
|
"WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
|
|
|
_enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2002-05-11 00:36:27 +02:00
|
|
|
/* This hack is only needed in a data-only restore */
|
|
|
|
if (!ropt->dataOnly || !ropt->disable_triggers)
|
2000-08-01 17:51:45 +02:00
|
|
|
return;
|
|
|
|
|
2003-03-09 20:38:52 +01:00
|
|
|
/* Don't do it for the BLOBS TocEntry, either */
|
|
|
|
if (te && strcmp(te->desc, "BLOBS") == 0)
|
|
|
|
return;
|
|
|
|
|
2000-08-01 17:51:45 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Become superuser if possible, since they are the only ones who can
|
2004-08-29 07:07:03 +02:00
|
|
|
* update pg_class. If -S was not given, assume the initial user
|
|
|
|
* identity is a superuser.
|
2000-08-01 17:51:45 +02:00
|
|
|
*/
|
2003-09-24 00:48:53 +02:00
|
|
|
_becomeUser(AH, ropt->superuser);
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2001-07-03 22:21:50 +02:00
|
|
|
ahlog(AH, 1, "enabling triggers\n");
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Enable them. This is a hack. Needs to be done via an appropriate
|
|
|
|
* 'SET' command when one is available.
|
2000-08-01 17:51:45 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
ahprintf(AH, "-- Enable triggers\n");
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
/*
|
2002-05-29 03:38:56 +02:00
|
|
|
* Just update the AFFECTED table, if known. Otherwise update all
|
|
|
|
* non-system tables.
|
2002-05-11 00:36:27 +02:00
|
|
|
*/
|
2002-07-04 17:35:07 +02:00
|
|
|
if (te && te->tag && strlen(te->tag) > 0)
|
2002-05-29 03:38:56 +02:00
|
|
|
ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
|
2002-05-29 03:49:57 +02:00
|
|
|
"(SELECT pg_catalog.count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
|
2002-05-29 03:38:56 +02:00
|
|
|
"WHERE oid = '%s'::pg_catalog.regclass;\n\n",
|
2002-08-18 11:36:26 +02:00
|
|
|
fmtId(te->tag));
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
2002-05-29 03:38:56 +02:00
|
|
|
ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
|
2002-05-29 03:49:57 +02:00
|
|
|
"(SELECT pg_catalog.count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
|
2002-05-29 03:38:56 +02:00
|
|
|
"FROM pg_catalog.pg_namespace "
|
2002-05-11 00:36:27 +02:00
|
|
|
"WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
|
|
|
/*
|
2000-07-21 13:40:08 +02:00
|
|
|
* This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
|
2000-07-04 16:25:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Public */
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t
|
|
|
|
WriteData(Archive *AHX, const void *data, size_t dLen)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!AH->currToc)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "internal error -- WriteData cannot be called outside the context of a DataDumper routine\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return (*AH->WriteDataPtr) (AH, data, dLen);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Create a new TOC entry. The TOC was designed as a TOC, but is now the
|
2000-07-04 16:25:28 +02:00
|
|
|
* repository for all metadata. But the name has stuck.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2003-12-06 04:00:16 +01:00
|
|
|
ArchiveEntry(Archive *AHX,
|
|
|
|
CatalogId catalogId, DumpId dumpId,
|
|
|
|
const char *tag,
|
2004-11-06 20:36:02 +01:00
|
|
|
const char *namespace,
|
|
|
|
const char *tablespace,
|
|
|
|
const char *owner, bool withOids,
|
2003-12-06 04:00:16 +01:00
|
|
|
const char *desc, const char *defn,
|
|
|
|
const char *dropStmt, const char *copyStmt,
|
|
|
|
const DumpId *deps, int nDeps,
|
2001-03-22 05:01:46 +01:00
|
|
|
DataDumperPtr dumpFn, void *dumpArg)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
|
|
|
TocEntry *newToc;
|
|
|
|
|
|
|
|
newToc = (TocEntry *) calloc(1, sizeof(TocEntry));
|
|
|
|
if (!newToc)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
AH->tocCount++;
|
|
|
|
if (dumpId > AH->maxDumpId)
|
|
|
|
AH->maxDumpId = dumpId;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
newToc->prev = AH->toc->prev;
|
|
|
|
newToc->next = AH->toc;
|
|
|
|
AH->toc->prev->next = newToc;
|
|
|
|
AH->toc->prev = newToc;
|
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
newToc->catalogId = catalogId;
|
|
|
|
newToc->dumpId = dumpId;
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2002-07-04 17:35:07 +02:00
|
|
|
newToc->tag = strdup(tag);
|
2002-05-11 00:36:27 +02:00
|
|
|
newToc->namespace = namespace ? strdup(namespace) : NULL;
|
2004-11-06 20:36:02 +01:00
|
|
|
newToc->tablespace = tablespace ? strdup(tablespace) : NULL;
|
2002-05-11 00:36:27 +02:00
|
|
|
newToc->owner = strdup(owner);
|
2004-03-24 04:06:08 +01:00
|
|
|
newToc->withOids = withOids;
|
2001-03-22 05:01:46 +01:00
|
|
|
newToc->desc = strdup(desc);
|
2002-05-11 00:36:27 +02:00
|
|
|
newToc->defn = strdup(defn);
|
|
|
|
newToc->dropStmt = strdup(dropStmt);
|
|
|
|
newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
if (nDeps > 0)
|
|
|
|
{
|
|
|
|
newToc->dependencies = (DumpId *) malloc(nDeps * sizeof(DumpId));
|
|
|
|
memcpy(newToc->dependencies, deps, nDeps * sizeof(DumpId));
|
|
|
|
newToc->nDeps = nDeps;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newToc->dependencies = NULL;
|
|
|
|
newToc->nDeps = 0;
|
|
|
|
}
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
newToc->dataDumper = dumpFn;
|
|
|
|
newToc->dataDumperArg = dumpArg;
|
2003-12-06 04:00:16 +01:00
|
|
|
newToc->hadDumper = dumpFn ? true : false;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
newToc->formatData = NULL;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->ArchiveEntryPtr !=NULL)
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->ArchiveEntryPtr) (AH, newToc);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
|
|
|
TocEntry *te = AH->toc->next;
|
|
|
|
OutputContext sav;
|
|
|
|
char *fmtName;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (ropt->filename)
|
2004-08-29 07:07:03 +02:00
|
|
|
sav = SetOutput(AH, ropt->filename, 0 /* no compression */ );
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
ahprintf(AH, ";\n; Archive created at %s", ctime(&AH->createDate));
|
|
|
|
ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %d\n",
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->archdbname, AH->tocCount, AH->compression);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
switch (AH->format)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
case archFiles:
|
|
|
|
fmtName = "FILES";
|
|
|
|
break;
|
|
|
|
case archCustom:
|
|
|
|
fmtName = "CUSTOM";
|
|
|
|
break;
|
|
|
|
case archTar:
|
|
|
|
fmtName = "TAR";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fmtName = "UNKNOWN";
|
|
|
|
}
|
2000-08-01 17:51:45 +02:00
|
|
|
|
|
|
|
ahprintf(AH, "; Dump Version: %d.%d-%d\n", AH->vmaj, AH->vmin, AH->vrev);
|
2002-10-22 21:15:23 +02:00
|
|
|
ahprintf(AH, "; Format: %s\n", fmtName);
|
2002-10-27 03:52:10 +01:00
|
|
|
ahprintf(AH, "; Integer: %d bytes\n", (int) AH->intSize);
|
|
|
|
ahprintf(AH, "; Offset: %d bytes\n", (int) AH->offSize);
|
2004-11-06 20:36:02 +01:00
|
|
|
if (AH->archiveRemoteVersion)
|
|
|
|
ahprintf(AH, "; Dumped from database version: %s\n",
|
|
|
|
AH->archiveRemoteVersion);
|
|
|
|
if (AH->archiveDumpVersion)
|
|
|
|
ahprintf(AH, "; Dumped by pg_dump version: %s\n",
|
|
|
|
AH->archiveDumpVersion);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
ahprintf(AH, ";\n;\n; Selected TOC Entries:\n;\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
2005-01-25 23:44:31 +01:00
|
|
|
if (_tocEntryRequired(te, ropt, true) != 0)
|
2004-10-08 17:03:26 +02:00
|
|
|
ahprintf(AH, "%d; %u %u %s %s %s %s\n", te->dumpId,
|
2003-12-06 04:00:16 +01:00
|
|
|
te->catalogId.tableoid, te->catalogId.oid,
|
2004-10-08 17:03:26 +02:00
|
|
|
te->desc, te->namespace ? te->namespace : "-",
|
|
|
|
te->tag, te->owner);
|
2000-07-04 16:25:28 +02:00
|
|
|
te = te->next;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (ropt->filename)
|
|
|
|
ResetOutput(AH, sav);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/***********
|
|
|
|
* BLOB Archival
|
|
|
|
***********/
|
|
|
|
|
|
|
|
/* Called by a dumper to signal start of a BLOB */
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
2001-04-01 07:42:51 +02:00
|
|
|
StartBlob(Archive *AHX, Oid oid)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!AH->StartBlobPtr)
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "large-object output not supported in chosen format\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->StartBlobPtr) (AH, AH->currToc, oid);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return 1;
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by a dumper to signal end of a BLOB */
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
2001-04-01 07:42:51 +02:00
|
|
|
EndBlob(Archive *AHX, Oid oid)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->EndBlobPtr)
|
|
|
|
(*AH->EndBlobPtr) (AH, AH->currToc, oid);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return 1;
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********
|
|
|
|
* BLOB Restoration
|
|
|
|
**********/
|
|
|
|
|
2000-10-31 15:20:30 +01:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Called by a format handler before any blobs are restored
|
2000-10-31 15:20:30 +01:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
StartRestoreBlobs(ArchiveHandle *AH)
|
2000-10-31 15:20:30 +01:00
|
|
|
{
|
|
|
|
AH->blobCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Called by a format handler after all blobs are restored
|
2000-10-31 15:20:30 +01:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
EndRestoreBlobs(ArchiveHandle *AH)
|
2000-10-31 15:20:30 +01:00
|
|
|
{
|
|
|
|
if (AH->txActive)
|
|
|
|
{
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 2, "committing large-object transactions\n");
|
2000-10-31 15:20:30 +01:00
|
|
|
CommitTransaction(AH);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AH->blobTxActive)
|
|
|
|
CommitTransactionXref(AH);
|
|
|
|
|
2004-03-03 22:28:55 +01:00
|
|
|
if (AH->createdBlobXref)
|
|
|
|
CreateBlobXrefIndex(AH);
|
|
|
|
|
2001-09-21 23:58:30 +02:00
|
|
|
ahlog(AH, 1, "restored %d large objects\n", AH->blobCount);
|
2000-10-31 15:20:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
|
|
|
* Called by a format handler to initiate restoration of a blob
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2001-04-01 07:42:51 +02:00
|
|
|
StartRestoreBlob(ArchiveHandle *AH, Oid oid)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-07-03 22:21:50 +02:00
|
|
|
Oid loOid;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2000-10-31 15:20:30 +01:00
|
|
|
AH->blobCount++;
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!AH->createdBlobXref)
|
|
|
|
{
|
|
|
|
if (!AH->connection)
|
2001-09-21 23:58:30 +02:00
|
|
|
die_horribly(AH, modulename, "cannot restore large objects without a database connection\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
CreateBlobXrefTable(AH);
|
|
|
|
AH->createdBlobXref = 1;
|
|
|
|
}
|
|
|
|
|
2002-04-24 04:21:04 +02:00
|
|
|
/* Initialize the LO Buffer */
|
|
|
|
AH->lo_buf_used = 0;
|
|
|
|
|
2000-10-31 15:20:30 +01:00
|
|
|
/*
|
|
|
|
* Start long-running TXs if necessary
|
|
|
|
*/
|
|
|
|
if (!AH->txActive)
|
|
|
|
{
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 2, "starting large-object transactions\n");
|
2000-10-31 15:20:30 +01:00
|
|
|
StartTransaction(AH);
|
|
|
|
}
|
|
|
|
if (!AH->blobTxActive)
|
|
|
|
StartTransactionXref(AH);
|
2000-08-01 17:51:45 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
|
|
|
|
if (loOid == 0)
|
2001-09-21 23:58:30 +02:00
|
|
|
die_horribly(AH, modulename, "could not create large object\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 2, "restoring large object with OID %u as %u\n", oid, loOid);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
InsertBlobXref(AH, oid, loOid);
|
|
|
|
|
|
|
|
AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
|
|
|
|
if (AH->loFd == -1)
|
2001-09-21 23:58:30 +02:00
|
|
|
die_horribly(AH, modulename, "could not open large object\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->writingBlob = 1;
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2001-04-01 07:42:51 +02:00
|
|
|
EndRestoreBlob(ArchiveHandle *AH, Oid oid)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2002-05-29 03:38:56 +02:00
|
|
|
if (AH->lo_buf_used > 0)
|
|
|
|
{
|
|
|
|
/* Write remaining bytes from the LO buffer */
|
2002-09-04 22:31:48 +02:00
|
|
|
size_t res;
|
2002-05-29 03:38:56 +02:00
|
|
|
|
|
|
|
res = lo_write(AH->connection, AH->loFd, (void *) AH->lo_buf, AH->lo_buf_used);
|
|
|
|
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 5, "wrote remaining %lu bytes of large-object data (result = %lu)\n",
|
2002-08-20 19:54:45 +02:00
|
|
|
(unsigned long) AH->lo_buf_used, (unsigned long) res);
|
2002-05-29 03:38:56 +02:00
|
|
|
if (res != AH->lo_buf_used)
|
2002-08-20 19:54:45 +02:00
|
|
|
die_horribly(AH, modulename, "could not write to large object (result: %lu, expected: %lu)\n",
|
2002-09-04 22:31:48 +02:00
|
|
|
(unsigned long) res, (unsigned long) AH->lo_buf_used);
|
2002-05-29 03:38:56 +02:00
|
|
|
AH->lo_buf_used = 0;
|
|
|
|
}
|
2002-04-24 04:21:04 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
lo_close(AH->connection, AH->loFd);
|
|
|
|
AH->writingBlob = 0;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2000-10-31 15:20:30 +01:00
|
|
|
/*
|
|
|
|
* Commit every BLOB_BATCH_SIZE blobs...
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
if (((AH->blobCount / BLOB_BATCH_SIZE) * BLOB_BATCH_SIZE) == AH->blobCount)
|
2000-10-31 15:20:30 +01:00
|
|
|
{
|
2003-07-23 10:47:41 +02:00
|
|
|
ahlog(AH, 2, "committing large-object transactions\n");
|
2000-10-31 15:20:30 +01:00
|
|
|
CommitTransaction(AH);
|
|
|
|
CommitTransactionXref(AH);
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
2000-07-04 16:25:28 +02:00
|
|
|
/***********
|
|
|
|
* Sorting and Reordering
|
|
|
|
***********/
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH = (ArchiveHandle *) AHX;
|
|
|
|
FILE *fh;
|
|
|
|
char buf[1024];
|
|
|
|
char *cmnt;
|
|
|
|
char *endptr;
|
2003-12-06 04:00:16 +01:00
|
|
|
DumpId id;
|
2001-03-22 05:01:46 +01:00
|
|
|
TocEntry *te;
|
|
|
|
TocEntry *tePrev;
|
|
|
|
|
|
|
|
/* Allocate space for the 'wanted' array, and init it */
|
2003-12-06 04:00:16 +01:00
|
|
|
ropt->idWanted = (bool *) malloc(sizeof(bool) * AH->maxDumpId);
|
|
|
|
memset(ropt->idWanted, 0, sizeof(bool) * AH->maxDumpId);
|
|
|
|
ropt->limitToList = true;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Set prev entry as head of list */
|
|
|
|
tePrev = AH->toc;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Setup the file */
|
|
|
|
fh = fopen(ropt->tocFile, PG_BINARY_R);
|
|
|
|
if (!fh)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not open TOC file\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
while (fgets(buf, 1024, fh) != NULL)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Find a comment */
|
|
|
|
cmnt = strchr(buf, ';');
|
|
|
|
if (cmnt == buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* End string at comment */
|
|
|
|
if (cmnt != NULL)
|
|
|
|
cmnt[0] = '\0';
|
|
|
|
|
|
|
|
/* Skip if all spaces */
|
|
|
|
if (strspn(buf, " \t") == strlen(buf))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Get an ID */
|
|
|
|
id = strtol(buf, &endptr, 10);
|
2003-12-06 04:00:16 +01:00
|
|
|
if (endptr == buf || id <= 0 || id > AH->maxDumpId)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2001-06-27 23:21:37 +02:00
|
|
|
write_msg(modulename, "WARNING: line ignored: %s\n", buf);
|
2001-03-22 05:01:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Find TOC entry */
|
2003-12-06 04:00:16 +01:00
|
|
|
te = getTocEntryByDumpId(AH, id);
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!te)
|
2003-12-06 04:00:16 +01:00
|
|
|
die_horribly(AH, modulename, "could not find entry for ID %d\n",
|
|
|
|
id);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
ropt->idWanted[id - 1] = true;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
_moveAfter(AH, tePrev, te);
|
|
|
|
tePrev = te;
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (fclose(fh) != 0)
|
2003-12-06 04:00:16 +01:00
|
|
|
die_horribly(AH, modulename, "could not close TOC file: %s\n",
|
|
|
|
strerror(errno));
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* 'Convenience functions that look like standard IO functions
|
|
|
|
* for writing data when in dump mode.
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
archputs(const char *s, Archive *AH)
|
|
|
|
{
|
|
|
|
return WriteData(AH, s, strlen(s));
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Public */
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
archprintf(Archive *AH, const char *fmt,...)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
char *p = NULL;
|
|
|
|
va_list ap;
|
|
|
|
int bSize = strlen(fmt) + 256;
|
|
|
|
int cnt = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is paranoid: deal with the possibility that vsnprintf is
|
2004-08-29 07:07:03 +02:00
|
|
|
* willing to ignore trailing null or returns > 0 even if string does
|
|
|
|
* not fit. It may be the case that it returns cnt = bufsize
|
2001-03-22 05:01:46 +01:00
|
|
|
*/
|
|
|
|
while (cnt < 0 || cnt >= (bSize - 1))
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
if (p != NULL)
|
|
|
|
free(p);
|
2001-02-23 23:52:32 +01:00
|
|
|
bSize *= 2;
|
2001-03-22 05:01:46 +01:00
|
|
|
p = (char *) malloc(bSize);
|
2001-02-23 23:52:32 +01:00
|
|
|
if (p == NULL)
|
2001-06-27 23:21:37 +02:00
|
|
|
exit_horribly(AH, modulename, "out of memory\n");
|
2001-02-23 23:52:32 +01:00
|
|
|
va_start(ap, fmt);
|
|
|
|
cnt = vsnprintf(p, bSize, fmt, ap);
|
|
|
|
va_end(ap);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
WriteData(AH, p, cnt);
|
|
|
|
free(p);
|
|
|
|
return cnt;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************
|
|
|
|
* Stuff below here should be 'private' to the archiver routines
|
|
|
|
*******************************/
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
OutputContext
|
|
|
|
SetOutput(ArchiveHandle *AH, char *filename, int compression)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
OutputContext sav;
|
2003-10-20 23:05:12 +02:00
|
|
|
int fn;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
|
|
|
/* Replace the AH output file handle */
|
|
|
|
sav.OF = AH->OF;
|
|
|
|
sav.gzOut = AH->gzOut;
|
|
|
|
|
|
|
|
if (filename)
|
2003-10-20 23:05:12 +02:00
|
|
|
fn = -1;
|
2001-03-22 05:01:46 +01:00
|
|
|
else if (AH->FH)
|
|
|
|
fn = fileno(AH->FH);
|
|
|
|
else if (AH->fSpec)
|
|
|
|
{
|
2003-10-20 23:05:12 +02:00
|
|
|
fn = -1;
|
2001-03-22 05:01:46 +01:00
|
|
|
filename = AH->fSpec;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fn = fileno(stdout);
|
|
|
|
|
|
|
|
/* If compression explicitly requested, use gzopen */
|
2000-07-06 20:39:39 +02:00
|
|
|
#ifdef HAVE_LIBZ
|
2001-03-22 05:01:46 +01:00
|
|
|
if (compression != 0)
|
|
|
|
{
|
2003-10-20 23:05:12 +02:00
|
|
|
char fmode[10];
|
|
|
|
|
|
|
|
/* Don't use PG_BINARY_x since this is zlib */
|
2001-01-12 05:32:07 +01:00
|
|
|
sprintf(fmode, "wb%d", compression);
|
2003-10-20 23:05:12 +02:00
|
|
|
if (fn >= 0)
|
|
|
|
AH->OF = gzdopen(dup(fn), fmode);
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
|
|
|
AH->OF = gzopen(filename, fmode);
|
2001-01-12 05:32:07 +01:00
|
|
|
AH->gzOut = 1;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
2000-07-04 16:25:28 +02:00
|
|
|
#endif
|
2003-10-20 23:05:12 +02:00
|
|
|
{ /* Use fopen */
|
|
|
|
if (fn >= 0)
|
2001-01-12 05:32:07 +01:00
|
|
|
AH->OF = fdopen(dup(fn), PG_BINARY_W);
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
2001-01-12 05:32:07 +01:00
|
|
|
AH->OF = fopen(filename, PG_BINARY_W);
|
|
|
|
AH->gzOut = 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-01-12 05:32:07 +01:00
|
|
|
if (!AH->OF)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not open output file: %s\n", strerror(errno));
|
2001-01-12 05:32:07 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return sav;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
ResetOutput(ArchiveHandle *AH, OutputContext sav)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int res;
|
2001-01-12 05:32:07 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->gzOut)
|
2001-01-12 05:32:07 +01:00
|
|
|
res = GZCLOSE(AH->OF);
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
2001-01-12 05:32:07 +01:00
|
|
|
res = fclose(AH->OF);
|
|
|
|
|
|
|
|
if (res != 0)
|
2003-10-20 23:05:12 +02:00
|
|
|
die_horribly(AH, modulename, "could not close output file: %s\n",
|
|
|
|
strerror(errno));
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->gzOut = sav.gzOut;
|
|
|
|
AH->OF = sav.OF;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Print formatted text to the output file (usually stdout).
|
2000-07-04 16:25:28 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
ahprintf(ArchiveHandle *AH, const char *fmt,...)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
char *p = NULL;
|
|
|
|
va_list ap;
|
|
|
|
int bSize = strlen(fmt) + 256; /* Should be enough */
|
|
|
|
int cnt = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is paranoid: deal with the possibility that vsnprintf is
|
|
|
|
* willing to ignore trailing null
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* or returns > 0 even if string does not fit. It may be the case that
|
|
|
|
* it returns cnt = bufsize
|
|
|
|
*/
|
|
|
|
while (cnt < 0 || cnt >= (bSize - 1))
|
2001-02-23 23:52:32 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
if (p != NULL)
|
|
|
|
free(p);
|
2000-07-21 13:40:08 +02:00
|
|
|
bSize *= 2;
|
2001-03-22 05:01:46 +01:00
|
|
|
p = (char *) malloc(bSize);
|
2000-07-21 13:40:08 +02:00
|
|
|
if (p == NULL)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2001-02-23 23:52:32 +01:00
|
|
|
va_start(ap, fmt);
|
2000-07-21 13:40:08 +02:00
|
|
|
cnt = vsnprintf(p, bSize, fmt, ap);
|
2001-02-23 23:52:32 +01:00
|
|
|
va_end(ap);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
ahwrite(p, 1, cnt, AH);
|
|
|
|
free(p);
|
|
|
|
return cnt;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
ahlog(ArchiveHandle *AH, int level, const char *fmt,...)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (AH->debugLevel < level && (!AH->public.verbose || level > 1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2001-07-03 22:21:50 +02:00
|
|
|
_write_msg(NULL, fmt, ap);
|
2000-07-21 13:40:08 +02:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2000-07-24 08:24:26 +02:00
|
|
|
/*
|
|
|
|
* Single place for logic which says 'We are restoring to a direct DB connection'.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
RestoringToDB(ArchiveHandle *AH)
|
2000-07-24 08:24:26 +02:00
|
|
|
{
|
|
|
|
return (AH->ropt && AH->ropt->useDB && AH->connection);
|
|
|
|
}
|
|
|
|
|
2000-07-04 16:25:28 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Write buffer to the output file (usually stdout). This is user for
|
|
|
|
* outputting 'restore' scripts etc. It is even possible for an archive
|
|
|
|
* format to create a custom output routine to 'fake' a restore if it
|
2000-07-21 13:40:08 +02:00
|
|
|
* wants to generate a script (see TAR output).
|
2000-07-04 16:25:28 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t res;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->writingBlob)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2002-08-20 19:54:45 +02:00
|
|
|
if (AH->lo_buf_used + size * nmemb > AH->lo_buf_size)
|
|
|
|
{
|
|
|
|
/* Split LO buffer */
|
2002-09-04 22:31:48 +02:00
|
|
|
size_t remaining = AH->lo_buf_size - AH->lo_buf_used;
|
|
|
|
size_t slack = nmemb * size - remaining;
|
2002-08-20 19:54:45 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
memcpy((char *) AH->lo_buf + AH->lo_buf_used, ptr, remaining);
|
2002-08-20 19:54:45 +02:00
|
|
|
res = lo_write(AH->connection, AH->loFd, AH->lo_buf, AH->lo_buf_size);
|
|
|
|
ahlog(AH, 5, "wrote %lu bytes of large object data (result = %lu)\n",
|
|
|
|
(unsigned long) AH->lo_buf_size, (unsigned long) res);
|
|
|
|
if (res != AH->lo_buf_size)
|
|
|
|
die_horribly(AH, modulename,
|
|
|
|
"could not write to large object (result: %lu, expected: %lu)\n",
|
2002-09-04 22:31:48 +02:00
|
|
|
(unsigned long) res, (unsigned long) AH->lo_buf_size);
|
|
|
|
memcpy(AH->lo_buf, (char *) ptr + remaining, slack);
|
2002-08-20 19:54:45 +02:00
|
|
|
AH->lo_buf_used = slack;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* LO Buffer is still large enough, buffer it */
|
2002-09-04 22:31:48 +02:00
|
|
|
memcpy((char *) AH->lo_buf + AH->lo_buf_used, ptr, size * nmemb);
|
2002-08-20 19:54:45 +02:00
|
|
|
AH->lo_buf_used += size * nmemb;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size * nmemb;
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
else if (AH->gzOut)
|
2001-01-12 05:32:07 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
res = GZWRITE((void *) ptr, size, nmemb, AH->OF);
|
2001-01-12 05:32:07 +01:00
|
|
|
if (res != (nmemb * size))
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not write to compressed archive\n");
|
2001-01-12 05:32:07 +01:00
|
|
|
return res;
|
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
else if (AH->CustomOutPtr)
|
2001-01-12 05:32:07 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
res = AH->CustomOutPtr (AH, ptr, size * nmemb);
|
|
|
|
|
2001-01-12 05:32:07 +01:00
|
|
|
if (res != (nmemb * size))
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not write to custom output routine\n");
|
2001-01-12 05:32:07 +01:00
|
|
|
return res;
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* If we're doing a restore, and it's direct to DB, and we're
|
|
|
|
* connected then send it to the DB.
|
|
|
|
*/
|
2000-07-24 08:24:26 +02:00
|
|
|
if (RestoringToDB(AH))
|
2001-03-22 05:01:46 +01:00
|
|
|
return ExecuteSqlCommandBuf(AH, (void *) ptr, size * nmemb); /* Always 1, currently */
|
2000-07-21 13:40:08 +02:00
|
|
|
else
|
2001-01-12 05:32:07 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
res = fwrite((void *) ptr, size, nmemb, AH->OF);
|
2001-01-12 05:32:07 +01:00
|
|
|
if (res != nmemb)
|
2002-08-20 19:54:45 +02:00
|
|
|
die_horribly(AH, modulename, "could not write to output file (%lu != %lu)\n",
|
|
|
|
(unsigned long) res, (unsigned long) nmemb);
|
2001-01-12 05:32:07 +01:00
|
|
|
return res;
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
/* Common exit code */
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
2001-06-27 23:21:37 +02:00
|
|
|
_write_msg(const char *modulename, const char *fmt, va_list ap)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-06-27 23:21:37 +02:00
|
|
|
if (modulename)
|
2005-02-22 05:43:23 +01:00
|
|
|
fprintf(stderr, "%s: [%s] ", progname, _(modulename));
|
2001-06-27 23:21:37 +02:00
|
|
|
else
|
|
|
|
fprintf(stderr, "%s: ", progname);
|
2005-02-22 05:43:23 +01:00
|
|
|
vfprintf(stderr, _(fmt), ap);
|
2001-06-27 23:21:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2001-10-25 07:50:21 +02:00
|
|
|
write_msg(const char *modulename, const char *fmt,...)
|
2001-06-27 23:21:37 +02:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
_write_msg(modulename, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
_die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
_write_msg(modulename, fmt, ap);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (AH)
|
|
|
|
{
|
2003-01-27 01:23:38 +01:00
|
|
|
if (AH->public.verbose)
|
|
|
|
write_msg(NULL, "*** aborted because of error\n");
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->connection)
|
|
|
|
PQfinish(AH->connection);
|
2003-01-27 01:23:38 +01:00
|
|
|
if (AH->blobConnection)
|
|
|
|
PQfinish(AH->blobConnection);
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
exit(1);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/* External use */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2001-06-27 23:21:37 +02:00
|
|
|
exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
va_list ap;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
va_start(ap, fmt);
|
2001-06-27 23:21:37 +02:00
|
|
|
_die_horribly((ArchiveHandle *) AH, modulename, fmt, ap);
|
2003-12-06 04:00:16 +01:00
|
|
|
va_end(ap);
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/* Archiver use (just different arg declaration) */
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
va_list ap;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
va_start(ap, fmt);
|
2001-06-27 23:21:37 +02:00
|
|
|
_die_horribly(AH, modulename, fmt, ap);
|
2003-12-06 04:00:16 +01:00
|
|
|
va_end(ap);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2004-04-22 04:39:10 +02:00
|
|
|
/* on some error, we may decide to go on... */
|
|
|
|
void
|
2004-08-29 07:07:03 +02:00
|
|
|
warn_or_die_horribly(ArchiveHandle *AH,
|
|
|
|
const char *modulename, const char *fmt,...)
|
2004-04-22 04:39:10 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
va_list ap;
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
switch (AH->stage)
|
|
|
|
{
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
|
|
|
|
case STAGE_NONE:
|
|
|
|
/* Do nothing special */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STAGE_INITIALIZING:
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->stage != AH->lastErrorStage)
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
write_msg(modulename, "Error while INITIALIZING:\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STAGE_PROCESSING:
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->stage != AH->lastErrorStage)
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
write_msg(modulename, "Error while PROCESSING TOC:\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STAGE_FINALIZING:
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->stage != AH->lastErrorStage)
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
write_msg(modulename, "Error while FINALIZING:\n");
|
|
|
|
break;
|
|
|
|
}
|
2004-08-29 07:07:03 +02:00
|
|
|
if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
|
|
|
|
{
|
2004-10-22 18:04:35 +02:00
|
|
|
write_msg(modulename, "Error from TOC entry %d; %u %u %s %s %s\n", AH->currentTE->dumpId,
|
2004-08-29 07:07:03 +02:00
|
|
|
AH->currentTE->catalogId.tableoid, AH->currentTE->catalogId.oid,
|
|
|
|
AH->currentTE->desc, AH->currentTE->tag, AH->currentTE->owner);
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
}
|
|
|
|
AH->lastErrorStage = AH->stage;
|
|
|
|
AH->lastErrorTE = AH->currentTE;
|
|
|
|
|
2004-04-22 04:39:10 +02:00
|
|
|
va_start(ap, fmt);
|
2004-08-20 06:20:23 +02:00
|
|
|
if (AH->public.exit_on_error)
|
2004-04-22 04:39:10 +02:00
|
|
|
_die_horribly(AH, modulename, fmt, ap);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_write_msg(modulename, fmt, ap);
|
|
|
|
AH->public.n_errors++;
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
|
|
|
_moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
te->prev->next = te->next;
|
|
|
|
te->next->prev = te->prev;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
te->prev = pos;
|
|
|
|
te->next = pos->next;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
pos->next->prev = te;
|
|
|
|
pos->next = te;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
#ifdef NOT_USED
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
|
|
|
_moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
te->prev->next = te->next;
|
|
|
|
te->next->prev = te->prev;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
te->prev = pos->prev;
|
|
|
|
te->next = pos;
|
|
|
|
pos->prev->next = te;
|
|
|
|
pos->prev = te;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
2003-12-06 04:00:16 +01:00
|
|
|
#endif
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
static TocEntry *
|
2003-12-06 04:00:16 +01:00
|
|
|
getTocEntryByDumpId(ArchiveHandle *AH, DumpId id)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
TocEntry *te;
|
|
|
|
|
|
|
|
te = AH->toc->next;
|
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
if (te->dumpId == id)
|
2001-03-22 05:01:46 +01:00
|
|
|
return te;
|
|
|
|
te = te->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2005-01-25 23:44:31 +01:00
|
|
|
teReqs
|
2003-12-06 04:00:16 +01:00
|
|
|
TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
TocEntry *te = getTocEntryByDumpId(AH, id);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!te)
|
|
|
|
return 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2005-01-25 23:44:31 +01:00
|
|
|
return _tocEntryRequired(te, ropt, true);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
size_t
|
|
|
|
WriteOffset(ArchiveHandle *AH, off_t o, int wasSet)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
int off;
|
2002-10-22 21:15:23 +02:00
|
|
|
|
|
|
|
/* Save the flag */
|
|
|
|
(*AH->WriteBytePtr) (AH, wasSet);
|
|
|
|
|
|
|
|
/* Write out off_t smallest byte first, prevents endian mismatch */
|
|
|
|
for (off = 0; off < sizeof(off_t); off++)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
(*AH->WriteBytePtr) (AH, o & 0xFF);
|
2002-10-22 21:15:23 +02:00
|
|
|
o >>= 8;
|
|
|
|
}
|
|
|
|
return sizeof(off_t) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ReadOffset(ArchiveHandle *AH, off_t *o)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
int i;
|
|
|
|
int off;
|
|
|
|
int offsetFlg;
|
2002-10-22 21:15:23 +02:00
|
|
|
|
|
|
|
/* Initialize to zero */
|
|
|
|
*o = 0;
|
|
|
|
|
|
|
|
/* Check for old version */
|
|
|
|
if (AH->version < K_VERS_1_7)
|
|
|
|
{
|
|
|
|
/* Prior versions wrote offsets using WriteInt */
|
|
|
|
i = ReadInt(AH);
|
|
|
|
/* -1 means not set */
|
|
|
|
if (i < 0)
|
2003-08-04 02:43:34 +02:00
|
|
|
return K_OFFSET_POS_NOT_SET;
|
2002-10-22 21:15:23 +02:00
|
|
|
else if (i == 0)
|
2003-08-04 02:43:34 +02:00
|
|
|
return K_OFFSET_NO_DATA;
|
2002-10-22 21:15:23 +02:00
|
|
|
|
|
|
|
/* Cast to off_t because it was written as an int. */
|
2003-08-04 02:43:34 +02:00
|
|
|
*o = (off_t) i;
|
2002-10-22 21:15:23 +02:00
|
|
|
return K_OFFSET_POS_SET;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Read the flag indicating the state of the data pointer. Check if
|
|
|
|
* valid and die if not.
|
2002-10-22 21:15:23 +02:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* This used to be handled by a negative or zero pointer, now we use an
|
|
|
|
* extra byte specifically for the state.
|
2002-10-22 21:15:23 +02:00
|
|
|
*/
|
|
|
|
offsetFlg = (*AH->ReadBytePtr) (AH) & 0xFF;
|
|
|
|
|
|
|
|
switch (offsetFlg)
|
|
|
|
{
|
|
|
|
case K_OFFSET_POS_NOT_SET:
|
|
|
|
case K_OFFSET_NO_DATA:
|
|
|
|
case K_OFFSET_POS_SET:
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
break;
|
2002-10-22 21:15:23 +02:00
|
|
|
|
|
|
|
default:
|
2003-08-04 02:43:34 +02:00
|
|
|
die_horribly(AH, modulename, "Unexpected data offset flag %d\n", offsetFlg);
|
2002-10-22 21:15:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the bytes
|
|
|
|
*/
|
|
|
|
for (off = 0; off < AH->offSize; off++)
|
|
|
|
{
|
|
|
|
if (off < sizeof(off_t))
|
2004-01-04 05:02:15 +01:00
|
|
|
*o |= ((off_t) ((*AH->ReadBytePtr) (AH))) << (off * 8);
|
2002-10-22 21:15:23 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((*AH->ReadBytePtr) (AH) != 0)
|
2003-08-04 02:43:34 +02:00
|
|
|
die_horribly(AH, modulename, "file offset in dump file is too large\n");
|
2002-10-22 21:15:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return offsetFlg;
|
|
|
|
}
|
|
|
|
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t
|
2001-03-22 05:01:46 +01:00
|
|
|
WriteInt(ArchiveHandle *AH, int i)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int b;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a bit yucky, but I don't want to make the binary format
|
2002-11-23 04:59:09 +01:00
|
|
|
* very dependent on representation, and not knowing much about it, I
|
2001-03-22 05:01:46 +01:00
|
|
|
* write out a sign byte. If you change this, don't forget to change
|
|
|
|
* the file version #, and modify readInt to read the new format AS
|
|
|
|
* WELL AS the old formats.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* SIGN byte */
|
|
|
|
if (i < 0)
|
|
|
|
{
|
|
|
|
(*AH->WriteBytePtr) (AH, 1);
|
2000-07-21 13:40:08 +02:00
|
|
|
i = -i;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
(*AH->WriteBytePtr) (AH, 0);
|
|
|
|
|
|
|
|
for (b = 0; b < AH->intSize; b++)
|
|
|
|
{
|
|
|
|
(*AH->WriteBytePtr) (AH, i & 0xFF);
|
2002-05-29 03:38:56 +02:00
|
|
|
i >>= 8;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return AH->intSize + 1;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
int
|
|
|
|
ReadInt(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int res = 0;
|
|
|
|
int bv,
|
|
|
|
b;
|
|
|
|
int sign = 0; /* Default positive */
|
|
|
|
int bitShift = 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->version > K_VERS_1_0)
|
2000-07-21 13:40:08 +02:00
|
|
|
/* Read a sign byte */
|
2001-03-22 05:01:46 +01:00
|
|
|
sign = (*AH->ReadBytePtr) (AH);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
for (b = 0; b < AH->intSize; b++)
|
|
|
|
{
|
|
|
|
bv = (*AH->ReadBytePtr) (AH) & 0xFF;
|
2000-07-21 13:40:08 +02:00
|
|
|
if (bv != 0)
|
|
|
|
res = res + (bv << bitShift);
|
|
|
|
bitShift += 8;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (sign)
|
|
|
|
res = -res;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return res;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t
|
2001-04-01 07:42:51 +02:00
|
|
|
WriteStr(ArchiveHandle *AH, const char *c)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t res;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
if (c)
|
|
|
|
{
|
|
|
|
res = WriteInt(AH, strlen(c));
|
2001-03-22 05:01:46 +01:00
|
|
|
res += (*AH->WriteBufPtr) (AH, c, strlen(c));
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res = WriteInt(AH, -1);
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return res;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
char *
|
|
|
|
ReadStr(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
char *buf;
|
|
|
|
int l;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
l = ReadInt(AH);
|
2000-07-21 13:40:08 +02:00
|
|
|
if (l == -1)
|
|
|
|
buf = NULL;
|
|
|
|
else
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
buf = (char *) malloc(l + 1);
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!buf)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->ReadBufPtr) (AH, (void *) buf, l);
|
2000-07-21 13:40:08 +02:00
|
|
|
buf[l] = '\0';
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return buf;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2000-12-07 03:52:27 +01:00
|
|
|
static int
|
2001-03-22 05:01:46 +01:00
|
|
|
_discoverArchiveFormat(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
FILE *fh;
|
|
|
|
char sig[6]; /* More than enough */
|
2002-08-20 19:54:45 +02:00
|
|
|
size_t cnt;
|
2001-03-22 05:01:46 +01:00
|
|
|
int wantClose = 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
#if 0
|
2001-10-25 07:50:21 +02:00
|
|
|
write_msg(modulename, "attempting to ascertain archive format\n");
|
2001-06-27 23:21:37 +02:00
|
|
|
#endif
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
if (AH->lookahead)
|
|
|
|
free(AH->lookahead);
|
|
|
|
|
|
|
|
AH->lookaheadSize = 512;
|
|
|
|
AH->lookahead = calloc(1, 512);
|
|
|
|
AH->lookaheadLen = 0;
|
|
|
|
AH->lookaheadPos = 0;
|
2000-07-06 20:39:39 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->fSpec)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
wantClose = 1;
|
|
|
|
fh = fopen(AH->fSpec, PG_BINARY_R);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
2000-07-21 13:40:08 +02:00
|
|
|
fh = stdin;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!fh)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not open input file: %s\n", strerror(errno));
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
cnt = fread(sig, 1, 5, fh);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (cnt != 5)
|
2001-06-27 23:21:37 +02:00
|
|
|
{
|
|
|
|
if (ferror(fh))
|
|
|
|
die_horribly(AH, modulename, "could not read input file: %s\n", strerror(errno));
|
|
|
|
else
|
2002-08-20 19:54:45 +02:00
|
|
|
die_horribly(AH, modulename, "input file is too short (read %lu, expected 5)\n",
|
|
|
|
(unsigned long) cnt);
|
2001-06-27 23:21:37 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Save it, just in case we need it later */
|
2000-07-21 13:40:08 +02:00
|
|
|
strncpy(&AH->lookahead[0], sig, 5);
|
|
|
|
AH->lookaheadLen = 5;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (strncmp(sig, "PGDMP", 5) == 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
AH->vmaj = fgetc(fh);
|
|
|
|
AH->vmin = fgetc(fh);
|
|
|
|
|
|
|
|
/* Save these too... */
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->vmaj;
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->vmin;
|
|
|
|
|
|
|
|
/* Check header version; varies from V1.0 */
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0))) /* Version > 1.0 */
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->vrev = fgetc(fh);
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->vrev;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
AH->vrev = 0;
|
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
/* Make a convenient integer <maj><min><rev>00 */
|
|
|
|
AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->intSize = fgetc(fh);
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->intSize;
|
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
if (AH->version >= K_VERS_1_7)
|
|
|
|
{
|
|
|
|
AH->offSize = fgetc(fh);
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->offSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
AH->offSize = AH->intSize;
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->format = fgetc(fh);
|
|
|
|
AH->lookahead[AH->lookaheadLen++] = AH->format;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* *Maybe* we have a tar archive format file... So, read first 512
|
|
|
|
* byte header...
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
|
|
|
cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
|
|
|
|
AH->lookaheadLen += cnt;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->lookaheadLen != 512)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "input file does not appear to be a valid archive (too short?)\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!isValidTarHeader(AH->lookahead))
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "input file does not appear to be a valid archive\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->format = archTar;
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* If we can't seek, then mark the header as read */
|
2002-08-20 19:54:45 +02:00
|
|
|
if (fseeko(fh, 0, SEEK_SET) != 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
/*
|
2003-03-10 23:28:22 +01:00
|
|
|
* NOTE: Formats that use the lookahead buffer can unset this in
|
2001-03-22 05:01:46 +01:00
|
|
|
* their Init routine.
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
|
|
|
AH->readHeader = 1;
|
|
|
|
}
|
|
|
|
else
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->lookaheadLen = 0; /* Don't bother since we've reset the file */
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
#if 0
|
2002-08-20 19:54:45 +02:00
|
|
|
write_msg(modulename, "read %lu bytes into lookahead buffer\n",
|
|
|
|
(unsigned long) AH->lookaheadLen);
|
2001-06-27 23:21:37 +02:00
|
|
|
#endif
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Close the file */
|
|
|
|
if (wantClose)
|
2001-01-12 05:32:07 +01:00
|
|
|
if (fclose(fh) != 0)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "could not close the input file after reading header: %s\n",
|
|
|
|
strerror(errno));
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return AH->format;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate an archive handle
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static ArchiveHandle *
|
|
|
|
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
|
|
|
|
const int compression, ArchiveMode mode)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
ArchiveHandle *AH;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
#if 0
|
2001-10-25 07:50:21 +02:00
|
|
|
write_msg(modulename, "allocating AH for %s, format %d\n", FileSpec, fmt);
|
2001-06-27 23:21:37 +02:00
|
|
|
#endif
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH = (ArchiveHandle *) calloc(1, sizeof(ArchiveHandle));
|
|
|
|
if (!AH)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
/* AH->debugLevel = 100; */
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->vmaj = K_VERS_MAJOR;
|
|
|
|
AH->vmin = K_VERS_MINOR;
|
2000-08-01 17:51:45 +02:00
|
|
|
AH->vrev = K_VERS_REV;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->createDate = time(NULL);
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->intSize = sizeof(int);
|
2002-10-22 21:15:23 +02:00
|
|
|
AH->offSize = sizeof(off_t);
|
2001-03-22 05:01:46 +01:00
|
|
|
if (FileSpec)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->fSpec = strdup(FileSpec);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/*
|
|
|
|
* Not used; maybe later....
|
|
|
|
*
|
2001-03-22 05:01:46 +01:00
|
|
|
* AH->workDir = strdup(FileSpec); for(i=strlen(FileSpec) ; i > 0 ;
|
|
|
|
* i--) if (AH->workDir[i-1] == '/')
|
2000-07-21 13:40:08 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->fSpec = NULL;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->currUser = strdup(""); /* So it's valid, but we can free() it
|
|
|
|
* later if necessary */
|
2002-09-04 22:31:48 +02:00
|
|
|
AH->currSchema = strdup(""); /* ditto */
|
2004-03-24 04:06:08 +01:00
|
|
|
AH->currWithOids = -1; /* force SET */
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->toc = (TocEntry *) calloc(1, sizeof(TocEntry));
|
|
|
|
if (!AH->toc)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "out of memory\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->toc->next = AH->toc;
|
|
|
|
AH->toc->prev = AH->toc;
|
|
|
|
|
|
|
|
AH->mode = mode;
|
|
|
|
AH->compression = compression;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->pgCopyBuf = createPQExpBuffer();
|
|
|
|
AH->sqlBuf = createPQExpBuffer();
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Open stdout with no compression for AH output handle */
|
|
|
|
AH->gzOut = 0;
|
|
|
|
AH->OF = stdout;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2005-01-26 20:44:43 +01:00
|
|
|
/*
|
|
|
|
* On Windows, we need to use binary mode to read/write non-text archive
|
|
|
|
* formats. Force stdin/stdout into binary mode in case that is what
|
|
|
|
* we are using.
|
|
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
|
|
if (fmt != archNull)
|
|
|
|
{
|
|
|
|
if (mode == archModeWrite)
|
|
|
|
setmode(fileno(stdout), O_BINARY);
|
|
|
|
else
|
|
|
|
setmode(fileno(stdin), O_BINARY);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2001-06-27 23:21:37 +02:00
|
|
|
#if 0
|
2001-10-25 07:50:21 +02:00
|
|
|
write_msg(modulename, "archive format is %d\n", fmt);
|
2001-06-27 23:21:37 +02:00
|
|
|
#endif
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (fmt == archUnknown)
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->format = _discoverArchiveFormat(AH);
|
|
|
|
else
|
|
|
|
AH->format = fmt;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
switch (AH->format)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
case archCustom:
|
|
|
|
InitArchiveFmt_Custom(AH);
|
|
|
|
break;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
case archFiles:
|
|
|
|
InitArchiveFmt_Files(AH);
|
|
|
|
break;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
case archNull:
|
|
|
|
InitArchiveFmt_Null(AH);
|
|
|
|
break;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
case archTar:
|
|
|
|
InitArchiveFmt_Tar(AH);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "unrecognized file format \"%d\"\n", fmt);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2004-04-22 04:39:10 +02:00
|
|
|
/* sql error handling */
|
2004-08-20 06:20:23 +02:00
|
|
|
AH->public.exit_on_error = true;
|
2004-04-22 04:39:10 +02:00
|
|
|
AH->public.n_errors = 0;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return AH;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
WriteDataChunks(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
TocEntry *te = AH->toc->next;
|
|
|
|
StartDataPtr startPtr;
|
|
|
|
EndDataPtr endPtr;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
|
|
|
if (te->dataDumper != NULL)
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->currToc = te;
|
|
|
|
/* printf("Writing data for %d (%x)\n", te->id, te); */
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (strcmp(te->desc, "BLOBS") == 0)
|
|
|
|
{
|
|
|
|
startPtr = AH->StartBlobsPtr;
|
|
|
|
endPtr = AH->EndBlobsPtr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
startPtr = AH->StartDataPtr;
|
|
|
|
endPtr = AH->EndDataPtr;
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (startPtr != NULL)
|
|
|
|
(*startPtr) (AH, te);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
|
|
|
* printf("Dumper arg for %d is %x\n", te->id,
|
|
|
|
* te->dataDumperArg);
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The user-provided DataDumper routine needs to call
|
|
|
|
* AH->WriteData
|
|
|
|
*/
|
2003-12-06 04:00:16 +01:00
|
|
|
(*te->dataDumper) ((Archive *) AH, te->dataDumperArg);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
|
|
|
if (endPtr != NULL)
|
|
|
|
(*endPtr) (AH, te);
|
|
|
|
AH->currToc = NULL;
|
|
|
|
}
|
2000-07-21 13:40:08 +02:00
|
|
|
te = te->next;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
WriteToc(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
TocEntry *te;
|
|
|
|
char workbuf[32];
|
2001-04-01 07:42:51 +02:00
|
|
|
int i;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
|
|
|
/* printf("%d TOC Entries to save\n", AH->tocCount); */
|
|
|
|
|
|
|
|
WriteInt(AH, AH->tocCount);
|
2003-12-06 04:00:16 +01:00
|
|
|
|
|
|
|
for (te = AH->toc->next; te != AH->toc; te = te->next)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
WriteInt(AH, te->dumpId);
|
2001-03-22 05:01:46 +01:00
|
|
|
WriteInt(AH, te->dataDumper ? 1 : 0);
|
2003-12-06 04:00:16 +01:00
|
|
|
|
|
|
|
/* OID is recorded as a string for historical reasons */
|
|
|
|
sprintf(workbuf, "%u", te->catalogId.tableoid);
|
|
|
|
WriteStr(AH, workbuf);
|
|
|
|
sprintf(workbuf, "%u", te->catalogId.oid);
|
|
|
|
WriteStr(AH, workbuf);
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2002-07-04 17:35:07 +02:00
|
|
|
WriteStr(AH, te->tag);
|
2001-03-22 05:01:46 +01:00
|
|
|
WriteStr(AH, te->desc);
|
|
|
|
WriteStr(AH, te->defn);
|
|
|
|
WriteStr(AH, te->dropStmt);
|
|
|
|
WriteStr(AH, te->copyStmt);
|
2002-05-11 00:36:27 +02:00
|
|
|
WriteStr(AH, te->namespace);
|
2004-11-06 20:36:02 +01:00
|
|
|
WriteStr(AH, te->tablespace);
|
2001-03-22 05:01:46 +01:00
|
|
|
WriteStr(AH, te->owner);
|
2004-03-24 04:06:08 +01:00
|
|
|
WriteStr(AH, te->withOids ? "true" : "false");
|
2001-04-01 07:42:51 +02:00
|
|
|
|
|
|
|
/* Dump list of dependencies */
|
2003-12-06 04:00:16 +01:00
|
|
|
for (i = 0; i < te->nDeps; i++)
|
2001-04-01 07:42:51 +02:00
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
sprintf(workbuf, "%d", te->dependencies[i]);
|
|
|
|
WriteStr(AH, workbuf);
|
2001-04-01 07:42:51 +02:00
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
WriteStr(AH, NULL); /* Terminate List */
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->WriteExtraTocPtr)
|
|
|
|
(*AH->WriteExtraTocPtr) (AH, te);
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
ReadToc(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int i;
|
2003-12-06 04:00:16 +01:00
|
|
|
char *tmp;
|
|
|
|
DumpId *deps;
|
2001-04-01 07:42:51 +02:00
|
|
|
int depIdx;
|
|
|
|
int depSize;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
TocEntry *te = AH->toc->next;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->tocCount = ReadInt(AH);
|
2003-12-06 04:00:16 +01:00
|
|
|
AH->maxDumpId = 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
for (i = 0; i < AH->tocCount; i++)
|
|
|
|
{
|
|
|
|
te = (TocEntry *) calloc(1, sizeof(TocEntry));
|
2003-12-06 04:00:16 +01:00
|
|
|
te->dumpId = ReadInt(AH);
|
|
|
|
|
|
|
|
if (te->dumpId > AH->maxDumpId)
|
|
|
|
AH->maxDumpId = te->dumpId;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
/* Sanity check */
|
2003-12-06 04:00:16 +01:00
|
|
|
if (te->dumpId <= 0)
|
|
|
|
die_horribly(AH, modulename,
|
2004-08-29 07:07:03 +02:00
|
|
|
"entry ID %d out of range -- perhaps a corrupt TOC\n",
|
2003-12-06 04:00:16 +01:00
|
|
|
te->dumpId);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
te->hadDumper = ReadInt(AH);
|
2003-12-06 04:00:16 +01:00
|
|
|
|
|
|
|
if (AH->version >= K_VERS_1_8)
|
|
|
|
{
|
|
|
|
tmp = ReadStr(AH);
|
|
|
|
sscanf(tmp, "%u", &te->catalogId.tableoid);
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
te->catalogId.tableoid = InvalidOid;
|
|
|
|
tmp = ReadStr(AH);
|
|
|
|
sscanf(tmp, "%u", &te->catalogId.oid);
|
|
|
|
free(tmp);
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2002-07-04 17:35:07 +02:00
|
|
|
te->tag = ReadStr(AH);
|
2000-07-21 13:40:08 +02:00
|
|
|
te->desc = ReadStr(AH);
|
|
|
|
te->defn = ReadStr(AH);
|
|
|
|
te->dropStmt = ReadStr(AH);
|
|
|
|
|
|
|
|
if (AH->version >= K_VERS_1_3)
|
|
|
|
te->copyStmt = ReadStr(AH);
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
if (AH->version >= K_VERS_1_6)
|
|
|
|
te->namespace = ReadStr(AH);
|
|
|
|
|
2004-11-06 20:36:02 +01:00
|
|
|
if (AH->version >= K_VERS_1_10)
|
|
|
|
te->tablespace = ReadStr(AH);
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
te->owner = ReadStr(AH);
|
2004-03-24 04:06:08 +01:00
|
|
|
if (AH->version >= K_VERS_1_9)
|
|
|
|
{
|
|
|
|
if (strcmp(ReadStr(AH), "true") == 0)
|
|
|
|
te->withOids = true;
|
|
|
|
else
|
|
|
|
te->withOids = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
te->withOids = true;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2001-04-01 07:42:51 +02:00
|
|
|
/* Read TOC entry dependencies */
|
|
|
|
if (AH->version >= K_VERS_1_5)
|
|
|
|
{
|
|
|
|
depSize = 100;
|
2003-12-06 04:00:16 +01:00
|
|
|
deps = (DumpId *) malloc(sizeof(DumpId) * depSize);
|
2001-04-01 07:42:51 +02:00
|
|
|
depIdx = 0;
|
2003-12-06 04:00:16 +01:00
|
|
|
for (;;)
|
2001-04-01 07:42:51 +02:00
|
|
|
{
|
2003-12-06 04:00:16 +01:00
|
|
|
tmp = ReadStr(AH);
|
|
|
|
if (!tmp)
|
|
|
|
break; /* end of list */
|
2003-05-04 00:18:59 +02:00
|
|
|
if (depIdx >= depSize)
|
2001-04-01 07:42:51 +02:00
|
|
|
{
|
|
|
|
depSize *= 2;
|
2003-12-06 04:00:16 +01:00
|
|
|
deps = (DumpId *) realloc(deps, sizeof(DumpId) * depSize);
|
2001-04-01 07:42:51 +02:00
|
|
|
}
|
2003-12-06 04:00:16 +01:00
|
|
|
sscanf(tmp, "%d", &deps[depIdx]);
|
|
|
|
free(tmp);
|
|
|
|
depIdx++;
|
|
|
|
}
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
if (depIdx > 0) /* We have a non-null entry */
|
|
|
|
{
|
|
|
|
deps = (DumpId *) realloc(deps, sizeof(DumpId) * depIdx);
|
|
|
|
te->dependencies = deps;
|
|
|
|
te->nDeps = depIdx;
|
|
|
|
}
|
2001-06-27 23:21:37 +02:00
|
|
|
else
|
2003-05-04 00:18:59 +02:00
|
|
|
{
|
|
|
|
free(deps);
|
2003-12-06 04:00:16 +01:00
|
|
|
te->dependencies = NULL;
|
|
|
|
te->nDeps = 0;
|
2003-05-04 00:18:59 +02:00
|
|
|
}
|
2001-04-01 07:42:51 +02:00
|
|
|
}
|
2001-06-27 23:21:37 +02:00
|
|
|
else
|
2003-12-06 04:00:16 +01:00
|
|
|
{
|
|
|
|
te->dependencies = NULL;
|
|
|
|
te->nDeps = 0;
|
|
|
|
}
|
2001-04-01 07:42:51 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->ReadExtraTocPtr)
|
|
|
|
(*AH->ReadExtraTocPtr) (AH, te);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2003-12-06 04:00:16 +01:00
|
|
|
ahlog(AH, 3, "read TOC entry %d (ID %d) for %s %s\n",
|
|
|
|
i, te->dumpId, te->desc, te->tag);
|
2000-07-21 13:40:08 +02:00
|
|
|
|
|
|
|
te->prev = AH->toc->prev;
|
|
|
|
AH->toc->prev->next = te;
|
|
|
|
AH->toc->prev = te;
|
|
|
|
te->next = AH->toc;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-11-04 05:05:36 +01:00
|
|
|
static teReqs
|
2005-01-25 23:44:31 +01:00
|
|
|
_tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2005-01-25 23:44:31 +01:00
|
|
|
teReqs res = REQ_ALL;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2004-02-24 04:35:19 +01:00
|
|
|
/* ENCODING objects are dumped specially, so always reject here */
|
|
|
|
if (strcmp(te->desc, "ENCODING") == 0)
|
|
|
|
return 0;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* If it's an ACL, maybe ignore it */
|
2005-01-25 23:44:31 +01:00
|
|
|
if ((!include_acls || ropt->aclsSkip) && strcmp(te->desc, "ACL") == 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
|
2000-08-01 17:51:45 +02:00
|
|
|
return 0;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Check if tablename only is wanted */
|
|
|
|
if (ropt->selTypes)
|
|
|
|
{
|
|
|
|
if ((strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0))
|
2000-07-21 13:40:08 +02:00
|
|
|
{
|
|
|
|
if (!ropt->selTable)
|
|
|
|
return 0;
|
2002-07-04 17:35:07 +02:00
|
|
|
if (ropt->tableNames && strcmp(ropt->tableNames, te->tag) != 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else if (strcmp(te->desc, "INDEX") == 0)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!ropt->selIndex)
|
|
|
|
return 0;
|
2002-07-04 17:35:07 +02:00
|
|
|
if (ropt->indexNames && strcmp(ropt->indexNames, te->tag) != 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else if (strcmp(te->desc, "FUNCTION") == 0)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!ropt->selFunction)
|
|
|
|
return 0;
|
2002-07-04 17:35:07 +02:00
|
|
|
if (ropt->functionNames && strcmp(ropt->functionNames, te->tag) != 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else if (strcmp(te->desc, "TRIGGER") == 0)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
if (!ropt->selTrigger)
|
|
|
|
return 0;
|
2002-07-04 17:35:07 +02:00
|
|
|
if (ropt->triggerNames && strcmp(ropt->triggerNames, te->tag) != 0)
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
else
|
|
|
|
return 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-11-05 18:46:40 +01:00
|
|
|
/*
|
|
|
|
* Check if we had a dataDumper. Indicates if the entry is schema or
|
|
|
|
* data
|
|
|
|
*/
|
|
|
|
if (!te->hadDumper)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Special Case: If 'SEQUENCE SET' then it is considered a data
|
|
|
|
* entry
|
|
|
|
*/
|
|
|
|
if (strcmp(te->desc, "SEQUENCE SET") == 0)
|
|
|
|
res = res & REQ_DATA;
|
|
|
|
else
|
2001-11-04 05:05:36 +01:00
|
|
|
res = res & ~REQ_DATA;
|
|
|
|
}
|
2000-10-13 02:43:31 +02:00
|
|
|
|
2002-01-18 20:17:05 +01:00
|
|
|
/*
|
2002-07-04 17:35:07 +02:00
|
|
|
* Special case: <Init> type with <Max OID> tag; this is part of a
|
2002-01-18 20:17:05 +01:00
|
|
|
* DATA restore even though it has SQL.
|
|
|
|
*/
|
2002-07-04 17:35:07 +02:00
|
|
|
if ((strcmp(te->desc, "<Init>") == 0) && (strcmp(te->tag, "Max OID") == 0))
|
2002-01-18 18:13:51 +01:00
|
|
|
res = REQ_DATA;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Mask it if we only want schema */
|
|
|
|
if (ropt->schemaOnly)
|
2001-11-04 05:05:36 +01:00
|
|
|
res = res & REQ_SCHEMA;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Mask it we only want data */
|
2001-05-12 03:03:59 +02:00
|
|
|
if (ropt->dataOnly)
|
2001-11-04 05:05:36 +01:00
|
|
|
res = res & REQ_DATA;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2003-03-10 23:28:22 +01:00
|
|
|
/* Mask it if we don't have a schema contribution */
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!te->defn || strlen(te->defn) == 0)
|
2001-11-04 05:05:36 +01:00
|
|
|
res = res & ~REQ_SCHEMA;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/* Finally, if we used a list, limit based on that as well */
|
2003-12-06 04:00:16 +01:00
|
|
|
if (ropt->limitToList && !ropt->idWanted[te->dumpId - 1])
|
2000-07-21 13:40:08 +02:00
|
|
|
return 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
return res;
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2004-02-24 04:35:19 +01:00
|
|
|
/*
|
|
|
|
* Issue SET commands for parameters that we want to have set the same way
|
|
|
|
* at all times during execution of a restore script.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_doSetFixedOutputState(ArchiveHandle *AH)
|
|
|
|
{
|
|
|
|
TocEntry *te;
|
|
|
|
|
|
|
|
/* If we have an encoding setting, emit that */
|
|
|
|
te = AH->toc->next;
|
|
|
|
while (te != AH->toc)
|
|
|
|
{
|
|
|
|
if (strcmp(te->desc, "ENCODING") == 0)
|
|
|
|
{
|
|
|
|
ahprintf(AH, "%s", te->defn);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
te = te->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure function checking is disabled */
|
|
|
|
ahprintf(AH, "SET check_function_bodies = false;\n");
|
|
|
|
|
They are two different problems; the TOC entry is important for any
multiline command or to rerun the command easily later.
Whereas displaying the failed SQL command is a matter of fixing the
error
messages.
The latter is complicated by failed COPY commands which, with
die-on-errors
off, results in the data being processed as a command, so dumping the
command will dump all of the data.
In the case of long commands, should the whole command be dumped? eg.
(eg.
several pages of function definition).
In the case of the COPY command, I'm not sure what to do. Obviously, it
would be best to avoid sending the data, but the data and command are
combined (from memory). Also, the 'data' may be in the form of INSERT
statements.
Attached patch produces the first 125 chars of the command:
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC Entry 26; 1255 16449270
FUNCTION
plpgsql_call_handler() pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_call_handler" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_call_handler() RETURNS
language_handler
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_call_han...
pg_restore: [archiver (db)] Error from TOC Entry 27; 1255 16449271
FUNCTION
plpgsql_validator(oid) pjw
pg_restore: [archiver (db)] could not execute query: ERROR: function
"plpgsql_validator" already exists with same argument types
Command was: CREATE FUNCTION plpgsql_validator(oid) RETURNS void
AS '/var/lib/pgsql-8.0b1/lib/plpgsql', 'plpgsql_validator'
LANGU...
Philip Warner
2004-08-20 22:00:34 +02:00
|
|
|
/* Avoid annoying notices etc */
|
|
|
|
ahprintf(AH, "SET client_min_messages = warning;\n");
|
|
|
|
|
2004-02-24 04:35:19 +01:00
|
|
|
ahprintf(AH, "\n");
|
|
|
|
}
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
/*
|
|
|
|
* Issue a SET SESSION AUTHORIZATION command. Caller is responsible
|
2003-09-24 00:48:53 +02:00
|
|
|
* for updating state if appropriate. If user is NULL or an empty string,
|
|
|
|
* the specification DEFAULT will be used.
|
2002-05-11 00:36:27 +02:00
|
|
|
*/
|
|
|
|
static void
|
2002-08-18 11:36:26 +02:00
|
|
|
_doSetSessionAuth(ArchiveHandle *AH, const char *user)
|
2002-05-11 00:36:27 +02:00
|
|
|
{
|
2002-08-18 11:36:26 +02:00
|
|
|
PQExpBuffer cmd = createPQExpBuffer();
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2002-08-18 11:36:26 +02:00
|
|
|
appendPQExpBuffer(cmd, "SET SESSION AUTHORIZATION ");
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2003-09-24 00:48:53 +02:00
|
|
|
/*
|
|
|
|
* SQL requires a string literal here. Might as well be correct.
|
|
|
|
*/
|
|
|
|
if (user && *user)
|
2002-08-18 11:36:26 +02:00
|
|
|
appendStringLiteral(cmd, user, false);
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(cmd, "DEFAULT");
|
|
|
|
appendPQExpBuffer(cmd, ";");
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
if (RestoringToDB(AH))
|
|
|
|
{
|
|
|
|
PGresult *res;
|
|
|
|
|
2002-08-18 11:36:26 +02:00
|
|
|
res = PQexec(AH->connection, cmd->data);
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
2004-04-22 04:39:10 +02:00
|
|
|
/* NOT warn_or_die_horribly... use -O instead to skip this. */
|
2003-07-23 10:47:41 +02:00
|
|
|
die_horribly(AH, modulename, "could not set session user to \"%s\": %s",
|
2002-08-18 11:36:26 +02:00
|
|
|
user, PQerrorMessage(AH->connection));
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
PQclear(res);
|
|
|
|
}
|
|
|
|
else
|
2002-08-18 11:36:26 +02:00
|
|
|
ahprintf(AH, "%s\n\n", cmd->data);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(cmd);
|
2002-05-11 00:36:27 +02:00
|
|
|
}
|
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
|
2004-03-24 04:06:08 +01:00
|
|
|
/*
|
|
|
|
* Issue a SET default_with_oids command. Caller is responsible
|
|
|
|
* for updating state if appropriate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_doSetWithOids(ArchiveHandle *AH, const bool withOids)
|
|
|
|
{
|
|
|
|
PQExpBuffer cmd = createPQExpBuffer();
|
|
|
|
|
|
|
|
appendPQExpBuffer(cmd, "SET default_with_oids = %s;", withOids ?
|
2004-08-29 07:07:03 +02:00
|
|
|
"true" : "false");
|
2004-03-24 04:06:08 +01:00
|
|
|
|
|
|
|
if (RestoringToDB(AH))
|
|
|
|
{
|
|
|
|
PGresult *res;
|
|
|
|
|
|
|
|
res = PQexec(AH->connection, cmd->data);
|
|
|
|
|
|
|
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
2004-08-29 07:07:03 +02:00
|
|
|
warn_or_die_horribly(AH, modulename,
|
2004-04-22 04:39:10 +02:00
|
|
|
"could not set default_with_oids: %s",
|
|
|
|
PQerrorMessage(AH->connection));
|
2004-03-24 04:06:08 +01:00
|
|
|
|
|
|
|
PQclear(res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ahprintf(AH, "%s\n\n", cmd->data);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
/*
|
2004-09-10 22:05:18 +02:00
|
|
|
* Issue the commands to connect to the specified database.
|
2001-08-22 22:23:24 +02:00
|
|
|
*
|
|
|
|
* If we're currently restoring right into a database, this will
|
2002-09-04 22:31:48 +02:00
|
|
|
* actually establish a connection. Otherwise it puts a \connect into
|
2001-08-22 22:23:24 +02:00
|
|
|
* the script output.
|
2004-09-10 22:05:18 +02:00
|
|
|
*
|
|
|
|
* NULL dbname implies reconnecting to the current DB (pretty useless).
|
2001-08-22 22:23:24 +02:00
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
2004-09-10 22:05:18 +02:00
|
|
|
_reconnectToDB(ArchiveHandle *AH, const char *dbname)
|
2000-07-24 08:24:26 +02:00
|
|
|
{
|
2003-09-24 00:48:53 +02:00
|
|
|
if (RestoringToDB(AH))
|
2004-09-10 22:05:18 +02:00
|
|
|
ReconnectToServer(AH, dbname, NULL);
|
2001-08-22 22:23:24 +02:00
|
|
|
else
|
2002-02-11 01:18:20 +01:00
|
|
|
{
|
|
|
|
PQExpBuffer qry = createPQExpBuffer();
|
|
|
|
|
2004-09-10 22:05:18 +02:00
|
|
|
appendPQExpBuffer(qry, "\\connect %s\n\n",
|
2002-08-18 11:36:26 +02:00
|
|
|
dbname ? fmtId(dbname) : "-");
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2002-02-11 01:18:20 +01:00
|
|
|
ahprintf(AH, qry->data);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
|
|
|
}
|
2001-08-22 22:23:24 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
|
|
|
* NOTE: currUser keeps track of what the imaginary session user in
|
2004-09-10 22:05:18 +02:00
|
|
|
* our script is. It's now effectively reset to the original userID.
|
2001-10-25 07:50:21 +02:00
|
|
|
*/
|
2001-08-22 22:23:24 +02:00
|
|
|
if (AH->currUser)
|
|
|
|
free(AH->currUser);
|
|
|
|
|
2004-09-10 22:05:18 +02:00
|
|
|
AH->currUser = strdup("");
|
2003-09-24 00:48:53 +02:00
|
|
|
|
|
|
|
/* don't assume we still know the output schema */
|
|
|
|
if (AH->currSchema)
|
|
|
|
free(AH->currSchema);
|
|
|
|
AH->currSchema = strdup("");
|
2004-03-24 04:06:08 +01:00
|
|
|
AH->currWithOids = -1;
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-02-24 04:35:19 +01:00
|
|
|
/* re-establish fixed state */
|
|
|
|
_doSetFixedOutputState(AH);
|
2000-08-01 17:51:45 +02:00
|
|
|
}
|
|
|
|
|
2003-09-24 00:48:53 +02:00
|
|
|
/*
|
|
|
|
* Become the specified user, and update state to avoid redundant commands
|
|
|
|
*
|
|
|
|
* NULL or empty argument is taken to mean restoring the session default
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_becomeUser(ArchiveHandle *AH, const char *user)
|
|
|
|
{
|
|
|
|
if (!user)
|
|
|
|
user = ""; /* avoid null pointers */
|
|
|
|
|
|
|
|
if (AH->currUser && strcmp(AH->currUser, user) == 0)
|
|
|
|
return; /* no need to do anything */
|
|
|
|
|
|
|
|
_doSetSessionAuth(AH, user);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: currUser keeps track of what the imaginary session user in
|
|
|
|
* our script is
|
|
|
|
*/
|
|
|
|
if (AH->currUser)
|
|
|
|
free(AH->currUser);
|
|
|
|
|
|
|
|
AH->currUser = strdup(user);
|
|
|
|
}
|
2001-08-22 22:23:24 +02:00
|
|
|
|
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Become the owner of the the given TOC entry object. If
|
2001-08-22 22:23:24 +02:00
|
|
|
* changes in ownership are not allowed, this doesn't do anything.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
2003-09-24 00:48:53 +02:00
|
|
|
_becomeOwner(ArchiveHandle *AH, TocEntry *te)
|
2000-08-01 17:51:45 +02:00
|
|
|
{
|
2004-07-13 05:00:17 +02:00
|
|
|
if (AH->ropt && (AH->ropt->noOwner || !AH->ropt->use_setsessauth))
|
2000-08-01 17:51:45 +02:00
|
|
|
return;
|
|
|
|
|
2003-09-24 00:48:53 +02:00
|
|
|
_becomeUser(AH, te->owner);
|
2000-07-24 08:24:26 +02:00
|
|
|
}
|
|
|
|
|
2001-08-22 22:23:24 +02:00
|
|
|
|
2004-03-24 04:06:08 +01:00
|
|
|
/*
|
|
|
|
* Set the proper default_with_oids value for the table.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_setWithOids(ArchiveHandle *AH, TocEntry *te)
|
|
|
|
{
|
|
|
|
if (AH->currWithOids != te->withOids)
|
|
|
|
{
|
|
|
|
_doSetWithOids(AH, te->withOids);
|
|
|
|
AH->currWithOids = te->withOids;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
/*
|
|
|
|
* Issue the commands to select the specified schema as the current schema
|
|
|
|
* in the target database.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_selectOutputSchema(ArchiveHandle *AH, const char *schemaName)
|
|
|
|
{
|
2002-05-29 00:26:57 +02:00
|
|
|
PQExpBuffer qry;
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
if (!schemaName || *schemaName == '\0' ||
|
|
|
|
strcmp(AH->currSchema, schemaName) == 0)
|
|
|
|
return; /* no need to do anything */
|
|
|
|
|
2002-05-29 00:26:57 +02:00
|
|
|
qry = createPQExpBuffer();
|
|
|
|
|
|
|
|
appendPQExpBuffer(qry, "SET search_path = %s",
|
2002-08-18 11:36:26 +02:00
|
|
|
fmtId(schemaName));
|
2002-05-29 00:26:57 +02:00
|
|
|
if (strcmp(schemaName, "pg_catalog") != 0)
|
|
|
|
appendPQExpBuffer(qry, ", pg_catalog");
|
|
|
|
|
2002-05-11 00:36:27 +02:00
|
|
|
if (RestoringToDB(AH))
|
|
|
|
{
|
|
|
|
PGresult *res;
|
|
|
|
|
|
|
|
res = PQexec(AH->connection, qry->data);
|
|
|
|
|
|
|
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
2004-08-29 07:07:03 +02:00
|
|
|
warn_or_die_horribly(AH, modulename,
|
|
|
|
"could not set search_path to \"%s\": %s",
|
|
|
|
schemaName, PQerrorMessage(AH->connection));
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
PQclear(res);
|
|
|
|
}
|
|
|
|
else
|
2002-05-29 00:26:57 +02:00
|
|
|
ahprintf(AH, "%s;\n\n", qry->data);
|
2002-05-11 00:36:27 +02:00
|
|
|
|
|
|
|
if (AH->currSchema)
|
|
|
|
free(AH->currSchema);
|
|
|
|
AH->currSchema = strdup(schemaName);
|
2002-05-29 00:26:57 +02:00
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
2002-05-11 00:36:27 +02:00
|
|
|
}
|
|
|
|
|
2004-11-06 20:36:02 +01:00
|
|
|
/*
|
|
|
|
* Issue the commands to select the specified tablespace as the current one
|
|
|
|
* in the target database.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_selectTablespace(ArchiveHandle *AH, const char *tablespace)
|
|
|
|
{
|
|
|
|
PQExpBuffer qry;
|
|
|
|
const char *want, *have;
|
|
|
|
|
|
|
|
have = AH->currTablespace;
|
|
|
|
want = tablespace;
|
|
|
|
|
|
|
|
/* no need to do anything for non-tablespace object */
|
|
|
|
if (!want)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (have && strcmp(want, have) == 0)
|
|
|
|
return; /* no need to do anything */
|
|
|
|
|
|
|
|
qry = createPQExpBuffer();
|
|
|
|
|
|
|
|
if (strcmp(want, "") == 0)
|
|
|
|
{
|
|
|
|
/* We want the tablespace to be the database's default */
|
|
|
|
appendPQExpBuffer(qry, "SET default_tablespace = ''");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We want an explicit tablespace */
|
|
|
|
appendPQExpBuffer(qry, "SET default_tablespace = %s", fmtId(want));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RestoringToDB(AH))
|
|
|
|
{
|
|
|
|
PGresult *res;
|
|
|
|
|
|
|
|
res = PQexec(AH->connection, qry->data);
|
|
|
|
|
|
|
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
|
|
warn_or_die_horribly(AH, modulename,
|
|
|
|
"could not set default_tablespace to %s: %s",
|
|
|
|
fmtId(want), PQerrorMessage(AH->connection));
|
|
|
|
|
|
|
|
PQclear(res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ahprintf(AH, "%s;\n\n", qry->data);
|
|
|
|
|
|
|
|
if (AH->currTablespace)
|
|
|
|
free(AH->currTablespace);
|
|
|
|
AH->currTablespace = strdup(want);
|
|
|
|
|
|
|
|
destroyPQExpBuffer(qry);
|
|
|
|
}
|
2002-05-11 00:36:27 +02:00
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
/*
|
|
|
|
* Extract an object description for a TOC entry, and append it to buf.
|
|
|
|
*
|
|
|
|
* This is not quite as general as it may seem, since it really only
|
|
|
|
* handles constructing the right thing to put into ALTER ... OWNER TO.
|
|
|
|
*
|
|
|
|
* The whole thing is pretty grotty, but we are kind of stuck since the
|
|
|
|
* information used is all that's available in older dump files.
|
2004-07-13 05:00:17 +02:00
|
|
|
*/
|
2005-01-11 06:14:13 +01:00
|
|
|
static void
|
2005-01-23 01:03:54 +01:00
|
|
|
_getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
|
2004-07-13 05:00:17 +02:00
|
|
|
{
|
2005-01-11 06:14:13 +01:00
|
|
|
const char *type = te->desc;
|
|
|
|
|
|
|
|
/* Use ALTER TABLE for views and sequences */
|
|
|
|
if (strcmp(type, "VIEW") == 0 ||
|
|
|
|
strcmp(type, "SEQUENCE") == 0)
|
|
|
|
type = "TABLE";
|
|
|
|
|
|
|
|
/* We assume CONSTRAINTs are always pkey/unique indexes */
|
|
|
|
if (strcmp(type, "CONSTRAINT") == 0)
|
|
|
|
type = "INDEX";
|
|
|
|
|
|
|
|
/* objects named by a schema and name */
|
|
|
|
if (strcmp(type, "CONVERSION") == 0 ||
|
|
|
|
strcmp(type, "DOMAIN") == 0 ||
|
|
|
|
strcmp(type, "INDEX") == 0 ||
|
|
|
|
strcmp(type, "TABLE") == 0 ||
|
|
|
|
strcmp(type, "TYPE") == 0)
|
|
|
|
{
|
2005-01-23 01:03:54 +01:00
|
|
|
appendPQExpBuffer(buf, "%s ", type);
|
|
|
|
if (te->namespace && te->namespace[0]) /* is null pre-7.3 */
|
|
|
|
appendPQExpBuffer(buf, "%s.", fmtId(te->namespace));
|
|
|
|
/*
|
|
|
|
* Pre-7.3 pg_dump would sometimes (not always) put
|
|
|
|
* a fmtId'd name into te->tag for an index.
|
|
|
|
* This check is heuristic, so make its scope as
|
|
|
|
* narrow as possible.
|
|
|
|
*/
|
|
|
|
if (AH->version < K_VERS_1_7 &&
|
|
|
|
te->tag[0] == '"' &&
|
|
|
|
te->tag[strlen(te->tag)-1] == '"' &&
|
|
|
|
strcmp(type, "INDEX") == 0)
|
|
|
|
appendPQExpBuffer(buf, "%s", te->tag);
|
|
|
|
else
|
|
|
|
appendPQExpBuffer(buf, "%s", fmtId(te->tag));
|
2005-01-11 06:14:13 +01:00
|
|
|
return;
|
|
|
|
}
|
2004-07-13 05:00:17 +02:00
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
/* objects named by just a name */
|
|
|
|
if (strcmp(type, "DATABASE") == 0 ||
|
|
|
|
strcmp(type, "SCHEMA") == 0)
|
|
|
|
{
|
|
|
|
appendPQExpBuffer(buf, "%s %s", type, fmtId(te->tag));
|
|
|
|
return;
|
|
|
|
}
|
2002-08-18 11:36:26 +02:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
/*
|
2005-01-11 06:14:13 +01:00
|
|
|
* These object types require additional decoration. Fortunately,
|
|
|
|
* the information needed is exactly what's in the DROP command.
|
2004-08-29 07:07:03 +02:00
|
|
|
*/
|
2005-01-11 06:14:13 +01:00
|
|
|
if (strcmp(type, "AGGREGATE") == 0 ||
|
|
|
|
strcmp(type, "FUNCTION") == 0 ||
|
|
|
|
strcmp(type, "OPERATOR") == 0 ||
|
|
|
|
strcmp(type, "OPERATOR CLASS") == 0)
|
2004-08-29 07:07:03 +02:00
|
|
|
{
|
2005-01-11 06:14:13 +01:00
|
|
|
/* Chop "DROP " off the front and make a modifiable copy */
|
|
|
|
char *first = strdup(te->dropStmt + 5);
|
|
|
|
char *last;
|
2004-07-13 05:00:17 +02:00
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
/* point to last character in string */
|
|
|
|
last = first + strlen(first) - 1;
|
2004-07-13 05:00:17 +02:00
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
/* Strip off any ';' or '\n' at the end */
|
|
|
|
while (last >= first && (*last == '\n' || *last == ';'))
|
|
|
|
last--;
|
|
|
|
*(last + 1) = '\0';
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
appendPQExpBufferStr(buf, first);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
|
|
|
free(first);
|
2005-01-11 06:14:13 +01:00
|
|
|
return;
|
2004-07-13 05:00:17 +02:00
|
|
|
}
|
|
|
|
|
2005-01-11 06:14:13 +01:00
|
|
|
write_msg(modulename, "WARNING: don't know how to set owner for object type %s\n",
|
|
|
|
type);
|
2004-07-13 05:00:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-08-13 23:37:28 +02:00
|
|
|
_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData, bool acl_pass)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2004-08-13 23:37:28 +02:00
|
|
|
/* ACLs are dumped only during acl pass */
|
|
|
|
if (acl_pass)
|
|
|
|
{
|
|
|
|
if (strcmp(te->desc, "ACL") != 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (strcmp(te->desc, "ACL") == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Avoid dumping the public schema, as it will already be created ...
|
|
|
|
* unless we are using --clean mode, in which case it's been deleted
|
|
|
|
* and we'd better recreate it.
|
|
|
|
*/
|
|
|
|
if (!ropt->dropSchema &&
|
|
|
|
strcmp(te->desc, "SCHEMA") == 0 && strcmp(te->tag, "public") == 0)
|
|
|
|
return;
|
|
|
|
|
2004-11-06 20:36:02 +01:00
|
|
|
/* Select owner, schema, and tablespace as necessary */
|
2004-08-13 23:37:28 +02:00
|
|
|
_becomeOwner(AH, te);
|
|
|
|
_selectOutputSchema(AH, te->namespace);
|
2004-11-06 20:36:02 +01:00
|
|
|
_selectTablespace(AH, te->tablespace);
|
2004-08-13 23:37:28 +02:00
|
|
|
|
|
|
|
/* Set up OID mode too */
|
|
|
|
if (strcmp(te->desc, "TABLE") == 0)
|
|
|
|
_setWithOids(AH, te);
|
|
|
|
|
|
|
|
/* Emit header comment for item */
|
2004-08-30 21:44:14 +02:00
|
|
|
if (!AH->noTocComments)
|
2003-12-06 04:00:16 +01:00
|
|
|
{
|
2004-08-30 21:44:14 +02:00
|
|
|
const char *pfx;
|
|
|
|
|
|
|
|
if (isData)
|
|
|
|
pfx = "Data for ";
|
|
|
|
else
|
|
|
|
pfx = "";
|
|
|
|
|
|
|
|
ahprintf(AH, "--\n");
|
|
|
|
if (AH->public.verbose)
|
2003-12-06 04:00:16 +01:00
|
|
|
{
|
2004-08-30 21:44:14 +02:00
|
|
|
ahprintf(AH, "-- TOC entry %d (class %u OID %u)\n",
|
|
|
|
te->dumpId, te->catalogId.tableoid, te->catalogId.oid);
|
|
|
|
if (te->nDeps > 0)
|
|
|
|
{
|
|
|
|
int i;
|
2003-12-06 04:00:16 +01:00
|
|
|
|
2004-08-30 21:44:14 +02:00
|
|
|
ahprintf(AH, "-- Dependencies:");
|
|
|
|
for (i = 0; i < te->nDeps; i++)
|
|
|
|
ahprintf(AH, " %d", te->dependencies[i]);
|
|
|
|
ahprintf(AH, "\n");
|
|
|
|
}
|
2003-12-06 04:00:16 +01:00
|
|
|
}
|
2004-11-06 20:36:02 +01:00
|
|
|
ahprintf(AH, "-- %sName: %s; Type: %s; Schema: %s; Owner: %s",
|
2004-08-30 21:44:14 +02:00
|
|
|
pfx, te->tag, te->desc,
|
|
|
|
te->namespace ? te->namespace : "-",
|
|
|
|
te->owner);
|
2004-11-06 20:36:02 +01:00
|
|
|
if (te->tablespace)
|
|
|
|
ahprintf(AH, "; Tablespace: %s", te->tablespace);
|
|
|
|
ahprintf(AH, "\n");
|
|
|
|
|
2004-08-30 21:44:14 +02:00
|
|
|
if (AH->PrintExtraTocPtr != NULL)
|
|
|
|
(*AH->PrintExtraTocPtr) (AH, te);
|
|
|
|
ahprintf(AH, "--\n\n");
|
2003-12-06 04:00:16 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2004-08-13 23:37:28 +02:00
|
|
|
/*
|
|
|
|
* Actually print the definition.
|
|
|
|
*
|
2005-01-11 06:14:13 +01:00
|
|
|
* Really crude hack for suppressing AUTHORIZATION clause that old
|
|
|
|
* pg_dump versions put into CREATE SCHEMA. We have to do this when
|
|
|
|
* --no-owner mode is selected. This is ugly, but I see
|
2004-08-29 07:07:03 +02:00
|
|
|
* no other good way ...
|
2004-08-13 23:37:28 +02:00
|
|
|
*/
|
2005-01-11 06:14:13 +01:00
|
|
|
if (ropt->noOwner && strcmp(te->desc, "SCHEMA") == 0)
|
2004-07-13 05:00:17 +02:00
|
|
|
{
|
2005-01-11 06:14:13 +01:00
|
|
|
ahprintf(AH, "CREATE SCHEMA %s;\n\n\n", fmtId(te->tag));
|
2003-09-24 01:31:52 +02:00
|
|
|
}
|
2004-08-13 23:37:28 +02:00
|
|
|
else
|
2003-09-24 01:31:52 +02:00
|
|
|
{
|
2004-08-13 23:37:28 +02:00
|
|
|
if (strlen(te->defn) > 0)
|
|
|
|
ahprintf(AH, "%s\n\n", te->defn);
|
2004-07-13 05:00:17 +02:00
|
|
|
}
|
2004-08-13 23:37:28 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we aren't using SET SESSION AUTH to determine ownership, we must
|
2005-01-11 06:14:13 +01:00
|
|
|
* instead issue an ALTER OWNER command. We assume that anything without
|
|
|
|
* a DROP command is not a separately ownable object. All the categories
|
|
|
|
* with DROP commands must appear in one list or the other.
|
2004-08-13 23:37:28 +02:00
|
|
|
*/
|
|
|
|
if (!ropt->noOwner && !ropt->use_setsessauth &&
|
2005-01-11 06:14:13 +01:00
|
|
|
strlen(te->owner) > 0 && strlen(te->dropStmt) > 0)
|
|
|
|
{
|
|
|
|
if (strcmp(te->desc, "AGGREGATE") == 0 ||
|
|
|
|
strcmp(te->desc, "CONSTRAINT") == 0 ||
|
|
|
|
strcmp(te->desc, "CONVERSION") == 0 ||
|
|
|
|
strcmp(te->desc, "DATABASE") == 0 ||
|
|
|
|
strcmp(te->desc, "DOMAIN") == 0 ||
|
|
|
|
strcmp(te->desc, "FUNCTION") == 0 ||
|
|
|
|
strcmp(te->desc, "INDEX") == 0 ||
|
|
|
|
strcmp(te->desc, "OPERATOR") == 0 ||
|
|
|
|
strcmp(te->desc, "OPERATOR CLASS") == 0 ||
|
|
|
|
strcmp(te->desc, "SCHEMA") == 0 ||
|
|
|
|
strcmp(te->desc, "TABLE") == 0 ||
|
|
|
|
strcmp(te->desc, "TYPE") == 0 ||
|
|
|
|
strcmp(te->desc, "VIEW") == 0 ||
|
|
|
|
strcmp(te->desc, "SEQUENCE") == 0)
|
|
|
|
{
|
|
|
|
PQExpBuffer temp = createPQExpBuffer();
|
|
|
|
|
|
|
|
appendPQExpBuffer(temp, "ALTER ");
|
2005-01-23 01:03:54 +01:00
|
|
|
_getObjectDescription(temp, te, AH);
|
2005-01-11 06:14:13 +01:00
|
|
|
appendPQExpBuffer(temp, " OWNER TO %s;", fmtId(te->owner));
|
|
|
|
ahprintf(AH, "%s\n\n", temp->data);
|
|
|
|
destroyPQExpBuffer(temp);
|
|
|
|
}
|
|
|
|
else if (strcmp(te->desc, "CAST") == 0 ||
|
|
|
|
strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
|
|
|
|
strcmp(te->desc, "DEFAULT") == 0 ||
|
|
|
|
strcmp(te->desc, "FK CONSTRAINT") == 0 ||
|
|
|
|
strcmp(te->desc, "PROCEDURAL LANGUAGE") == 0 ||
|
|
|
|
strcmp(te->desc, "RULE") == 0 ||
|
|
|
|
strcmp(te->desc, "TRIGGER") == 0)
|
|
|
|
{
|
|
|
|
/* these object types don't have separate owners */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
write_msg(modulename, "WARNING: don't know how to set owner for object type %s\n",
|
|
|
|
te->desc);
|
|
|
|
}
|
2003-09-24 01:31:52 +02:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2004-07-19 23:02:17 +02:00
|
|
|
/*
|
|
|
|
* If it's an ACL entry, it might contain SET SESSION AUTHORIZATION
|
2004-08-29 07:07:03 +02:00
|
|
|
* commands, so we can no longer assume we know the current auth
|
|
|
|
* setting.
|
2004-07-19 23:02:17 +02:00
|
|
|
*/
|
|
|
|
if (strncmp(te->desc, "ACL", 3) == 0)
|
|
|
|
{
|
|
|
|
if (AH->currUser)
|
|
|
|
free(AH->currUser);
|
|
|
|
AH->currUser = NULL;
|
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
WriteHead(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
struct tm crtm;
|
2000-07-21 13:40:08 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->WriteBufPtr) (AH, "PGDMP", 5); /* Magic code */
|
|
|
|
(*AH->WriteBytePtr) (AH, AH->vmaj);
|
|
|
|
(*AH->WriteBytePtr) (AH, AH->vmin);
|
|
|
|
(*AH->WriteBytePtr) (AH, AH->vrev);
|
|
|
|
(*AH->WriteBytePtr) (AH, AH->intSize);
|
2002-10-22 21:15:23 +02:00
|
|
|
(*AH->WriteBytePtr) (AH, AH->offSize);
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->WriteBytePtr) (AH, AH->format);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-06 20:39:39 +02:00
|
|
|
#ifndef HAVE_LIBZ
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->compression != 0)
|
2001-06-27 23:21:37 +02:00
|
|
|
write_msg(modulename, "WARNING: requested compression not available in this "
|
2003-07-23 10:47:41 +02:00
|
|
|
"installation -- archive will be uncompressed\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->compression = 0;
|
2000-07-21 13:40:08 +02:00
|
|
|
#endif
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
WriteInt(AH, AH->compression);
|
|
|
|
|
|
|
|
crtm = *localtime(&AH->createDate);
|
|
|
|
WriteInt(AH, crtm.tm_sec);
|
|
|
|
WriteInt(AH, crtm.tm_min);
|
|
|
|
WriteInt(AH, crtm.tm_hour);
|
|
|
|
WriteInt(AH, crtm.tm_mday);
|
|
|
|
WriteInt(AH, crtm.tm_mon);
|
|
|
|
WriteInt(AH, crtm.tm_year);
|
|
|
|
WriteInt(AH, crtm.tm_isdst);
|
2002-08-10 18:57:32 +02:00
|
|
|
WriteStr(AH, PQdb(AH->connection));
|
2004-11-06 20:36:02 +01:00
|
|
|
WriteStr(AH, AH->public.remoteVersionStr);
|
|
|
|
WriteStr(AH, PG_VERSION);
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
void
|
|
|
|
ReadHead(ArchiveHandle *AH)
|
2000-07-04 16:25:28 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
char tmpMag[7];
|
|
|
|
int fmt;
|
2000-07-21 13:40:08 +02:00
|
|
|
struct tm crtm;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
/* If we haven't already read the header... */
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!AH->readHeader)
|
|
|
|
{
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
(*AH->ReadBufPtr) (AH, tmpMag, 5);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (strncmp(tmpMag, "PGDMP", 5) != 0)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "did not find magic string in file header\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->vmaj = (*AH->ReadBytePtr) (AH);
|
|
|
|
AH->vmin = (*AH->ReadBytePtr) (AH);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0))) /* Version > 1.0 */
|
|
|
|
AH->vrev = (*AH->ReadBytePtr) (AH);
|
|
|
|
else
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->vrev = 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "unsupported version (%d.%d) in file header\n",
|
|
|
|
AH->vmaj, AH->vmin);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->intSize = (*AH->ReadBytePtr) (AH);
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->intSize > 32)
|
2002-08-20 19:54:45 +02:00
|
|
|
die_horribly(AH, modulename, "sanity check on integer size (%lu) failed\n",
|
|
|
|
(unsigned long) AH->intSize);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->intSize > sizeof(int))
|
2001-06-27 23:21:37 +02:00
|
|
|
write_msg(modulename, "WARNING: archive was made on a machine with larger integers, some operations may fail\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2002-10-22 21:15:23 +02:00
|
|
|
if (AH->version >= K_VERS_1_7)
|
2003-08-04 02:43:34 +02:00
|
|
|
AH->offSize = (*AH->ReadBytePtr) (AH);
|
2002-10-22 21:15:23 +02:00
|
|
|
else
|
2003-08-04 02:43:34 +02:00
|
|
|
AH->offSize = AH->intSize;
|
2002-10-22 21:15:23 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
fmt = (*AH->ReadBytePtr) (AH);
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->format != fmt)
|
2001-06-27 23:21:37 +02:00
|
|
|
die_horribly(AH, modulename, "expected format (%d) differs from format found in file (%d)\n",
|
|
|
|
AH->format, fmt);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->version >= K_VERS_1_2)
|
|
|
|
{
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->version < K_VERS_1_4)
|
2001-03-22 05:01:46 +01:00
|
|
|
AH->compression = (*AH->ReadBytePtr) (AH);
|
2000-07-21 13:40:08 +02:00
|
|
|
else
|
|
|
|
AH->compression = ReadInt(AH);
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
2000-07-21 13:40:08 +02:00
|
|
|
AH->compression = Z_DEFAULT_COMPRESSION;
|
2000-07-04 16:25:28 +02:00
|
|
|
|
2000-07-06 20:39:39 +02:00
|
|
|
#ifndef HAVE_LIBZ
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->compression != 0)
|
2003-07-23 10:47:41 +02:00
|
|
|
write_msg(modulename, "WARNING: archive is compressed, but this installation does not support compression -- no data will be available\n");
|
2000-07-04 16:25:28 +02:00
|
|
|
#endif
|
|
|
|
|
2000-07-21 13:40:08 +02:00
|
|
|
if (AH->version >= K_VERS_1_4)
|
|
|
|
{
|
|
|
|
crtm.tm_sec = ReadInt(AH);
|
|
|
|
crtm.tm_min = ReadInt(AH);
|
|
|
|
crtm.tm_hour = ReadInt(AH);
|
|
|
|
crtm.tm_mday = ReadInt(AH);
|
|
|
|
crtm.tm_mon = ReadInt(AH);
|
|
|
|
crtm.tm_year = ReadInt(AH);
|
|
|
|
crtm.tm_isdst = ReadInt(AH);
|
|
|
|
|
|
|
|
AH->archdbname = ReadStr(AH);
|
|
|
|
|
|
|
|
AH->createDate = mktime(&crtm);
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (AH->createDate == (time_t) -1)
|
2001-07-03 22:21:50 +02:00
|
|
|
write_msg(modulename, "WARNING: invalid creation date in header\n");
|
2000-07-21 13:40:08 +02:00
|
|
|
}
|
|
|
|
|
2004-11-06 20:36:02 +01:00
|
|
|
if (AH->version >= K_VERS_1_10)
|
|
|
|
{
|
|
|
|
AH->archiveRemoteVersion = ReadStr(AH);
|
|
|
|
AH->archiveDumpVersion = ReadStr(AH);
|
|
|
|
}
|
|
|
|
|
2000-07-04 16:25:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-25 03:33:17 +02:00
|
|
|
/*
|
|
|
|
* checkSeek
|
|
|
|
* check to see if fseek can be performed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool
|
|
|
|
checkSeek(FILE *fp)
|
|
|
|
{
|
|
|
|
|
2002-10-25 05:47:30 +02:00
|
|
|
if (fseeko(fp, 0, SEEK_CUR) != 0)
|
2002-10-25 03:33:17 +02:00
|
|
|
return false;
|
|
|
|
else if (sizeof(off_t) > sizeof(long))
|
2003-08-04 02:43:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, off_t is too large for long, so we return based
|
|
|
|
* on whether an off_t version of fseek is available.
|
|
|
|
*/
|
2002-10-25 03:33:17 +02:00
|
|
|
#ifdef HAVE_FSEEKO
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|