pg_backup_archiver.c 56.8 KB
Newer Older
B
Bruce Momjian 已提交
1 2 3 4 5 6 7 8 9 10
/*-------------------------------------------------------------------------
 *
 * 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
B
Bruce Momjian 已提交
11
 *	as this notice is not removed.
B
Bruce Momjian 已提交
12 13 14 15 16 17
 *
 *	The author is not responsible for loss or damages that may
 *	result from it's use.
 *
 *
 * IDENTIFICATION
18
 *		$Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.48 2002/05/29 01:38:56 tgl Exp $
B
Bruce Momjian 已提交
19 20 21
 *
 * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
 *
B
Bruce Momjian 已提交
22
 *		Initial version.
23 24 25
 *
 * Modifications - 31-Jul-2000 - pjw@rhyme.com.au (1.46, 1.47)
 *		Fixed version number initialization in _allocAH (pg_backup_archiver.c)
B
Bruce Momjian 已提交
26
 *
27 28 29 30
 *
 * Modifications - 30-Oct-2000 - pjw@rhyme.com.au
 *		Added {Start,End}RestoreBlobs to allow extended TX during BLOB restore.
 *
31
 * Modifications - 04-Jan-2001 - pjw@rhyme.com.au
B
Bruce Momjian 已提交
32 33 34
 *	  - strdup() the current user just in case it's deallocated from it's TOC
 *		entry. Should *never* happen, but that's what they said about the
 *		Titanic...
35 36 37
 *
 *	  - Check results of IO routines more carefully.
 *
38 39 40
 * Modifications - 27-Jan-2001 - pjw@rhyme.com.au
 *	  - When dropping the schema, reconnect as owner of each object.
 *
41 42 43
 * Modifications - 6-Mar-2001 - pjw@rhyme.com.au
 *	  - Only disable triggers in DataOnly (or implied data-only) restores.
 *
44
 * Modifications - 31-Mar-2001 - pjw@rhyme.com.au
45
 *
46 47 48 49 50
 *	  - Rudimentary support for dependencies in archives. Current implementation
 *		uses dependencies to modify the OID used in sorting TOC entries.
 *		This will NOT handle multi-level dependencies, but will manage simple
 *		relationships like UDTs & their functions.
 *
51
 *	  - Treat OIDs with more respect (avoid using ints, use macros for
52 53
 *		conversion & comparison).
 *
54 55 56 57
 * Modifications - 10-May-2001 - pjw@rhyme.com.au
 *	  - Treat SEQUENCE SET TOC entries as data entries rather than schema
 *		entries.
 *	  - Make allowance for data entries that did not have a data dumper
58
 *		routine (eg. SEQUENCE SET)
59
 *
60 61
 * Modifications - 01-Nov-2001 - pjw@rhyme.com.au
 *	  - Fix handling of {data/schema}-only restores when using a full
62
 *		backup file; prior version was restoring schema in data-only
63 64
 *		restores. Added enum to make code easier to understand.
 *
65 66
 * Modifications - 18-Jan-2002 - pjw@rhyme.com.au
 *	  - Modified _tocEntryRequired to handle '<Init>/Max OID' as a special
67
 *		case (ie. as a DATA item) as per bugs reported by Bruce Momjian
68 69
 *		around 17-Jan-2002.
 *
B
Bruce Momjian 已提交
70 71 72 73 74
 *-------------------------------------------------------------------------
 */

#include "pg_backup.h"
#include "pg_backup_archiver.h"
75 76
#include "pg_backup_db.h"

77
#include <ctype.h>
78
#include <errno.h>
B
Bruce Momjian 已提交
79
#include <unistd.h>				/* for dup */
B
Bruce Momjian 已提交
80

81 82 83
#include "pqexpbuffer.h"
#include "libpq/libpq-fs.h"

84 85
typedef enum _teReqs_
{
86
	REQ_SCHEMA = 1,
87 88
	REQ_DATA = 2,
	REQ_ALL = REQ_SCHEMA + REQ_DATA
89 90
} teReqs;

B
Bruce Momjian 已提交
91 92 93 94 95
static void _SortToc(ArchiveHandle *AH, TocSortCompareFn fn);
static int	_tocSortCompareByOIDNum(const void *p1, const void *p2);
static int	_tocSortCompareByIDNum(const void *p1, const void *p2);
static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
		 const int compression, ArchiveMode mode);
96
static int	_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData);
97

98
static void _doSetSessionAuth(ArchiveHandle *AH, const char *autharg);
B
Bruce Momjian 已提交
99
static void _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te);
100
static void _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user);
101
static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
102

103
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt);
B
Bruce Momjian 已提交
104 105 106 107 108 109
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
static TocEntry *_getTocEntry(ArchiveHandle *AH, int id);
static void _moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te);
static void _moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te);
static int	_discoverArchiveFormat(ArchiveHandle *AH);
110
static void _fixupOidInfo(TocEntry *te);
111
static Oid	_findMaxOID(const char *((*deps)[]));
112

113
const char *progname;
114
static char *modulename = gettext_noop("archiver");
B
Bruce Momjian 已提交
115

116 117
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);
118

B
Bruce Momjian 已提交
119 120
static int	_canRestoreBlobs(ArchiveHandle *AH);
static int	_restoringToDB(ArchiveHandle *AH);
121

B
Bruce Momjian 已提交
122
/*
B
Bruce Momjian 已提交
123 124 125 126
 *	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.
B
Bruce Momjian 已提交
127 128 129 130 131 132
 *
 */


/* Create a new archive */
/* Public */
133
Archive *
B
Bruce Momjian 已提交
134 135
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
			  const int compression)
136

B
Bruce Momjian 已提交
137
{
B
Bruce Momjian 已提交
138 139 140
	ArchiveHandle *AH = _allocAH(FileSpec, fmt, compression, archModeWrite);

	return (Archive *) AH;
B
Bruce Momjian 已提交
141 142 143 144
}

/* Open an existing archive */
/* Public */
145
Archive *
B
Bruce Momjian 已提交
146
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
B
Bruce Momjian 已提交
147
{
B
Bruce Momjian 已提交
148 149 150
	ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, archModeRead);

	return (Archive *) AH;
B
Bruce Momjian 已提交
151 152 153
}

/* Public */
B
Bruce Momjian 已提交
154 155
void
CloseArchive(Archive *AHX)
B
Bruce Momjian 已提交
156
{
B
Bruce Momjian 已提交
157 158 159 160
	int			res = 0;
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	(*AH->ClosePtr) (AH);
B
Bruce Momjian 已提交
161

B
Bruce Momjian 已提交
162 163
	/* Close the output */
	if (AH->gzOut)
164
		res = GZCLOSE(AH->OF);
B
Bruce Momjian 已提交
165
	else if (AH->OF != stdout)
166 167 168
		res = fclose(AH->OF);

	if (res != 0)
169
		die_horribly(AH, modulename, "could not close the output file in CloseArchive\n");
B
Bruce Momjian 已提交
170 171 172
}

/* Public */
B
Bruce Momjian 已提交
173 174
void
RestoreArchive(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
175
{
B
Bruce Momjian 已提交
176 177
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
178
	teReqs		reqs;
B
Bruce Momjian 已提交
179 180
	OutputContext sav;
	int			impliedDataOnly;
181
	bool		defnDumped;
B
Bruce Momjian 已提交
182

183 184
	AH->ropt = ropt;

185 186 187 188 189
	/*
	 * 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
190 191 192
	 * 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...
193
	 */
194
	if (ropt->create && ropt->noReconnect)
195
		die_horribly(AH, modulename, "-C and -R are incompatible options\n");
196

197 198 199
	if (ropt->create && ropt->dropSchema)
		die_horribly(AH, modulename, "-C and -c are incompatible options\n");

200 201 202 203 204
	/*
	 * If we're using a DB connection, then connect it.
	 */
	if (ropt->useDB)
	{
205
		ahlog(AH, 1, "connecting to database for restore\n");
206
		if (AH->version < K_VERS_1_3)
207
			die_horribly(AH, modulename, "direct database connections are not supported in pre-1.3 archives\n");
208

209 210 211 212
		/* XXX Should get this from the archive */
		AHX->minRemoteVersion = 070100;
		AHX->maxRemoteVersion = 999999;

213 214
		ConnectDatabase(AHX, ropt->dbname,
						ropt->pghost, ropt->pgport, ropt->username,
B
Bruce Momjian 已提交
215
						ropt->requirePassword, ropt->ignoreVersion);
216 217
	}

218
	/*
219
	 * Work out if we have an implied data-only restore. This can happen if
B
Bruce Momjian 已提交
220 221 222
	 * the dump was data only or if the user has used a toc list to
	 * exclude all of the schema data. All we do is look for schema
	 * entries - if none are found then we set the dataOnly flag.
223
	 *
B
Bruce Momjian 已提交
224
	 * We could scan for wanted TABLE entries, but that is not the same as
225
	 * dataOnly. At this stage, it seems unnecessary (6-Mar-2001).
B
Bruce Momjian 已提交
226 227 228
	 */
	if (!ropt->dataOnly)
	{
229 230
		te = AH->toc->next;
		impliedDataOnly = 1;
B
Bruce Momjian 已提交
231 232
		while (te != AH->toc)
		{
233
			reqs = _tocEntryRequired(te, ropt);
234
			if ((reqs & REQ_SCHEMA) != 0)
B
Bruce Momjian 已提交
235
			{					/* It's schema, and it's wanted */
236 237 238 239 240 241 242 243
				impliedDataOnly = 0;
				break;
			}
			te = te->next;
		}
		if (impliedDataOnly)
		{
			ropt->dataOnly = impliedDataOnly;
244
			ahlog(AH, 1, "implied data-only restore\n");
245
		}
B
Bruce Momjian 已提交
246
	}
247

248
	/*
B
Bruce Momjian 已提交
249
	 * Setup the output file if necessary.
250
	 */
B
Bruce Momjian 已提交
251
	if (ropt->filename || ropt->compression)
252
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
253

B
Bruce Momjian 已提交
254
	ahprintf(AH, "--\n-- Selected TOC Entries:\n--\n");
B
Bruce Momjian 已提交
255

B
Bruce Momjian 已提交
256 257
	/*
	 * Drop the items at the start, in reverse order
258
	 */
B
Bruce Momjian 已提交
259 260
	if (ropt->dropSchema)
	{
261
		te = AH->toc->prev;
B
Bruce Momjian 已提交
262 263
		while (te != AH->toc)
		{
264
			reqs = _tocEntryRequired(te, ropt);
265
			if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt)
266 267 268
			{
				/* We want the schema */
				ahlog(AH, 1, "dropping %s %s\n", te->desc, te->name);
269
				/* Select owner and schema as necessary */
270
				_reconnectAsOwner(AH, NULL, te);
271
				_selectOutputSchema(AH, te->namespace);
272
				/* Drop it */
273 274 275 276
				ahprintf(AH, "%s", te->dropStmt);
			}
			te = te->prev;
		}
B
Bruce Momjian 已提交
277
	}
B
Bruce Momjian 已提交
278

279 280 281
	/*
	 * Now process each TOC entry
	 */
B
Bruce Momjian 已提交
282 283 284
	te = AH->toc->next;
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
285

286 287 288
		/* Work out what, if anything, we want from this entry */
		reqs = _tocEntryRequired(te, ropt);

289 290 291
		/* Dump any relevant dump warnings to stderr */
		if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
		{
292
			if (!ropt->dataOnly && te->defn != NULL && strlen(te->defn) != 0)
293 294 295
				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);
296
		}
297

298 299
		defnDumped = false;

300
		if ((reqs & REQ_SCHEMA) != 0)	/* We want the schema */
301
		{
302
			ahlog(AH, 1, "creating %s %s\n", te->desc, te->name);
303
			_printTocEntry(AH, te, ropt, false);
304
			defnDumped = true;
305 306

			/* If we created a DB, connect to it... */
B
Bruce Momjian 已提交
307
			if (strcmp(te->desc, "DATABASE") == 0)
308
			{
309
				ahlog(AH, 1, "connecting to new database %s as user %s\n", te->name, te->owner);
310 311
				_reconnectAsUser(AH, te->name, te->owner);
			}
312
		}
B
Bruce Momjian 已提交
313

B
Bruce Momjian 已提交
314
		/*
315
		 * If we have a data component, then process it
316
		 */
317
		if ((reqs & REQ_DATA) != 0)
B
Bruce Momjian 已提交
318
		{
319 320 321 322 323
			/*
			 * 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.
324
			 */
325
			if (te->hadDumper)
326 327
			{
				/*
328
				 * If we can output the data, then restore it.
329
				 */
330
				if (AH->PrintTocDataPtr !=NULL && (reqs & REQ_DATA) != 0)
331 332 333
				{
#ifndef HAVE_LIBZ
					if (AH->compression != 0)
334
						die_horribly(AH, modulename, "unable to restore from compressed archive (not configured for compression support)\n");
335
#endif
336

337 338 339
					_printTocEntry(AH, te, ropt, true);

					/*
340 341
					 * Maybe we can't do BLOBS, so check if this node is
					 * for BLOBS
342 343 344 345 346 347
					 */
					if ((strcmp(te->desc, "BLOBS") == 0) && !_canRestoreBlobs(AH))
					{
						ahprintf(AH, "--\n-- SKIPPED \n--\n\n");

						/*
348 349 350
						 * This is a bit nasty - we assume, for the
						 * moment, that if a custom output is used, then
						 * we don't want warnings.
351 352
						 */
						if (!AH->CustomOutPtr)
353
							write_msg(modulename, "WARNING: skipping large object restoration\n");
354 355 356 357 358 359 360 361

					}
					else
					{

						_disableTriggersIfNecessary(AH, te, ropt);

						/*
362 363
						 * Reconnect if necessary (_disableTriggers may
						 * have reconnected)
364
						 */
365
						_reconnectAsOwner(AH, NULL, te);
366
						_selectOutputSchema(AH, te->namespace);
367

368
						ahlog(AH, 1, "restoring data for table %s\n", te->name);
369 370

						/*
371 372 373 374 375
						 * 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.
376
						 *
377 378 379
						 * For V1.3+, the table data MUST have a copy
						 * statement so that we can go into appropriate
						 * mode with libpq.
380 381 382 383 384 385 386 387 388
						 */
						if (te->copyStmt && strlen(te->copyStmt) > 0)
							ahprintf(AH, te->copyStmt);

						(*AH->PrintTocDataPtr) (AH, te, ropt);

						_enableTriggersIfNecessary(AH, te, ropt);
					}
				}
389 390 391 392
			}
			else if (!defnDumped)
			{
				/* If we haven't already dumped the defn part, do so now */
393
				ahlog(AH, 1, "executing %s %s\n", te->desc, te->name);
394
				_printTocEntry(AH, te, ropt, false);
395 396 397
			}
		}
		te = te->next;
B
Bruce Momjian 已提交
398
	}
B
Bruce Momjian 已提交
399

400
	/*
B
Bruce Momjian 已提交
401 402
	 * Now use blobs_xref (if used) to fixup any refs for tables that we
	 * loaded
403 404 405
	 */
	if (_canRestoreBlobs(AH) && AH->createdBlobXref)
	{
406
		/* NULL parameter means disable ALL user triggers */
407
		_disableTriggersIfNecessary(AH, NULL, ropt);
408

409
		te = AH->toc->next;
B
Bruce Momjian 已提交
410 411
		while (te != AH->toc)
		{
412 413

			/* Is it table data? */
B
Bruce Momjian 已提交
414 415
			if (strcmp(te->desc, "TABLE DATA") == 0)
			{
416

417
				ahlog(AH, 2, "checking whether we loaded %s\n", te->name);
418 419 420

				reqs = _tocEntryRequired(te, ropt);

421
				if ((reqs & REQ_DATA) != 0)		/* We loaded the data */
422
				{
423
					ahlog(AH, 1, "fixing up large object cross-reference for %s\n", te->name);
424
					FixupBlobRefs(AH, te);
425 426 427
				}
			}
			else
428
				ahlog(AH, 2, "ignoring large object cross-references for %s %s\n", te->desc, te->name);
429 430 431

			te = te->next;
		}
432 433

		/* NULL parameter means enable ALL user triggers */
434
		_enableTriggersIfNecessary(AH, NULL, ropt);
435 436 437 438 439
	}

	/*
	 * Clean up & we're done.
	 */
B
Bruce Momjian 已提交
440
	if (ropt->filename)
441
		ResetOutput(AH, sav);
B
Bruce Momjian 已提交
442

443 444 445 446
	if (ropt->useDB)
	{
		PQfinish(AH->connection);
		AH->connection = NULL;
447 448 449 450 451 452

		if (AH->blobConnection)
		{
			PQfinish(AH->blobConnection);
			AH->blobConnection = NULL;
		}
453
	}
B
Bruce Momjian 已提交
454 455
}

456 457 458 459
/*
 * Allocate a new RestoreOptions block.
 * This is mainly so we can initialize it, but also for future expansion,
 */
B
Bruce Momjian 已提交
460 461
RestoreOptions *
NewRestoreOptions(void)
B
Bruce Momjian 已提交
462
{
B
Bruce Momjian 已提交
463
	RestoreOptions *opts;
B
Bruce Momjian 已提交
464

B
Bruce Momjian 已提交
465
	opts = (RestoreOptions *) calloc(1, sizeof(RestoreOptions));
B
Bruce Momjian 已提交
466 467

	opts->format = archUnknown;
468
	opts->suppressDumpWarnings = false;
B
Bruce Momjian 已提交
469 470 471 472

	return opts;
}

473 474 475 476
/*
 * Returns true if we're restoring directly to the database (and
 * aren't just making a psql script that can do the restoration).
 */
B
Bruce Momjian 已提交
477 478
static int
_restoringToDB(ArchiveHandle *AH)
479 480 481 482
{
	return (AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
483 484
static int
_canRestoreBlobs(ArchiveHandle *AH)
485 486 487 488
{
	return _restoringToDB(AH);
}

B
Bruce Momjian 已提交
489 490
static void
_disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
491
{
492 493
	char	   *oldUser;
	char	   *oldSchema;
494

495 496
	/* This hack is only needed in a data-only restore */
	if (!ropt->dataOnly || !ropt->disable_triggers)
497 498
		return;

499 500 501
	oldUser = strdup(AH->currUser);
	oldSchema = strdup(AH->currSchema);

502
	/*
503 504 505 506
	 * Become superuser if possible, since they are the only ones
	 * who can update pg_class.  If -S was not given, but we are allowed
	 * to use SET SESSION AUTHORIZATION, assume the initial user identity
	 * is a superuser.  Otherwise we just have to bull ahead anyway.
507 508 509
	 */
	if (ropt->superuser)
	{
510 511 512 513 514 515 516
		_reconnectAsUser(AH, NULL, ropt->superuser);
		/* be careful to preserve schema setting */
		_selectOutputSchema(AH, oldSchema);
	}
	else if (AH->ropt->use_setsessauth)
	{
		_doSetSessionAuth(AH, "DEFAULT");
517 518
	}

519
	ahlog(AH, 1, "disabling triggers\n");
520 521

	/*
B
Bruce Momjian 已提交
522 523
	 * Disable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
524
	 */
B
Bruce Momjian 已提交
525
	ahprintf(AH, "-- Disable triggers\n");
526 527

	/*
528 529
	 * Just update the AFFECTED table, if known.  Otherwise update all
	 * non-system tables.
530 531
	 */
	if (te && te->name && strlen(te->name) > 0)
532 533
		ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 "
				 "WHERE oid = '%s'::pg_catalog.regclass;\n\n",
534
				 fmtId(te->name, false));
535
	else
536
		ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 FROM pg_catalog.pg_namespace "
537
				 "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
538 539

	/*
540
	 * Restore original user and schema state.
541
	 */
542
	if (ropt->superuser)
543
	{
544
		_reconnectAsUser(AH, NULL, oldUser);
545 546
		/* be careful to preserve schema setting */
		_selectOutputSchema(AH, oldSchema);
547
	}
548 549 550 551 552 553
	else if (AH->ropt->use_setsessauth)
	{
		_doSetSessionAuth(AH, fmtId(oldUser, false));
	}
	free(oldUser);
	free(oldSchema);
B
Bruce Momjian 已提交
554 555
}

B
Bruce Momjian 已提交
556 557
static void
_enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
558
{
559 560
	char	   *oldUser;
	char	   *oldSchema;
561

562 563
	/* This hack is only needed in a data-only restore */
	if (!ropt->dataOnly || !ropt->disable_triggers)
564 565
		return;

566 567 568
	oldUser = strdup(AH->currUser);
	oldSchema = strdup(AH->currSchema);

569
	/*
570 571 572 573
	 * Become superuser if possible, since they are the only ones
	 * who can update pg_class.  If -S was not given, but we are allowed
	 * to use SET SESSION AUTHORIZATION, assume the initial user identity
	 * is a superuser.  Otherwise we just have to bull ahead anyway.
574 575 576
	 */
	if (ropt->superuser)
	{
577 578 579 580 581 582 583
		_reconnectAsUser(AH, NULL, ropt->superuser);
		/* be careful to preserve schema setting */
		_selectOutputSchema(AH, oldSchema);
	}
	else if (AH->ropt->use_setsessauth)
	{
		_doSetSessionAuth(AH, "DEFAULT");
584 585
	}

586
	ahlog(AH, 1, "enabling triggers\n");
587 588

	/*
B
Bruce Momjian 已提交
589 590
	 * Enable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
591
	 */
B
Bruce Momjian 已提交
592
	ahprintf(AH, "-- Enable triggers\n");
593 594

	/*
595 596
	 * Just update the AFFECTED table, if known.  Otherwise update all
	 * non-system tables.
597
	 */
598
	if (te && te->name && strlen(te->name) > 0)
599 600 601
		ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
				 "(SELECT count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
				 "WHERE oid = '%s'::pg_catalog.regclass;\n\n",
602
				 fmtId(te->name, false));
B
Bruce Momjian 已提交
603
	else
604 605 606
		ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
				 "(SELECT count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
				 "FROM pg_catalog.pg_namespace "
607
				 "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
B
Bruce Momjian 已提交
608

609
	/*
610
	 * Restore original user and schema state.
611
	 */
612
	if (ropt->superuser)
613
	{
614
		_reconnectAsUser(AH, NULL, oldUser);
615 616
		/* be careful to preserve schema setting */
		_selectOutputSchema(AH, oldSchema);
617
	}
618 619 620 621 622 623
	else if (AH->ropt->use_setsessauth)
	{
		_doSetSessionAuth(AH, fmtId(oldUser, false));
	}
	free(oldUser);
	free(oldSchema);
624
}
B
Bruce Momjian 已提交
625 626

/*
627
 * This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
B
Bruce Momjian 已提交
628 629 630
 */

/* Public */
B
Bruce Momjian 已提交
631 632
int
WriteData(Archive *AHX, const void *data, int dLen)
B
Bruce Momjian 已提交
633
{
B
Bruce Momjian 已提交
634
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
B
Bruce Momjian 已提交
635

636
	if (!AH->currToc)
637
		die_horribly(AH, modulename, "WriteData cannot be called outside the context of a DataDumper routine\n");
638

B
Bruce Momjian 已提交
639
	return (*AH->WriteDataPtr) (AH, data, dLen);
B
Bruce Momjian 已提交
640 641 642
}

/*
B
Bruce Momjian 已提交
643
 * Create a new TOC entry. The TOC was designed as a TOC, but is now the
B
Bruce Momjian 已提交
644 645 646 647
 * repository for all metadata. But the name has stuck.
 */

/* Public */
B
Bruce Momjian 已提交
648 649
void
ArchiveEntry(Archive *AHX, const char *oid, const char *name,
650 651 652 653
			 const char *namespace, const char *owner,
			 const char *desc, const char *((*deps)[]),
			 const char *defn, const char *dropStmt,
			 const char *copyStmt,
B
Bruce Momjian 已提交
654
			 DataDumperPtr dumpFn, void *dumpArg)
B
Bruce Momjian 已提交
655
{
B
Bruce Momjian 已提交
656 657 658 659 660 661 662 663
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *newToc;

	AH->lastID++;
	AH->tocCount++;

	newToc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!newToc)
664
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
665 666 667 668 669 670 671

	newToc->prev = AH->toc->prev;
	newToc->next = AH->toc;
	AH->toc->prev->next = newToc;
	AH->toc->prev = newToc;

	newToc->id = AH->lastID;
672

B
Bruce Momjian 已提交
673
	newToc->name = strdup(name);
674 675
	newToc->namespace = namespace ? strdup(namespace) : NULL;
	newToc->owner = strdup(owner);
B
Bruce Momjian 已提交
676
	newToc->desc = strdup(desc);
677 678 679
	newToc->defn = strdup(defn);
	newToc->dropStmt = strdup(dropStmt);
	newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
680 681

	newToc->oid = strdup(oid);
682
	newToc->depOid = deps;		/* NB: not copied */
683 684
	_fixupOidInfo(newToc);

B
Bruce Momjian 已提交
685 686
	newToc->printed = 0;
	newToc->formatData = NULL;
687 688
	newToc->dataDumper = dumpFn;
	newToc->dataDumperArg = dumpArg;
B
Bruce Momjian 已提交
689

B
Bruce Momjian 已提交
690
	newToc->hadDumper = dumpFn ? 1 : 0;
B
Bruce Momjian 已提交
691

B
Bruce Momjian 已提交
692 693
	if (AH->ArchiveEntryPtr !=NULL)
		(*AH->ArchiveEntryPtr) (AH, newToc);
B
Bruce Momjian 已提交
694

B
Bruce Momjian 已提交
695 696 697 698
	/*
	 * printf("New toc owned by '%s', oid %d\n", newToc->owner,
	 * newToc->oidVal);
	 */
B
Bruce Momjian 已提交
699 700 701
}

/* Public */
B
Bruce Momjian 已提交
702 703
void
PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
704
{
B
Bruce Momjian 已提交
705 706 707 708
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	OutputContext sav;
	char	   *fmtName;
B
Bruce Momjian 已提交
709

B
Bruce Momjian 已提交
710 711
	if (ropt->filename)
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
712

713 714
	ahprintf(AH, ";\n; Archive created at %s", ctime(&AH->createDate));
	ahprintf(AH, ";     dbname: %s\n;     TOC Entries: %d\n;     Compression: %d\n",
B
Bruce Momjian 已提交
715
			 AH->archdbname, AH->tocCount, AH->compression);
716

B
Bruce Momjian 已提交
717 718
	switch (AH->format)
	{
719 720 721 722 723 724 725 726 727 728 729 730
		case archFiles:
			fmtName = "FILES";
			break;
		case archCustom:
			fmtName = "CUSTOM";
			break;
		case archTar:
			fmtName = "TAR";
			break;
		default:
			fmtName = "UNKNOWN";
	}
731 732

	ahprintf(AH, ";     Dump Version: %d.%d-%d\n", AH->vmaj, AH->vmin, AH->vrev);
733 734
	ahprintf(AH, ";     Format: %s\n;\n", fmtName);

B
Bruce Momjian 已提交
735
	ahprintf(AH, ";\n; Selected TOC Entries:\n;\n");
B
Bruce Momjian 已提交
736

B
Bruce Momjian 已提交
737 738 739 740
	while (te != AH->toc)
	{
		if (_tocEntryRequired(te, ropt) != 0)
			ahprintf(AH, "%d; %d %s %s %s\n", te->id, te->oidVal, te->desc, te->name, te->owner);
B
Bruce Momjian 已提交
741
		te = te->next;
B
Bruce Momjian 已提交
742
	}
B
Bruce Momjian 已提交
743

B
Bruce Momjian 已提交
744 745
	if (ropt->filename)
		ResetOutput(AH, sav);
B
Bruce Momjian 已提交
746 747
}

748 749 750 751 752
/***********
 * BLOB Archival
 ***********/

/* Called by a dumper to signal start of a BLOB */
B
Bruce Momjian 已提交
753
int
754
StartBlob(Archive *AHX, Oid oid)
755
{
B
Bruce Momjian 已提交
756
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
757

B
Bruce Momjian 已提交
758
	if (!AH->StartBlobPtr)
759
		die_horribly(AH, modulename, "large object output not supported in chosen format\n");
760

B
Bruce Momjian 已提交
761
	(*AH->StartBlobPtr) (AH, AH->currToc, oid);
762

B
Bruce Momjian 已提交
763
	return 1;
764 765 766
}

/* Called by a dumper to signal end of a BLOB */
B
Bruce Momjian 已提交
767
int
768
EndBlob(Archive *AHX, Oid oid)
769
{
B
Bruce Momjian 已提交
770
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
771

B
Bruce Momjian 已提交
772 773
	if (AH->EndBlobPtr)
		(*AH->EndBlobPtr) (AH, AH->currToc, oid);
774

B
Bruce Momjian 已提交
775
	return 1;
776 777 778 779 780 781
}

/**********
 * BLOB Restoration
 **********/

782
/*
B
Bruce Momjian 已提交
783
 * Called by a format handler before any blobs are restored
784
 */
B
Bruce Momjian 已提交
785 786
void
StartRestoreBlobs(ArchiveHandle *AH)
787 788 789 790 791
{
	AH->blobCount = 0;
}

/*
B
Bruce Momjian 已提交
792
 * Called by a format handler after all blobs are restored
793
 */
B
Bruce Momjian 已提交
794 795
void
EndRestoreBlobs(ArchiveHandle *AH)
796 797 798
{
	if (AH->txActive)
	{
799
		ahlog(AH, 2, "committing large object transactions\n");
800 801 802 803 804 805
		CommitTransaction(AH);
	}

	if (AH->blobTxActive)
		CommitTransactionXref(AH);

806
	ahlog(AH, 1, "restored %d large objects\n", AH->blobCount);
807 808 809
}


810 811 812
/*
 * Called by a format handler to initiate restoration of a blob
 */
B
Bruce Momjian 已提交
813
void
814
StartRestoreBlob(ArchiveHandle *AH, Oid oid)
815
{
816
	Oid			loOid;
817

818 819
	AH->blobCount++;

820 821 822
	if (!AH->createdBlobXref)
	{
		if (!AH->connection)
823
			die_horribly(AH, modulename, "cannot restore large objects without a database connection\n");
824 825 826 827 828

		CreateBlobXrefTable(AH);
		AH->createdBlobXref = 1;
	}

829 830 831
	/* Initialize the LO Buffer */
	AH->lo_buf_used = 0;

832 833 834 835 836
	/*
	 * Start long-running TXs if necessary
	 */
	if (!AH->txActive)
	{
837
		ahlog(AH, 2, "starting large object transactions\n");
838 839 840 841
		StartTransaction(AH);
	}
	if (!AH->blobTxActive)
		StartTransactionXref(AH);
842

843 844
	loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
	if (loOid == 0)
845
		die_horribly(AH, modulename, "could not create large object\n");
846

847
	ahlog(AH, 2, "restoring large object with oid %u as %u\n", oid, loOid);
848 849 850 851 852

	InsertBlobXref(AH, oid, loOid);

	AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
	if (AH->loFd == -1)
853
		die_horribly(AH, modulename, "could not open large object\n");
854

B
Bruce Momjian 已提交
855
	AH->writingBlob = 1;
856 857
}

B
Bruce Momjian 已提交
858
void
859
EndRestoreBlob(ArchiveHandle *AH, Oid oid)
860
{
861 862 863 864 865 866 867 868 869 870 871 872 873 874
	if (AH->lo_buf_used > 0)
	{
		/* Write remaining bytes from the LO buffer */
		int res;

		res = lo_write(AH->connection, AH->loFd, (void *) AH->lo_buf, AH->lo_buf_used);

		ahlog(AH, 5, "wrote remaining %d bytes of large object data (result = %d)\n",
			  (int)AH->lo_buf_used, res);
		if (res != AH->lo_buf_used)
			die_horribly(AH, modulename, "could not write to large object (result: %d, expected: %d)\n",
						 res, AH->lo_buf_used);
		AH->lo_buf_used = 0;
	}
875

B
Bruce Momjian 已提交
876 877
	lo_close(AH->connection, AH->loFd);
	AH->writingBlob = 0;
878

879 880 881
	/*
	 * Commit every BLOB_BATCH_SIZE blobs...
	 */
B
Bruce Momjian 已提交
882
	if (((AH->blobCount / BLOB_BATCH_SIZE) * BLOB_BATCH_SIZE) == AH->blobCount)
883
	{
884
		ahlog(AH, 2, "committing large object transactions\n");
885 886 887
		CommitTransaction(AH);
		CommitTransactionXref(AH);
	}
888 889
}

B
Bruce Momjian 已提交
890 891 892 893 894 895 896
/***********
 * Sorting and Reordering
 ***********/

/*
 * Move TOC entries of the specified type to the START of the TOC.
 */
897

B
Bruce Momjian 已提交
898
/* Public */
B
Bruce Momjian 已提交
899 900
void
MoveToStart(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
901
{
B
Bruce Momjian 已提交
902 903 904
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;
B
Bruce Momjian 已提交
905

B
Bruce Momjian 已提交
906 907
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
908 909 910
		te->_moved = 0;
		te = te->next;
	}
B
Bruce Momjian 已提交
911 912 913 914 915 916 917

	te = AH->toc->prev;
	while (te != AH->toc && !te->_moved)
	{
		newTe = te->prev;
		if (strcmp(te->desc, oType) == 0)
			_moveAfter(AH, AH->toc, te);
B
Bruce Momjian 已提交
918
		te = newTe;
B
Bruce Momjian 已提交
919
	}
B
Bruce Momjian 已提交
920 921 922 923 924 925 926
}


/*
 * Move TOC entries of the specified type to the end of the TOC.
 */
/* Public */
B
Bruce Momjian 已提交
927 928
void
MoveToEnd(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
929
{
B
Bruce Momjian 已提交
930 931 932 933 934 935 936 937
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;

	while (te != AH->toc)
	{
		te->_moved = 0;
		te = te->next;
B
Bruce Momjian 已提交
938
	}
B
Bruce Momjian 已提交
939 940 941 942 943 944 945

	te = AH->toc->next;
	while (te != AH->toc && !te->_moved)
	{
		newTe = te->next;
		if (strcmp(te->desc, oType) == 0)
			_moveBefore(AH, AH->toc, te);
B
Bruce Momjian 已提交
946
		te = newTe;
B
Bruce Momjian 已提交
947
	}
B
Bruce Momjian 已提交
948 949
}

B
Bruce Momjian 已提交
950
/*
B
Bruce Momjian 已提交
951 952 953
 * Sort TOC by OID
 */
/* Public */
B
Bruce Momjian 已提交
954 955
void
SortTocByOID(Archive *AHX)
B
Bruce Momjian 已提交
956
{
B
Bruce Momjian 已提交
957 958 959
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByOIDNum);
B
Bruce Momjian 已提交
960 961 962 963 964 965
}

/*
 * Sort TOC by ID
 */
/* Public */
B
Bruce Momjian 已提交
966 967
void
SortTocByID(Archive *AHX)
B
Bruce Momjian 已提交
968
{
B
Bruce Momjian 已提交
969 970 971
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByIDNum);
B
Bruce Momjian 已提交
972 973
}

B
Bruce Momjian 已提交
974 975
void
SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
976
{
B
Bruce Momjian 已提交
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	FILE	   *fh;
	char		buf[1024];
	char	   *cmnt;
	char	   *endptr;
	int			id;
	TocEntry   *te;
	TocEntry   *tePrev;
	int			i;

	/* Allocate space for the 'wanted' array, and init it */
	ropt->idWanted = (int *) malloc(sizeof(int) * AH->tocCount);
	for (i = 0; i < AH->tocCount; i++)
		ropt->idWanted[i] = 0;

	ropt->limitToList = 1;

	/* Mark all entries as 'not moved' */
	te = AH->toc->next;
	while (te != AH->toc)
	{
		te->_moved = 0;
		te = te->next;
	}
B
Bruce Momjian 已提交
1001

B
Bruce Momjian 已提交
1002 1003
	/* Set prev entry as head of list */
	tePrev = AH->toc;
B
Bruce Momjian 已提交
1004

B
Bruce Momjian 已提交
1005 1006 1007
	/* Setup the file */
	fh = fopen(ropt->tocFile, PG_BINARY_R);
	if (!fh)
1008
		die_horribly(AH, modulename, "could not open TOC file\n");
B
Bruce Momjian 已提交
1009

B
Bruce Momjian 已提交
1010
	while (fgets(buf, 1024, fh) != NULL)
B
Bruce Momjian 已提交
1011
	{
B
Bruce Momjian 已提交
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
		/* 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);
		if (endptr == buf)
		{
1029
			write_msg(modulename, "WARNING: line ignored: %s\n", buf);
B
Bruce Momjian 已提交
1030 1031
			continue;
		}
B
Bruce Momjian 已提交
1032

B
Bruce Momjian 已提交
1033 1034 1035
		/* Find TOC entry */
		te = _getTocEntry(AH, id);
		if (!te)
1036
			die_horribly(AH, modulename, "could not find entry for id %d\n", id);
B
Bruce Momjian 已提交
1037

B
Bruce Momjian 已提交
1038
		ropt->idWanted[id - 1] = 1;
B
Bruce Momjian 已提交
1039

B
Bruce Momjian 已提交
1040 1041 1042
		_moveAfter(AH, tePrev, te);
		tePrev = te;
	}
B
Bruce Momjian 已提交
1043

B
Bruce Momjian 已提交
1044
	if (fclose(fh) != 0)
1045
		die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
B
Bruce Momjian 已提交
1046 1047 1048 1049 1050 1051 1052 1053
}

/**********************
 * 'Convenience functions that look like standard IO functions
 * for writing data when in dump mode.
 **********************/

/* Public */
B
Bruce Momjian 已提交
1054 1055 1056 1057
int
archputs(const char *s, Archive *AH)
{
	return WriteData(AH, s, strlen(s));
B
Bruce Momjian 已提交
1058 1059 1060
}

/* Public */
B
Bruce Momjian 已提交
1061 1062 1063 1064
int
archputc(const char c, Archive *AH)
{
	return WriteData(AH, &c, 1);
B
Bruce Momjian 已提交
1065 1066 1067
}

/* Public */
B
Bruce Momjian 已提交
1068 1069
int
archprintf(Archive *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1070
{
B
Bruce Momjian 已提交
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
	char	   *p = NULL;
	va_list		ap;
	int			bSize = strlen(fmt) + 256;
	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))
B
Bruce Momjian 已提交
1086
	{
B
Bruce Momjian 已提交
1087 1088
		if (p != NULL)
			free(p);
1089
		bSize *= 2;
B
Bruce Momjian 已提交
1090
		p = (char *) malloc(bSize);
1091
		if (p == NULL)
1092
			exit_horribly(AH, modulename, "out of memory\n");
1093 1094 1095
		va_start(ap, fmt);
		cnt = vsnprintf(p, bSize, fmt, ap);
		va_end(ap);
B
Bruce Momjian 已提交
1096 1097 1098 1099
	}
	WriteData(AH, p, cnt);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1100 1101 1102 1103 1104 1105 1106
}


/*******************************
 * Stuff below here should be 'private' to the archiver routines
 *******************************/

B
Bruce Momjian 已提交
1107 1108
OutputContext
SetOutput(ArchiveHandle *AH, char *filename, int compression)
B
Bruce Momjian 已提交
1109
{
B
Bruce Momjian 已提交
1110 1111
	OutputContext sav;

1112
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1113
	char		fmode[10];
B
Bruce Momjian 已提交
1114
#endif
B
Bruce Momjian 已提交
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
	int			fn = 0;

	/* Replace the AH output file handle */
	sav.OF = AH->OF;
	sav.gzOut = AH->gzOut;

	if (filename)
		fn = 0;
	else if (AH->FH)
		fn = fileno(AH->FH);
	else if (AH->fSpec)
	{
		fn = 0;
		filename = AH->fSpec;
	}
	else
		fn = fileno(stdout);

	/* If compression explicitly requested, use gzopen */
1134
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1135 1136
	if (compression != 0)
	{
1137
		sprintf(fmode, "wb%d", compression);
B
Bruce Momjian 已提交
1138 1139 1140 1141
		if (fn)
		{
			AH->OF = gzdopen(dup(fn), fmode);	/* Don't use PG_BINARY_x
												 * since this is zlib */
1142
		}
B
Bruce Momjian 已提交
1143 1144
		else
			AH->OF = gzopen(filename, fmode);
1145
		AH->gzOut = 1;
B
Bruce Momjian 已提交
1146 1147 1148
	}
	else
	{							/* Use fopen */
B
Bruce Momjian 已提交
1149
#endif
B
Bruce Momjian 已提交
1150
		if (fn)
1151
			AH->OF = fdopen(dup(fn), PG_BINARY_W);
B
Bruce Momjian 已提交
1152
		else
1153 1154
			AH->OF = fopen(filename, PG_BINARY_W);
		AH->gzOut = 0;
1155
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1156
	}
B
Bruce Momjian 已提交
1157 1158
#endif

1159
	if (!AH->OF)
1160
		die_horribly(AH, modulename, "could not open output file: %s\n", strerror(errno));
1161

B
Bruce Momjian 已提交
1162
	return sav;
B
Bruce Momjian 已提交
1163 1164
}

B
Bruce Momjian 已提交
1165 1166
void
ResetOutput(ArchiveHandle *AH, OutputContext sav)
B
Bruce Momjian 已提交
1167
{
B
Bruce Momjian 已提交
1168
	int			res;
1169

B
Bruce Momjian 已提交
1170
	if (AH->gzOut)
1171
		res = GZCLOSE(AH->OF);
B
Bruce Momjian 已提交
1172
	else
1173 1174 1175
		res = fclose(AH->OF);

	if (res != 0)
1176
		die_horribly(AH, modulename, "could not close output file: %s\n", strerror(errno));
B
Bruce Momjian 已提交
1177

B
Bruce Momjian 已提交
1178 1179
	AH->gzOut = sav.gzOut;
	AH->OF = sav.OF;
B
Bruce Momjian 已提交
1180 1181 1182 1183 1184
}



/*
B
Bruce Momjian 已提交
1185
 *	Print formatted text to the output file (usually stdout).
B
Bruce Momjian 已提交
1186
 */
B
Bruce Momjian 已提交
1187 1188
int
ahprintf(ArchiveHandle *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1189
{
B
Bruce Momjian 已提交
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
	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))
1205
	{
B
Bruce Momjian 已提交
1206 1207
		if (p != NULL)
			free(p);
1208
		bSize *= 2;
B
Bruce Momjian 已提交
1209
		p = (char *) malloc(bSize);
1210
		if (p == NULL)
1211
			die_horribly(AH, modulename, "out of memory\n");
1212
		va_start(ap, fmt);
1213
		cnt = vsnprintf(p, bSize, fmt, ap);
1214
		va_end(ap);
B
Bruce Momjian 已提交
1215 1216 1217 1218
	}
	ahwrite(p, 1, cnt, AH);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1219 1220
}

B
Bruce Momjian 已提交
1221 1222
void
ahlog(ArchiveHandle *AH, int level, const char *fmt,...)
1223 1224 1225 1226 1227 1228 1229
{
	va_list		ap;

	if (AH->debugLevel < level && (!AH->public.verbose || level > 1))
		return;

	va_start(ap, fmt);
1230
	_write_msg(NULL, fmt, ap);
1231 1232 1233
	va_end(ap);
}

1234 1235 1236
/*
 * Single place for logic which says 'We are restoring to a direct DB connection'.
 */
B
Bruce Momjian 已提交
1237 1238
int
RestoringToDB(ArchiveHandle *AH)
1239 1240 1241 1242
{
	return (AH->ropt && AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
1243
/*
B
Bruce Momjian 已提交
1244 1245 1246
 *	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
1247
 *	wants to generate a script (see TAR output).
B
Bruce Momjian 已提交
1248
 */
B
Bruce Momjian 已提交
1249 1250
int
ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
B
Bruce Momjian 已提交
1251
{
B
Bruce Momjian 已提交
1252
	int			res;
1253

B
Bruce Momjian 已提交
1254
	if (AH->writingBlob)
1255
	{
1256 1257 1258 1259 1260
	        if(AH->lo_buf_used + size * nmemb > AH->lo_buf_size) {
		  /* Split LO buffer */
		  int remaining = AH->lo_buf_size - AH->lo_buf_used;
		  int slack = nmemb * size - remaining;

1261
		  memcpy((char *)AH->lo_buf + AH->lo_buf_used, ptr, remaining);
1262 1263 1264 1265
		  res = lo_write(AH->connection, AH->loFd, AH->lo_buf, AH->lo_buf_size);
		  ahlog(AH, 5, "wrote %d bytes of large object data (result = %d)\n",
		  	        AH->lo_buf_size, res);
		  if (res != AH->lo_buf_size)
1266
			die_horribly(AH, modulename, "could not write to large object (result: %d, expected: %d)\n",
1267
						 res, AH->lo_buf_size);
1268
	          memcpy(AH->lo_buf, (char *)ptr + remaining, slack);
1269 1270 1271
		  AH->lo_buf_used = slack;
	       } else {
	         /* LO Buffer is still large enough, buffer it */
1272
		 memcpy((char *)AH->lo_buf + AH->lo_buf_used, ptr, size * nmemb);
1273 1274 1275 1276
		 AH->lo_buf_used += size * nmemb;
	       }

	       return size * nmemb;
1277
	}
B
Bruce Momjian 已提交
1278
	else if (AH->gzOut)
1279
	{
B
Bruce Momjian 已提交
1280
		res = GZWRITE((void *) ptr, size, nmemb, AH->OF);
1281
		if (res != (nmemb * size))
1282
			die_horribly(AH, modulename, "could not write to compressed archive\n");
1283 1284
		return res;
	}
B
Bruce Momjian 已提交
1285
	else if (AH->CustomOutPtr)
1286
	{
B
Bruce Momjian 已提交
1287 1288
		res = AH->CustomOutPtr (AH, ptr, size * nmemb);

1289
		if (res != (nmemb * size))
1290
			die_horribly(AH, modulename, "could not write to custom output routine\n");
1291 1292
		return res;
	}
1293 1294 1295
	else
	{
		/*
B
Bruce Momjian 已提交
1296 1297 1298
		 * If we're doing a restore, and it's direct to DB, and we're
		 * connected then send it to the DB.
		 */
1299
		if (RestoringToDB(AH))
B
Bruce Momjian 已提交
1300
			return ExecuteSqlCommandBuf(AH, (void *) ptr, size * nmemb);		/* Always 1, currently */
1301
		else
1302
		{
B
Bruce Momjian 已提交
1303
			res = fwrite((void *) ptr, size, nmemb, AH->OF);
1304
			if (res != nmemb)
1305 1306
				die_horribly(AH, modulename, "could not write to output file (%d != %d)\n",
							 res, (int) nmemb);
1307 1308
			return res;
		}
1309
	}
B
Bruce Momjian 已提交
1310
}
1311 1312

/* Common exit code */
B
Bruce Momjian 已提交
1313
static void
1314
_write_msg(const char *modulename, const char *fmt, va_list ap)
1315
{
1316
	if (modulename)
1317
		fprintf(stderr, "%s: [%s] ", progname, gettext(modulename));
1318 1319 1320 1321 1322 1323
	else
		fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, gettext(fmt), ap);
}

void
1324
write_msg(const char *modulename, const char *fmt,...)
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
{
	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);
1338 1339
	if (AH->public.verbose)
		write_msg(NULL, "*** aborted because of error\n");
1340

B
Bruce Momjian 已提交
1341 1342 1343
	if (AH)
		if (AH->connection)
			PQfinish(AH->connection);
1344 1345
	if (AH->blobConnection)
		PQfinish(AH->blobConnection);
1346

B
Bruce Momjian 已提交
1347
	exit(1);
B
Bruce Momjian 已提交
1348 1349
}

1350
/* External use */
B
Bruce Momjian 已提交
1351
void
1352
exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
1353
{
B
Bruce Momjian 已提交
1354
	va_list		ap;
1355

B
Bruce Momjian 已提交
1356
	va_start(ap, fmt);
1357
	_die_horribly((ArchiveHandle *) AH, modulename, fmt, ap);
1358
}
B
Bruce Momjian 已提交
1359

1360
/* Archiver use (just different arg declaration) */
B
Bruce Momjian 已提交
1361
void
1362
die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...)
B
Bruce Momjian 已提交
1363
{
B
Bruce Momjian 已提交
1364
	va_list		ap;
B
Bruce Momjian 已提交
1365

B
Bruce Momjian 已提交
1366
	va_start(ap, fmt);
1367
	_die_horribly(AH, modulename, fmt, ap);
B
Bruce Momjian 已提交
1368 1369
}

1370

B
Bruce Momjian 已提交
1371 1372
static void
_moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1373
{
B
Bruce Momjian 已提交
1374 1375
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1376

B
Bruce Momjian 已提交
1377 1378
	te->prev = pos;
	te->next = pos->next;
B
Bruce Momjian 已提交
1379

B
Bruce Momjian 已提交
1380 1381
	pos->next->prev = te;
	pos->next = te;
B
Bruce Momjian 已提交
1382

B
Bruce Momjian 已提交
1383
	te->_moved = 1;
B
Bruce Momjian 已提交
1384 1385
}

B
Bruce Momjian 已提交
1386 1387
static void
_moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1388
{
B
Bruce Momjian 已提交
1389 1390
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1391

B
Bruce Momjian 已提交
1392 1393 1394 1395
	te->prev = pos->prev;
	te->next = pos;
	pos->prev->next = te;
	pos->prev = te;
B
Bruce Momjian 已提交
1396

B
Bruce Momjian 已提交
1397
	te->_moved = 1;
B
Bruce Momjian 已提交
1398 1399
}

B
Bruce Momjian 已提交
1400 1401
static TocEntry *
_getTocEntry(ArchiveHandle *AH, int id)
B
Bruce Momjian 已提交
1402
{
B
Bruce Momjian 已提交
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
	TocEntry   *te;

	te = AH->toc->next;
	while (te != AH->toc)
	{
		if (te->id == id)
			return te;
		te = te->next;
	}
	return NULL;
B
Bruce Momjian 已提交
1413 1414
}

B
Bruce Momjian 已提交
1415 1416
int
TocIDRequired(ArchiveHandle *AH, int id, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1417
{
B
Bruce Momjian 已提交
1418
	TocEntry   *te = _getTocEntry(AH, id);
B
Bruce Momjian 已提交
1419

B
Bruce Momjian 已提交
1420 1421
	if (!te)
		return 0;
B
Bruce Momjian 已提交
1422

B
Bruce Momjian 已提交
1423
	return _tocEntryRequired(te, ropt);
B
Bruce Momjian 已提交
1424 1425
}

B
Bruce Momjian 已提交
1426 1427
int
WriteInt(ArchiveHandle *AH, int i)
B
Bruce Momjian 已提交
1428
{
B
Bruce Momjian 已提交
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
	int			b;

	/*
	 * This is a bit yucky, but I don't want to make the binary format
	 * very dependant on representation, and not knowing much about it, I
	 * 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);
1443
		i = -i;
B
Bruce Momjian 已提交
1444 1445 1446 1447 1448 1449 1450
	}
	else
		(*AH->WriteBytePtr) (AH, 0);

	for (b = 0; b < AH->intSize; b++)
	{
		(*AH->WriteBytePtr) (AH, i & 0xFF);
1451
		i >>= 8;
B
Bruce Momjian 已提交
1452 1453 1454
	}

	return AH->intSize + 1;
B
Bruce Momjian 已提交
1455 1456
}

B
Bruce Momjian 已提交
1457 1458
int
ReadInt(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1459
{
B
Bruce Momjian 已提交
1460 1461 1462 1463 1464
	int			res = 0;
	int			bv,
				b;
	int			sign = 0;		/* Default positive */
	int			bitShift = 0;
B
Bruce Momjian 已提交
1465

B
Bruce Momjian 已提交
1466
	if (AH->version > K_VERS_1_0)
1467
		/* Read a sign byte */
B
Bruce Momjian 已提交
1468
		sign = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
1469

B
Bruce Momjian 已提交
1470 1471 1472
	for (b = 0; b < AH->intSize; b++)
	{
		bv = (*AH->ReadBytePtr) (AH) & 0xFF;
1473 1474 1475
		if (bv != 0)
			res = res + (bv << bitShift);
		bitShift += 8;
B
Bruce Momjian 已提交
1476
	}
B
Bruce Momjian 已提交
1477

B
Bruce Momjian 已提交
1478 1479
	if (sign)
		res = -res;
B
Bruce Momjian 已提交
1480

B
Bruce Momjian 已提交
1481
	return res;
B
Bruce Momjian 已提交
1482 1483
}

B
Bruce Momjian 已提交
1484
int
1485
WriteStr(ArchiveHandle *AH, const char *c)
B
Bruce Momjian 已提交
1486
{
B
Bruce Momjian 已提交
1487
	int			res;
1488 1489 1490 1491

	if (c)
	{
		res = WriteInt(AH, strlen(c));
B
Bruce Momjian 已提交
1492
		res += (*AH->WriteBufPtr) (AH, c, strlen(c));
1493 1494 1495 1496
	}
	else
		res = WriteInt(AH, -1);

B
Bruce Momjian 已提交
1497
	return res;
B
Bruce Momjian 已提交
1498 1499
}

B
Bruce Momjian 已提交
1500 1501
char *
ReadStr(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1502
{
B
Bruce Momjian 已提交
1503 1504
	char	   *buf;
	int			l;
B
Bruce Momjian 已提交
1505

B
Bruce Momjian 已提交
1506
	l = ReadInt(AH);
1507 1508 1509 1510
	if (l == -1)
		buf = NULL;
	else
	{
B
Bruce Momjian 已提交
1511
		buf = (char *) malloc(l + 1);
1512
		if (!buf)
1513
			die_horribly(AH, modulename, "out of memory\n");
1514

B
Bruce Momjian 已提交
1515
		(*AH->ReadBufPtr) (AH, (void *) buf, l);
1516 1517
		buf[l] = '\0';
	}
B
Bruce Momjian 已提交
1518

B
Bruce Momjian 已提交
1519
	return buf;
B
Bruce Momjian 已提交
1520 1521
}

T
Tom Lane 已提交
1522
static int
B
Bruce Momjian 已提交
1523
_discoverArchiveFormat(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1524
{
B
Bruce Momjian 已提交
1525 1526 1527 1528
	FILE	   *fh;
	char		sig[6];			/* More than enough */
	int			cnt;
	int			wantClose = 0;
B
Bruce Momjian 已提交
1529

1530
#if 0
1531
	write_msg(modulename, "attempting to ascertain archive format\n");
1532
#endif
1533 1534 1535 1536 1537 1538 1539 1540

	if (AH->lookahead)
		free(AH->lookahead);

	AH->lookaheadSize = 512;
	AH->lookahead = calloc(1, 512);
	AH->lookaheadLen = 0;
	AH->lookaheadPos = 0;
1541

B
Bruce Momjian 已提交
1542 1543
	if (AH->fSpec)
	{
1544 1545
		wantClose = 1;
		fh = fopen(AH->fSpec, PG_BINARY_R);
B
Bruce Momjian 已提交
1546 1547
	}
	else
1548
		fh = stdin;
B
Bruce Momjian 已提交
1549

B
Bruce Momjian 已提交
1550
	if (!fh)
1551
		die_horribly(AH, modulename, "could not open input file: %s\n", strerror(errno));
B
Bruce Momjian 已提交
1552

B
Bruce Momjian 已提交
1553
	cnt = fread(sig, 1, 5, fh);
B
Bruce Momjian 已提交
1554

B
Bruce Momjian 已提交
1555
	if (cnt != 5)
1556 1557 1558 1559 1560 1561
	{
		if (ferror(fh))
			die_horribly(AH, modulename, "could not read input file: %s\n", strerror(errno));
		else
			die_horribly(AH, modulename, "input file is too short (read %d, expected 5)\n", cnt);
	}
B
Bruce Momjian 已提交
1562

B
Bruce Momjian 已提交
1563
	/* Save it, just in case we need it later */
1564 1565
	strncpy(&AH->lookahead[0], sig, 5);
	AH->lookaheadLen = 5;
B
Bruce Momjian 已提交
1566

B
Bruce Momjian 已提交
1567
	if (strncmp(sig, "PGDMP", 5) == 0)
1568 1569 1570 1571 1572 1573 1574 1575 1576
	{
		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 */
B
Bruce Momjian 已提交
1577 1578
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
		{
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
			AH->vrev = fgetc(fh);
			AH->lookahead[AH->lookaheadLen++] = AH->vrev;
		}
		else
			AH->vrev = 0;

		AH->intSize = fgetc(fh);
		AH->lookahead[AH->lookaheadLen++] = AH->intSize;

		AH->format = fgetc(fh);
		AH->lookahead[AH->lookaheadLen++] = AH->format;

		/* Make a convenient integer <maj><min><rev>00 */
B
Bruce Momjian 已提交
1592 1593 1594 1595
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
	}
	else
	{
1596
		/*
B
Bruce Momjian 已提交
1597 1598
		 * *Maybe* we have a tar archive format file... So, read first 512
		 * byte header...
1599 1600 1601
		 */
		cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
		AH->lookaheadLen += cnt;
B
Bruce Momjian 已提交
1602

1603
		if (AH->lookaheadLen != 512)
1604
			die_horribly(AH, modulename, "input file does not appear to be a valid archive (too short?)\n");
B
Bruce Momjian 已提交
1605

1606
		if (!isValidTarHeader(AH->lookahead))
1607
			die_horribly(AH, modulename, "input file does not appear to be a valid archive\n");
B
Bruce Momjian 已提交
1608

1609 1610
		AH->format = archTar;
	}
B
Bruce Momjian 已提交
1611

B
Bruce Momjian 已提交
1612 1613
	/* If we can't seek, then mark the header as read */
	if (fseek(fh, 0, SEEK_SET) != 0)
1614 1615
	{
		/*
B
Bruce Momjian 已提交
1616 1617
		 * NOTE: Formats that use the looahead buffer can unset this in
		 * their Init routine.
1618 1619 1620 1621
		 */
		AH->readHeader = 1;
	}
	else
B
Bruce Momjian 已提交
1622
		AH->lookaheadLen = 0;	/* Don't bother since we've reset the file */
1623

1624 1625 1626
#if 0
	write_msg(modulename, "read %d bytes into lookahead buffer\n", AH->lookaheadLen);
#endif
B
Bruce Momjian 已提交
1627

B
Bruce Momjian 已提交
1628 1629
	/* Close the file */
	if (wantClose)
1630
		if (fclose(fh) != 0)
1631 1632
			die_horribly(AH, modulename, "could not close the input file after reading header: %s\n",
						 strerror(errno));
B
Bruce Momjian 已提交
1633

B
Bruce Momjian 已提交
1634
	return AH->format;
B
Bruce Momjian 已提交
1635 1636 1637 1638 1639 1640
}


/*
 * Allocate an archive handle
 */
B
Bruce Momjian 已提交
1641 1642 1643
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
		 const int compression, ArchiveMode mode)
1644
{
B
Bruce Momjian 已提交
1645
	ArchiveHandle *AH;
B
Bruce Momjian 已提交
1646

1647
#if 0
1648
	write_msg(modulename, "allocating AH for %s, format %d\n", FileSpec, fmt);
1649
#endif
1650

B
Bruce Momjian 已提交
1651 1652
	AH = (ArchiveHandle *) calloc(1, sizeof(ArchiveHandle));
	if (!AH)
1653
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1654

B
Bruce Momjian 已提交
1655 1656
	AH->vmaj = K_VERS_MAJOR;
	AH->vmin = K_VERS_MINOR;
1657
	AH->vrev = K_VERS_REV;
B
Bruce Momjian 已提交
1658

1659 1660
	AH->createDate = time(NULL);

B
Bruce Momjian 已提交
1661 1662 1663 1664
	AH->intSize = sizeof(int);
	AH->lastID = 0;
	if (FileSpec)
	{
1665
		AH->fSpec = strdup(FileSpec);
B
Bruce Momjian 已提交
1666

1667 1668 1669
		/*
		 * Not used; maybe later....
		 *
B
Bruce Momjian 已提交
1670 1671
		 * AH->workDir = strdup(FileSpec); for(i=strlen(FileSpec) ; i > 0 ;
		 * i--) if (AH->workDir[i-1] == '/')
1672
		 */
B
Bruce Momjian 已提交
1673 1674
	}
	else
1675
		AH->fSpec = NULL;
B
Bruce Momjian 已提交
1676

B
Bruce Momjian 已提交
1677 1678
	AH->currUser = strdup("");	/* So it's valid, but we can free() it
								 * later if necessary */
1679
	AH->currSchema = strdup(""); /* ditto */
B
Bruce Momjian 已提交
1680

B
Bruce Momjian 已提交
1681 1682
	AH->toc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!AH->toc)
1683
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1684

B
Bruce Momjian 已提交
1685 1686 1687 1688 1689
	AH->toc->next = AH->toc;
	AH->toc->prev = AH->toc;

	AH->mode = mode;
	AH->compression = compression;
B
Bruce Momjian 已提交
1690

1691 1692
	AH->pgCopyBuf = createPQExpBuffer();
	AH->sqlBuf = createPQExpBuffer();
B
Bruce Momjian 已提交
1693

B
Bruce Momjian 已提交
1694 1695 1696
	/* Open stdout with no compression for AH output handle */
	AH->gzOut = 0;
	AH->OF = stdout;
B
Bruce Momjian 已提交
1697

1698
#if 0
1699
	write_msg(modulename, "archive format is %d\n", fmt);
1700
#endif
1701

B
Bruce Momjian 已提交
1702
	if (fmt == archUnknown)
1703 1704 1705
		AH->format = _discoverArchiveFormat(AH);
	else
		AH->format = fmt;
B
Bruce Momjian 已提交
1706

B
Bruce Momjian 已提交
1707 1708
	switch (AH->format)
	{
B
Bruce Momjian 已提交
1709

1710 1711 1712
		case archCustom:
			InitArchiveFmt_Custom(AH);
			break;
B
Bruce Momjian 已提交
1713

1714 1715 1716
		case archFiles:
			InitArchiveFmt_Files(AH);
			break;
B
Bruce Momjian 已提交
1717

1718 1719 1720
		case archNull:
			InitArchiveFmt_Null(AH);
			break;
B
Bruce Momjian 已提交
1721

1722 1723 1724 1725 1726
		case archTar:
			InitArchiveFmt_Tar(AH);
			break;

		default:
1727
			die_horribly(AH, modulename, "unrecognized file format '%d'\n", fmt);
B
Bruce Momjian 已提交
1728
	}
B
Bruce Momjian 已提交
1729

B
Bruce Momjian 已提交
1730
	return AH;
B
Bruce Momjian 已提交
1731 1732 1733
}


B
Bruce Momjian 已提交
1734 1735
void
WriteDataChunks(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1736
{
B
Bruce Momjian 已提交
1737 1738 1739
	TocEntry   *te = AH->toc->next;
	StartDataPtr startPtr;
	EndDataPtr	endPtr;
B
Bruce Momjian 已提交
1740

B
Bruce Momjian 已提交
1741 1742 1743
	while (te != AH->toc)
	{
		if (te->dataDumper != NULL)
1744
		{
B
Bruce Momjian 已提交
1745 1746
			AH->currToc = te;
			/* printf("Writing data for %d (%x)\n", te->id, te); */
1747

B
Bruce Momjian 已提交
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757
			if (strcmp(te->desc, "BLOBS") == 0)
			{
				startPtr = AH->StartBlobsPtr;
				endPtr = AH->EndBlobsPtr;
			}
			else
			{
				startPtr = AH->StartDataPtr;
				endPtr = AH->EndDataPtr;
			}
B
Bruce Momjian 已提交
1758

B
Bruce Momjian 已提交
1759 1760
			if (startPtr != NULL)
				(*startPtr) (AH, te);
B
Bruce Momjian 已提交
1761

B
Bruce Momjian 已提交
1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
			/*
			 * printf("Dumper arg for %d is %x\n", te->id,
			 * te->dataDumperArg);
			 */

			/*
			 * The user-provided DataDumper routine needs to call
			 * AH->WriteData
			 */
			(*te->dataDumper) ((Archive *) AH, te->oid, te->dataDumperArg);

			if (endPtr != NULL)
				(*endPtr) (AH, te);
			AH->currToc = NULL;
		}
1777
		te = te->next;
B
Bruce Momjian 已提交
1778
	}
B
Bruce Momjian 已提交
1779 1780
}

B
Bruce Momjian 已提交
1781 1782
void
WriteToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1783
{
B
Bruce Momjian 已提交
1784
	TocEntry   *te = AH->toc->next;
1785 1786
	const char *dep;
	int			i;
B
Bruce Momjian 已提交
1787 1788 1789 1790 1791 1792 1793 1794 1795

	/* printf("%d TOC Entries to save\n", AH->tocCount); */

	WriteInt(AH, AH->tocCount);
	while (te != AH->toc)
	{
		WriteInt(AH, te->id);
		WriteInt(AH, te->dataDumper ? 1 : 0);
		WriteStr(AH, te->oid);
1796

B
Bruce Momjian 已提交
1797 1798 1799 1800 1801
		WriteStr(AH, te->name);
		WriteStr(AH, te->desc);
		WriteStr(AH, te->defn);
		WriteStr(AH, te->dropStmt);
		WriteStr(AH, te->copyStmt);
1802
		WriteStr(AH, te->namespace);
B
Bruce Momjian 已提交
1803
		WriteStr(AH, te->owner);
1804 1805 1806 1807 1808

		/* Dump list of dependencies */
		if (te->depOid != NULL)
		{
			i = 0;
1809
			while ((dep = (*te->depOid)[i++]) != NULL)
1810 1811
				WriteStr(AH, dep);
		}
1812
		WriteStr(AH, NULL);		/* Terminate List */
1813

B
Bruce Momjian 已提交
1814 1815
		if (AH->WriteExtraTocPtr)
			(*AH->WriteExtraTocPtr) (AH, te);
1816
		te = te->next;
B
Bruce Momjian 已提交
1817
	}
B
Bruce Momjian 已提交
1818 1819
}

B
Bruce Momjian 已提交
1820 1821
void
ReadToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1822
{
B
Bruce Momjian 已提交
1823
	int			i;
1824
	char	   *((*deps)[]);
1825 1826
	int			depIdx;
	int			depSize;
B
Bruce Momjian 已提交
1827

B
Bruce Momjian 已提交
1828
	TocEntry   *te = AH->toc->next;
B
Bruce Momjian 已提交
1829

B
Bruce Momjian 已提交
1830
	AH->tocCount = ReadInt(AH);
B
Bruce Momjian 已提交
1831

B
Bruce Momjian 已提交
1832 1833
	for (i = 0; i < AH->tocCount; i++)
	{
B
Bruce Momjian 已提交
1834

B
Bruce Momjian 已提交
1835
		te = (TocEntry *) calloc(1, sizeof(TocEntry));
1836 1837 1838 1839
		te->id = ReadInt(AH);

		/* Sanity check */
		if (te->id <= 0 || te->id > AH->tocCount)
1840
			die_horribly(AH, modulename, "entry id out of range - perhaps a corrupt TOC\n");
1841 1842 1843

		te->hadDumper = ReadInt(AH);
		te->oid = ReadStr(AH);
1844 1845
		te->oidVal = atooid(te->oid);

1846 1847 1848 1849 1850 1851 1852 1853
		te->name = ReadStr(AH);
		te->desc = ReadStr(AH);
		te->defn = ReadStr(AH);
		te->dropStmt = ReadStr(AH);

		if (AH->version >= K_VERS_1_3)
			te->copyStmt = ReadStr(AH);

1854 1855 1856
		if (AH->version >= K_VERS_1_6)
			te->namespace = ReadStr(AH);

1857 1858
		te->owner = ReadStr(AH);

1859 1860 1861 1862
		/* Read TOC entry dependencies */
		if (AH->version >= K_VERS_1_5)
		{
			depSize = 100;
1863
			deps = malloc(sizeof(char *) * depSize);
1864 1865 1866 1867 1868 1869
			depIdx = 0;
			do
			{
				if (depIdx > depSize)
				{
					depSize *= 2;
1870
					deps = realloc(deps, sizeof(char *) * depSize);
1871 1872
				}
				(*deps)[depIdx] = ReadStr(AH);
1873 1874 1875 1876 1877
#if 0
				if ((*deps)[depIdx])
					write_msg(modulename, "read dependency for %s -> %s\n",
							  te->name, (*deps)[depIdx]);
#endif
1878
			} while ((*deps)[depIdx++] != NULL);
1879

1880 1881
			if (depIdx > 1)		/* We have a non-null entry */
				te->depOid = realloc(deps, sizeof(char *) * depIdx);	/* trim it */
1882
			else
1883
				te->depOid = NULL;		/* no deps */
1884
		}
1885 1886
		else
			te->depOid = NULL;
1887 1888 1889 1890

		/* Set maxOidVal etc for use in sorting */
		_fixupOidInfo(te);

B
Bruce Momjian 已提交
1891 1892
		if (AH->ReadExtraTocPtr)
			(*AH->ReadExtraTocPtr) (AH, te);
1893

1894
		ahlog(AH, 3, "read TOC entry %d (id %d) for %s %s\n", i, te->id, te->desc, te->name);
1895 1896 1897 1898 1899

		te->prev = AH->toc->prev;
		AH->toc->prev->next = te;
		AH->toc->prev = te;
		te->next = AH->toc;
B
Bruce Momjian 已提交
1900
	}
B
Bruce Momjian 已提交
1901 1902
}

1903
static teReqs
B
Bruce Momjian 已提交
1904
_tocEntryRequired(TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1905
{
1906
	teReqs		res = 3;		/* Schema = 1, Data = 2, Both = 3 */
B
Bruce Momjian 已提交
1907 1908 1909

	/* If it's an ACL, maybe ignore it */
	if (ropt->aclsSkip && strcmp(te->desc, "ACL") == 0)
1910
		return 0;
B
Bruce Momjian 已提交
1911

B
Bruce Momjian 已提交
1912
	if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
1913 1914
		return 0;

B
Bruce Momjian 已提交
1915 1916 1917 1918
	/* Check if tablename only is wanted */
	if (ropt->selTypes)
	{
		if ((strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0))
1919 1920 1921 1922 1923
		{
			if (!ropt->selTable)
				return 0;
			if (ropt->tableNames && strcmp(ropt->tableNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1924 1925 1926
		}
		else if (strcmp(te->desc, "INDEX") == 0)
		{
1927 1928 1929 1930
			if (!ropt->selIndex)
				return 0;
			if (ropt->indexNames && strcmp(ropt->indexNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1931 1932 1933
		}
		else if (strcmp(te->desc, "FUNCTION") == 0)
		{
1934 1935 1936 1937
			if (!ropt->selFunction)
				return 0;
			if (ropt->functionNames && strcmp(ropt->functionNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1938 1939 1940
		}
		else if (strcmp(te->desc, "TRIGGER") == 0)
		{
1941 1942 1943 1944 1945
			if (!ropt->selTrigger)
				return 0;
			if (ropt->triggerNames && strcmp(ropt->triggerNames, te->name) != 0)
				return 0;
		}
B
Bruce Momjian 已提交
1946 1947
		else
			return 0;
B
Bruce Momjian 已提交
1948 1949
	}

1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
	/*
	 * 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
1963 1964
			res = res & ~REQ_DATA;
	}
1965

1966 1967 1968 1969 1970
	/*
	 * Special case: <Init> type with <Max OID> name; this is part of a
	 * DATA restore even though it has SQL.
	 */
	if ((strcmp(te->desc, "<Init>") == 0) && (strcmp(te->name, "Max OID") == 0))
1971 1972
		res = REQ_DATA;

B
Bruce Momjian 已提交
1973 1974
	/* Mask it if we only want schema */
	if (ropt->schemaOnly)
1975
		res = res & REQ_SCHEMA;
B
Bruce Momjian 已提交
1976

B
Bruce Momjian 已提交
1977
	/* Mask it we only want data */
1978
	if (ropt->dataOnly)
1979
		res = res & REQ_DATA;
B
Bruce Momjian 已提交
1980

B
Bruce Momjian 已提交
1981 1982
	/* Mask it if we don't have a schema contribition */
	if (!te->defn || strlen(te->defn) == 0)
1983
		res = res & ~REQ_SCHEMA;
B
Bruce Momjian 已提交
1984

B
Bruce Momjian 已提交
1985 1986
	/* Finally, if we used a list, limit based on that as well */
	if (ropt->limitToList && !ropt->idWanted[te->id - 1])
1987
		return 0;
B
Bruce Momjian 已提交
1988

B
Bruce Momjian 已提交
1989
	return res;
B
Bruce Momjian 已提交
1990 1991
}

1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
/*
 * Issue a SET SESSION AUTHORIZATION command.  Caller is responsible
 * for updating state if appropriate.  Note that caller must also quote
 * the argument if it's a username (it might be DEFAULT, too).
 */
static void
_doSetSessionAuth(ArchiveHandle *AH, const char *autharg)
{
	if (RestoringToDB(AH))
	{
		PQExpBuffer qry = createPQExpBuffer();
		PGresult   *res;

		appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION %s;", autharg);
		res = PQexec(AH->connection, qry->data);

		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
			die_horribly(AH, modulename, "could not set session user to %s: %s",
						 autharg, PQerrorMessage(AH->connection));

		PQclear(res);
		destroyPQExpBuffer(qry);
	}
	else
		ahprintf(AH, "SET SESSION AUTHORIZATION %s;\n\n", autharg);
}

2019 2020 2021 2022 2023 2024 2025 2026

/*
 * Issue the commands to connect to the database as the specified user
 * to the specified database.  The database name may be NULL, then the
 * current database is kept.  If reconnects were disallowed by the
 * user, this won't do anything.
 *
 * If we're currently restoring right into a database, this will
2027
 * actually establish a connection.	Otherwise it puts a \connect into
2028 2029
 * the script output.
 */
B
Bruce Momjian 已提交
2030
static void
2031
_reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user)
2032
{
2033 2034 2035
	if (!user || strlen(user) == 0
		|| (strcmp(AH->currUser, user) == 0 && !dbname))
		return;					/* no need to do anything */
2036

2037 2038 2039 2040
	/*
	 * Use SET SESSION AUTHORIZATION if allowed and no database change
	 * needed
	 */
2041
	if (!dbname && AH->ropt->use_setsessauth)
2042
	{
2043
		_doSetSessionAuth(AH, fmtId(user, false));
B
Bruce Momjian 已提交
2044
	}
2045
	else if (AH->ropt && AH->ropt->noReconnect)
2046 2047
	{
		/* When -R was given, don't do anything. */
2048
		return;
2049
	}
2050 2051 2052
	else if (RestoringToDB(AH))
		ReconnectToServer(AH, dbname, user);
	else
2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
	{
		PQExpBuffer qry = createPQExpBuffer();

		appendPQExpBuffer(qry, "\\connect %s",
						  dbname ? fmtId(dbname, false) : "-");
		appendPQExpBuffer(qry, " %s\n\n",
						  fmtId(user, false));

		ahprintf(AH, qry->data);

		destroyPQExpBuffer(qry);
2064 2065 2066 2067 2068

		/* don't assume we still know the output schema */
		if (AH->currSchema)
			free(AH->currSchema);
		AH->currSchema = strdup("");
2069
	}
2070

2071 2072 2073 2074
	/*
	 * NOTE: currUser keeps track of what the imaginary session user in
	 * our script is
	 */
2075 2076 2077 2078
	if (AH->currUser)
		free(AH->currUser);

	AH->currUser = strdup(user);
2079 2080
}

2081 2082 2083 2084 2085 2086

/*
 * Issues the commands to connect to the database (or the current one,
 * if NULL) as the owner of the the given TOC entry object.  If
 * changes in ownership are not allowed, this doesn't do anything.
 */
B
Bruce Momjian 已提交
2087 2088
static void
_reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te)
2089 2090 2091 2092 2093
{
	if (AH->ropt && AH->ropt->noOwner)
		return;

	_reconnectAsUser(AH, dbname, te->owner);
2094 2095
}

2096

2097 2098 2099 2100 2101 2102 2103
/*
 * Issue the commands to select the specified schema as the current schema
 * in the target database.
 */
static void
_selectOutputSchema(ArchiveHandle *AH, const char *schemaName)
{
2104 2105
	PQExpBuffer qry;

2106 2107 2108 2109
	if (!schemaName || *schemaName == '\0' ||
		strcmp(AH->currSchema, schemaName) == 0)
		return;					/* no need to do anything */

2110 2111 2112 2113 2114 2115 2116
	qry = createPQExpBuffer();

	appendPQExpBuffer(qry, "SET search_path = %s",
					  fmtId(schemaName, false));
	if (strcmp(schemaName, "pg_catalog") != 0)
		appendPQExpBuffer(qry, ", pg_catalog");

2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129
	if (RestoringToDB(AH))
	{
		PGresult   *res;

		res = PQexec(AH->connection, qry->data);

		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
			die_horribly(AH, modulename, "could not set search_path to %s: %s",
						 schemaName, PQerrorMessage(AH->connection));

		PQclear(res);
	}
	else
2130
		ahprintf(AH, "%s;\n\n", qry->data);
2131 2132 2133 2134

	if (AH->currSchema)
		free(AH->currSchema);
	AH->currSchema = strdup(schemaName);
2135 2136

	destroyPQExpBuffer(qry);
2137 2138 2139
}


2140 2141 2142
/*
 * fmtId
 *
2143 2144
 *	Quotes input string if it's not a legitimate SQL identifier as-is,
 *	or all the time if force_quotes is true.
2145
 *
2146 2147 2148
 *	Note that the returned string must be used before calling fmtId again,
 *	since we re-use the same return buffer each time.  Non-reentrant but
 *	avoids memory leakage.
2149 2150 2151 2152 2153 2154 2155
 */
const char *
fmtId(const char *rawid, bool force_quotes)
{
	static PQExpBuffer id_return = NULL;
	const char *cp;

2156 2157 2158 2159 2160
	if (id_return)				/* first time through? */
		resetPQExpBuffer(id_return);
	else
		id_return = createPQExpBuffer();

2161 2162 2163
	if (!force_quotes)
	{
		/* do a quick check on the first character... */
2164
		if (!islower((unsigned char) *rawid) && *rawid != '_')
2165 2166
			force_quotes = true;
		else
2167 2168
		{
			/* otherwise check the entire string */
2169 2170 2171 2172 2173 2174 2175 2176 2177 2178
			for (cp = rawid; *cp; cp++)
			{
				if (!(islower((unsigned char) *cp) ||
					  isdigit((unsigned char) *cp) ||
					  (*cp == '_')))
				{
					force_quotes = true;
					break;
				}
			}
2179
		}
2180 2181 2182
	}

	if (!force_quotes)
2183 2184 2185 2186
	{
		/* no quoting needed */
		appendPQExpBufferStr(id_return, rawid);
	}
2187 2188
	else
	{
2189 2190
		appendPQExpBufferChar(id_return, '\"');
		for (cp = rawid; *cp; cp++)
2191
		{
2192 2193 2194 2195 2196 2197 2198 2199
			/*
			 * Did we find a double-quote in the string? Then make this a
			 * double double-quote per SQL99. Before, we put in a
			 * backslash/double-quote pair. - thomas 2000-08-05
			 */
			if (*cp == '\"')
				appendPQExpBufferChar(id_return, '\"');
			appendPQExpBufferChar(id_return, *cp);
2200
		}
2201
		appendPQExpBufferChar(id_return, '\"');
2202 2203 2204 2205 2206 2207
	}

	return id_return->data;
}


B
Bruce Momjian 已提交
2208
static int
2209
_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData)
B
Bruce Momjian 已提交
2210
{
2211
	char	   *pfx;
2212

2213
	/* Select owner and schema as necessary */
2214
	_reconnectAsOwner(AH, NULL, te);
2215
	_selectOutputSchema(AH, te->namespace);
2216

2217 2218
	if (isData)
		pfx = "Data for ";
2219
	else
2220 2221
		pfx = "";

2222 2223 2224 2225
	ahprintf(AH, "--\n-- %sTOC Entry ID %d (OID %s)\n--\n-- Name: %s Type: %s Schema: %s Owner: %s\n",
			 pfx, te->id, te->oid, te->name, te->desc,
			 te->namespace ? te->namespace : "-",
			 te->owner);
B
Bruce Momjian 已提交
2226 2227 2228
	if (AH->PrintExtraTocPtr !=NULL)
		(*AH->PrintExtraTocPtr) (AH, te);
	ahprintf(AH, "--\n\n");
B
Bruce Momjian 已提交
2229

2230
	ahprintf(AH, "%s\n", te->defn);
B
Bruce Momjian 已提交
2231

B
Bruce Momjian 已提交
2232
	return 1;
B
Bruce Momjian 已提交
2233 2234
}

B
Bruce Momjian 已提交
2235 2236
void
WriteHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
2237
{
B
Bruce Momjian 已提交
2238
	struct tm	crtm;
2239

B
Bruce Momjian 已提交
2240 2241 2242 2243 2244 2245
	(*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);
	(*AH->WriteBytePtr) (AH, AH->format);
B
Bruce Momjian 已提交
2246

2247
#ifndef HAVE_LIBZ
B
Bruce Momjian 已提交
2248
	if (AH->compression != 0)
2249 2250
		write_msg(modulename, "WARNING: requested compression not available in this "
				  "installation - archive will be uncompressed\n");
B
Bruce Momjian 已提交
2251

B
Bruce Momjian 已提交
2252
	AH->compression = 0;
2253
#endif
B
Bruce Momjian 已提交
2254

2255 2256 2257 2258 2259 2260 2261 2262 2263 2264
	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);
B
Bruce Momjian 已提交
2265
	WriteStr(AH, AH->dbname);
B
Bruce Momjian 已提交
2266 2267
}

B
Bruce Momjian 已提交
2268 2269
void
ReadHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
2270
{
B
Bruce Momjian 已提交
2271 2272
	char		tmpMag[7];
	int			fmt;
2273
	struct tm	crtm;
B
Bruce Momjian 已提交
2274

2275
	/* If we haven't already read the header... */
B
Bruce Momjian 已提交
2276 2277
	if (!AH->readHeader)
	{
B
Bruce Momjian 已提交
2278

B
Bruce Momjian 已提交
2279
		(*AH->ReadBufPtr) (AH, tmpMag, 5);
B
Bruce Momjian 已提交
2280

B
Bruce Momjian 已提交
2281
		if (strncmp(tmpMag, "PGDMP", 5) != 0)
2282
			die_horribly(AH, modulename, "did not find magic string in file header\n");
B
Bruce Momjian 已提交
2283

B
Bruce Momjian 已提交
2284 2285
		AH->vmaj = (*AH->ReadBytePtr) (AH);
		AH->vmin = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
2286

B
Bruce Momjian 已提交
2287 2288 2289
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
			AH->vrev = (*AH->ReadBytePtr) (AH);
		else
2290
			AH->vrev = 0;
B
Bruce Momjian 已提交
2291

B
Bruce Momjian 已提交
2292
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
B
Bruce Momjian 已提交
2293 2294


2295
		if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
2296 2297
			die_horribly(AH, modulename, "unsupported version (%d.%d) in file header\n",
						 AH->vmaj, AH->vmin);
B
Bruce Momjian 已提交
2298

B
Bruce Momjian 已提交
2299
		AH->intSize = (*AH->ReadBytePtr) (AH);
2300
		if (AH->intSize > 32)
2301
			die_horribly(AH, modulename, "sanity check on integer size (%d) failed\n", AH->intSize);
B
Bruce Momjian 已提交
2302

2303
		if (AH->intSize > sizeof(int))
2304
			write_msg(modulename, "WARNING: archive was made on a machine with larger integers, some operations may fail\n");
B
Bruce Momjian 已提交
2305

B
Bruce Momjian 已提交
2306
		fmt = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
2307

2308
		if (AH->format != fmt)
2309 2310
			die_horribly(AH, modulename, "expected format (%d) differs from format found in file (%d)\n",
						 AH->format, fmt);
B
Bruce Momjian 已提交
2311
	}
B
Bruce Momjian 已提交
2312

B
Bruce Momjian 已提交
2313 2314
	if (AH->version >= K_VERS_1_2)
	{
2315
		if (AH->version < K_VERS_1_4)
B
Bruce Momjian 已提交
2316
			AH->compression = (*AH->ReadBytePtr) (AH);
2317 2318
		else
			AH->compression = ReadInt(AH);
B
Bruce Momjian 已提交
2319 2320
	}
	else
2321
		AH->compression = Z_DEFAULT_COMPRESSION;
B
Bruce Momjian 已提交
2322

2323
#ifndef HAVE_LIBZ
B
Bruce Momjian 已提交
2324
	if (AH->compression != 0)
2325
		write_msg(modulename, "WARNING: archive is compressed, but this installation does not support compression - no data will be available\n");
B
Bruce Momjian 已提交
2326 2327
#endif

2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341
	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);

B
Bruce Momjian 已提交
2342
		if (AH->createDate == (time_t) -1)
2343
			write_msg(modulename, "WARNING: invalid creation date in header\n");
2344 2345
	}

B
Bruce Momjian 已提交
2346 2347 2348
}


B
Bruce Momjian 已提交
2349 2350
static void
_SortToc(ArchiveHandle *AH, TocSortCompareFn fn)
B
Bruce Momjian 已提交
2351
{
B
Bruce Momjian 已提交
2352 2353 2354
	TocEntry  **tea;
	TocEntry   *te;
	int			i;
B
Bruce Momjian 已提交
2355

B
Bruce Momjian 已提交
2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369
	/* Allocate an array for quicksort (TOC size + head & foot) */
	tea = (TocEntry **) malloc(sizeof(TocEntry *) * (AH->tocCount + 2));

	/* Build array of toc entries, including header at start and end */
	te = AH->toc;
	for (i = 0; i <= AH->tocCount + 1; i++)
	{
		/*
		 * printf("%d: %x (%x, %x) - %d\n", i, te, te->prev, te->next,
		 * te->oidVal);
		 */
		tea[i] = te;
		te = te->next;
	}
B
Bruce Momjian 已提交
2370

B
Bruce Momjian 已提交
2371 2372
	/* Sort it, but ignore the header entries */
	qsort(&(tea[1]), AH->tocCount, sizeof(TocEntry *), fn);
B
Bruce Momjian 已提交
2373

B
Bruce Momjian 已提交
2374 2375 2376 2377 2378 2379
	/* Rebuild list: this works becuase we have headers at each end */
	for (i = 1; i <= AH->tocCount; i++)
	{
		tea[i]->next = tea[i + 1];
		tea[i]->prev = tea[i - 1];
	}
B
Bruce Momjian 已提交
2380 2381


B
Bruce Momjian 已提交
2382 2383 2384 2385 2386 2387 2388 2389 2390
	te = AH->toc;
	for (i = 0; i <= AH->tocCount + 1; i++)
	{
		/*
		 * printf("%d: %x (%x, %x) - %d\n", i, te, te->prev, te->next,
		 * te->oidVal);
		 */
		te = te->next;
	}
B
Bruce Momjian 已提交
2391 2392


B
Bruce Momjian 已提交
2393 2394
	AH->toc->next = tea[1];
	AH->toc->prev = tea[AH->tocCount];
B
Bruce Momjian 已提交
2395 2396
}

B
Bruce Momjian 已提交
2397 2398
static int
_tocSortCompareByOIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2399
{
B
Bruce Momjian 已提交
2400 2401
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
2402 2403 2404
	Oid			id1 = te1->maxOidVal;
	Oid			id2 = te2->maxOidVal;
	int			cmpval;
B
Bruce Momjian 已提交
2405

B
Bruce Momjian 已提交
2406
	/* printf("Comparing %d to %d\n", id1, id2); */
B
Bruce Momjian 已提交
2407

2408 2409 2410 2411
	cmpval = oidcmp(id1, id2);

	/* If we have a deterministic answer, return it. */
	if (cmpval != 0)
2412
		return cmpval;
2413 2414

	/* More comparisons required */
2415
	if (oideq(id1, te1->maxDepOidVal))	/* maxOid1 came from deps */
2416
	{
2417 2418
		if (oideq(id2, te2->maxDepOidVal))		/* maxOid2 also came from
												 * deps */
2419
		{
2420 2421
			cmpval = oidcmp(te1->oidVal, te2->oidVal);	/* Just compare base
														 * OIDs */
2422
		}
2423 2424
		else
/* MaxOid2 was entry OID */
2425
		{
2426
			return 1;			/* entry1 > entry2 */
2427
		};
2428 2429 2430
	}
	else
/* must have oideq(id1, te1->oidVal) => maxOid1 = Oid1 */
2431
	{
2432
		if (oideq(id2, te2->maxDepOidVal))		/* maxOid2 came from deps */
2433
		{
2434
			return -1;			/* entry1 < entry2 */
2435
		}
2436 2437
		else
/* MaxOid2 was entry OID - deps don't matter */
2438 2439 2440 2441 2442
		{
			cmpval = 0;
		};
	};

2443 2444 2445
	/*
	 * If we get here, then we've done another comparison Once again, a 0
	 * result means we require even more
2446 2447 2448 2449
	 */
	if (cmpval != 0)
		return cmpval;

2450 2451 2452 2453
	/*
	 * Entire OID details match, so use ID number (ie. original pg_dump
	 * order)
	 */
2454
	return _tocSortCompareByIDNum(te1, te2);
B
Bruce Momjian 已提交
2455 2456
}

B
Bruce Momjian 已提交
2457 2458
static int
_tocSortCompareByIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2459
{
B
Bruce Momjian 已提交
2460 2461 2462 2463
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
	int			id1 = te1->id;
	int			id2 = te2->id;
B
Bruce Momjian 已提交
2464

B
Bruce Momjian 已提交
2465
	/* printf("Comparing %d to %d\n", id1, id2); */
B
Bruce Momjian 已提交
2466

B
Bruce Momjian 已提交
2467 2468 2469 2470 2471 2472
	if (id1 < id2)
		return -1;
	else if (id1 > id2)
		return 1;
	else
		return 0;
B
Bruce Momjian 已提交
2473 2474
}

2475 2476 2477 2478
/*
 * Assuming Oid and depOid are set, work out the various
 * Oid values used in sorting.
 */
2479
static void
2480 2481 2482 2483 2484 2485
_fixupOidInfo(TocEntry *te)
{
	te->oidVal = atooid(te->oid);
	te->maxDepOidVal = _findMaxOID(te->depOid);

	/* For the purpose of sorting, find the max OID. */
2486
	if (oidcmp(te->oidVal, te->maxDepOidVal) >= 0)
2487 2488 2489 2490 2491
		te->maxOidVal = te->oidVal;
	else
		te->maxOidVal = te->maxDepOidVal;
}

2492 2493
/*
 * Find the max OID value for a given list of string Oid values
2494 2495 2496 2497 2498 2499
 */
static Oid
_findMaxOID(const char *((*deps)[]))
{
	const char *dep;
	int			i;
2500
	Oid			maxOid = (Oid) 0;
2501 2502 2503 2504 2505 2506
	Oid			currOid;

	if (!deps)
		return maxOid;

	i = 0;
2507
	while ((dep = (*deps)[i++]) != NULL)
2508 2509 2510 2511 2512 2513 2514 2515 2516
	{
		currOid = atooid(dep);
		if (oidcmp(maxOid, currOid) < 0)
			maxOid = currOid;
	}

	return maxOid;
}

2517 2518 2519 2520 2521 2522 2523 2524
/*
 * Maybe I can use this somewhere...
 *
 *create table pgdump_blob_path(p text);
 *insert into pgdump_blob_path values('/home/pjw/work/postgresql-cvs/pgsql/src/bin/pg_dump_140');
 *
 *insert into dump_blob_xref select 12345,lo_import(p || '/q.q') from pgdump_blob_path;
 */