pg_backup_archiver.c 52.0 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.40 2002/01/18 19:17:04 momjian 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 <errno.h>
B
Bruce Momjian 已提交
78
#include <unistd.h>				/* for dup */
B
Bruce Momjian 已提交
79

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

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

B
Bruce Momjian 已提交
90 91 92 93 94
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);
95
static int	_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData);
96

B
Bruce Momjian 已提交
97
static void _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te);
98
static void _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user);
99

100
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt);
B
Bruce Momjian 已提交
101 102 103 104 105 106
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);
107
static void _fixupOidInfo(TocEntry *te);
108
static Oid	_findMaxOID(const char *((*deps)[]));
109

110
const char *progname;
111
static char *modulename = gettext_noop("archiver");
B
Bruce Momjian 已提交
112

113 114
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);
115

B
Bruce Momjian 已提交
116 117
static int	_canRestoreBlobs(ArchiveHandle *AH);
static int	_restoringToDB(ArchiveHandle *AH);
118

B
Bruce Momjian 已提交
119
/*
B
Bruce Momjian 已提交
120 121 122 123
 *	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 已提交
124 125 126 127 128 129
 *
 */


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

B
Bruce Momjian 已提交
134
{
B
Bruce Momjian 已提交
135 136 137
	ArchiveHandle *AH = _allocAH(FileSpec, fmt, compression, archModeWrite);

	return (Archive *) AH;
B
Bruce Momjian 已提交
138 139 140 141
}

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

	return (Archive *) AH;
B
Bruce Momjian 已提交
148 149 150
}

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

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

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

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

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

180 181
	AH->ropt = ropt;

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

194 195 196
	if (ropt->create && ropt->dropSchema)
		die_horribly(AH, modulename, "-C and -c are incompatible options\n");

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

206 207 208 209
		/* XXX Should get this from the archive */
		AHX->minRemoteVersion = 070100;
		AHX->maxRemoteVersion = 999999;

210
		ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport, ropt->username,
B
Bruce Momjian 已提交
211
						ropt->requirePassword, ropt->ignoreVersion);
212 213

		/*
B
Bruce Momjian 已提交
214 215
		 * If no superuser was specified then see if the current user will
		 * do...
216 217 218 219 220 221 222
		 */
		if (!ropt->superuser)
		{
			if (UserIsSuperuser(AH, ConnectedUser(AH)))
				ropt->superuser = strdup(ConnectedUser(AH));
		}

223 224
	}

225
	/*
B
Bruce Momjian 已提交
226 227 228 229
	 * Work out if we have an implied data-only retore. This can happen if
	 * 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.
230
	 *
B
Bruce Momjian 已提交
231
	 * We could scan for wanted TABLE entries, but that is not the same as
232
	 * dataOnly. At this stage, it seems unnecessary (6-Mar-2001).
B
Bruce Momjian 已提交
233 234 235
	 */
	if (!ropt->dataOnly)
	{
236 237
		te = AH->toc->next;
		impliedDataOnly = 1;
B
Bruce Momjian 已提交
238 239
		while (te != AH->toc)
		{
240
			reqs = _tocEntryRequired(te, ropt);
241
			if ((reqs & REQ_SCHEMA) != 0)
B
Bruce Momjian 已提交
242
			{					/* It's schema, and it's wanted */
243 244 245 246 247 248 249 250
				impliedDataOnly = 0;
				break;
			}
			te = te->next;
		}
		if (impliedDataOnly)
		{
			ropt->dataOnly = impliedDataOnly;
251
			ahlog(AH, 1, "implied data-only restore\n");
252
		}
B
Bruce Momjian 已提交
253
	}
254

255
	if (!ropt->superuser)
256 257 258
		write_msg(modulename, "WARNING:\n"
				  "  Data restoration may fail because existing triggers cannot be disabled\n"
				  "  (no superuser user name specified).  This is only a problem when\n"
259
		"  restoring into a database with already existing triggers.\n");
260

261
	/*
B
Bruce Momjian 已提交
262
	 * Setup the output file if necessary.
263
	 */
B
Bruce Momjian 已提交
264
	if (ropt->filename || ropt->compression)
265
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
266

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

B
Bruce Momjian 已提交
269 270
	/*
	 * Drop the items at the start, in reverse order
271
	 */
B
Bruce Momjian 已提交
272 273
	if (ropt->dropSchema)
	{
274
		te = AH->toc->prev;
B
Bruce Momjian 已提交
275 276
		while (te != AH->toc)
		{
277
			reqs = _tocEntryRequired(te, ropt);
278
			if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt)
279 280 281
			{
				/* We want the schema */
				ahlog(AH, 1, "dropping %s %s\n", te->desc, te->name);
282
				/* Reconnect if necessary */
283
				_reconnectAsOwner(AH, NULL, te);
284
				/* Drop it */
285 286 287 288
				ahprintf(AH, "%s", te->dropStmt);
			}
			te = te->prev;
		}
B
Bruce Momjian 已提交
289
	}
B
Bruce Momjian 已提交
290

291 292 293
	/*
	 * Now process each TOC entry
	 */
B
Bruce Momjian 已提交
294 295 296
	te = AH->toc->next;
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
297

298 299 300
		/* Work out what, if anything, we want from this entry */
		reqs = _tocEntryRequired(te, ropt);

301 302 303
		/* Dump any relevant dump warnings to stderr */
		if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
		{
304
			if (!ropt->dataOnly && te->defn != NULL && strlen(te->defn) != 0)
305 306 307
				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);
308
		}
309

310 311
		defnDumped = false;

312
		if ((reqs & REQ_SCHEMA) != 0)	/* We want the schema */
313
		{
314
			/* Reconnect if necessary */
315
			_reconnectAsOwner(AH, NULL, te);
316

317
			ahlog(AH, 1, "creating %s %s\n", te->desc, te->name);
318
			_printTocEntry(AH, te, ropt, false);
319
			defnDumped = true;
320 321

			/* If we created a DB, connect to it... */
B
Bruce Momjian 已提交
322
			if (strcmp(te->desc, "DATABASE") == 0)
323
			{
324
				ahlog(AH, 1, "connecting to new database %s as user %s\n", te->name, te->owner);
325 326
				_reconnectAsUser(AH, te->name, te->owner);
			}
327
		}
B
Bruce Momjian 已提交
328

B
Bruce Momjian 已提交
329
		/*
330
		 * If we have a data component, then process it
331
		 */
332
		if ((reqs & REQ_DATA) != 0)
B
Bruce Momjian 已提交
333
		{
334 335 336 337 338
			/*
			 * 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.
339
			 */
340
			if (te->hadDumper)
341 342
			{
				/*
343
				 * If we can output the data, then restore it.
344
				 */
345
				if (AH->PrintTocDataPtr !=NULL && (reqs & REQ_DATA) != 0)
346 347 348
				{
#ifndef HAVE_LIBZ
					if (AH->compression != 0)
349
						die_horribly(AH, modulename, "unable to restore from compressed archive (not configured for compression support)\n");
350
#endif
351

352 353 354
					_printTocEntry(AH, te, ropt, true);

					/*
355 356
					 * Maybe we can't do BLOBS, so check if this node is
					 * for BLOBS
357 358 359 360 361 362
					 */
					if ((strcmp(te->desc, "BLOBS") == 0) && !_canRestoreBlobs(AH))
					{
						ahprintf(AH, "--\n-- SKIPPED \n--\n\n");

						/*
363 364 365
						 * This is a bit nasty - we assume, for the
						 * moment, that if a custom output is used, then
						 * we don't want warnings.
366 367
						 */
						if (!AH->CustomOutPtr)
368
							write_msg(modulename, "WARNING: skipping large object restoration\n");
369 370 371 372 373 374 375 376

					}
					else
					{

						_disableTriggersIfNecessary(AH, te, ropt);

						/*
377 378
						 * Reconnect if necessary (_disableTriggers may
						 * have reconnected)
379
						 */
380
						_reconnectAsOwner(AH, NULL, te);
381

382
						ahlog(AH, 1, "restoring data for table %s\n", te->name);
383 384

						/*
385 386 387 388 389
						 * 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.
390
						 *
391 392 393
						 * For V1.3+, the table data MUST have a copy
						 * statement so that we can go into appropriate
						 * mode with libpq.
394 395 396 397 398 399 400 401 402
						 */
						if (te->copyStmt && strlen(te->copyStmt) > 0)
							ahprintf(AH, te->copyStmt);

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

						_enableTriggersIfNecessary(AH, te, ropt);
					}
				}
403 404 405 406
			}
			else if (!defnDumped)
			{
				/* If we haven't already dumped the defn part, do so now */
407
				ahlog(AH, 1, "executing %s %s\n", te->desc, te->name);
408
				_printTocEntry(AH, te, ropt, false);
409 410 411
			}
		}
		te = te->next;
B
Bruce Momjian 已提交
412
	}
B
Bruce Momjian 已提交
413

414
	/*
B
Bruce Momjian 已提交
415 416
	 * Now use blobs_xref (if used) to fixup any refs for tables that we
	 * loaded
417 418 419
	 */
	if (_canRestoreBlobs(AH) && AH->createdBlobXref)
	{
420
		/* NULL parameter means disable ALL user triggers */
421
		_disableTriggersIfNecessary(AH, NULL, ropt);
422

423
		te = AH->toc->next;
B
Bruce Momjian 已提交
424 425
		while (te != AH->toc)
		{
426 427

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

431
				ahlog(AH, 2, "checking whether we loaded %s\n", te->name);
432 433 434

				reqs = _tocEntryRequired(te, ropt);

435
				if ((reqs & REQ_DATA) != 0)		/* We loaded the data */
436
				{
437
					ahlog(AH, 1, "fixing up large object cross-reference for %s\n", te->name);
438 439 440 441
					FixupBlobRefs(AH, te->name);
				}
			}
			else
442
				ahlog(AH, 2, "ignoring large object cross-references for %s %s\n", te->desc, te->name);
443 444 445

			te = te->next;
		}
446 447

		/* NULL parameter means enable ALL user triggers */
448
		_enableTriggersIfNecessary(AH, NULL, ropt);
449 450 451 452 453
	}

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

457 458 459 460
	if (ropt->useDB)
	{
		PQfinish(AH->connection);
		AH->connection = NULL;
461 462 463 464 465 466

		if (AH->blobConnection)
		{
			PQfinish(AH->blobConnection);
			AH->blobConnection = NULL;
		}
467
	}
B
Bruce Momjian 已提交
468 469
}

470 471 472 473
/*
 * Allocate a new RestoreOptions block.
 * This is mainly so we can initialize it, but also for future expansion,
 */
B
Bruce Momjian 已提交
474 475
RestoreOptions *
NewRestoreOptions(void)
B
Bruce Momjian 已提交
476
{
B
Bruce Momjian 已提交
477
	RestoreOptions *opts;
B
Bruce Momjian 已提交
478

B
Bruce Momjian 已提交
479
	opts = (RestoreOptions *) calloc(1, sizeof(RestoreOptions));
B
Bruce Momjian 已提交
480 481

	opts->format = archUnknown;
482
	opts->suppressDumpWarnings = false;
B
Bruce Momjian 已提交
483 484 485 486

	return opts;
}

487 488 489 490
/*
 * 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 已提交
491 492
static int
_restoringToDB(ArchiveHandle *AH)
493 494 495 496
{
	return (AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
497 498
static int
_canRestoreBlobs(ArchiveHandle *AH)
499 500 501 502
{
	return _restoringToDB(AH);
}

B
Bruce Momjian 已提交
503 504
static void
_disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
505
{
B
Bruce Momjian 已提交
506
	char	   *oldUser = NULL;
507 508

	/* Can't do much if we're connected & don't have a superuser */
B
Bruce Momjian 已提交
509 510
	/* Also, don't bother with triggers unless a data-only retore. */
	if (!ropt->dataOnly || (_restoringToDB(AH) && !ropt->superuser))
511 512 513
		return;

	/*
B
Bruce Momjian 已提交
514
	 * Reconnect as superuser if possible, since they are the only ones
515 516 517 518
	 * who can update pg_class...
	 */
	if (ropt->superuser)
	{
519 520
		if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH))
		{
B
Bruce Momjian 已提交
521 522 523 524
			/*
			 * If we're not allowing changes for ownership, then remember
			 * the user so we can change it back here. Otherwise, let
			 * _reconnectAsOwner do what it has to do.
525 526 527
			 */
			if (ropt->noOwner)
				oldUser = strdup(ConnectedUser(AH));
528
			_reconnectAsUser(AH, NULL, ropt->superuser);
529
		}
530 531
	}

532
	ahlog(AH, 1, "disabling triggers\n");
533 534

	/*
B
Bruce Momjian 已提交
535 536
	 * Disable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
537
	 */
B
Bruce Momjian 已提交
538
	ahprintf(AH, "-- Disable triggers\n");
539 540 541 542 543 544

	/*
	 * Just update the AFFECTED table, if known.
	 */

	if (te && te->name && strlen(te->name) > 0)
B
Bruce Momjian 已提交
545 546
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" = '%s';\n\n",
				 te->name);
547 548
	else
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" !~ '^pg_';\n\n");
549 550

	/*
B
Bruce Momjian 已提交
551 552
	 * Restore the user connection from the start of this procedure if
	 * _reconnectAsOwner is disabled.
553 554 555
	 */
	if (ropt->noOwner && oldUser)
	{
556
		_reconnectAsUser(AH, NULL, oldUser);
557 558
		free(oldUser);
	}
B
Bruce Momjian 已提交
559 560
}

B
Bruce Momjian 已提交
561 562
static void
_enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
563
{
B
Bruce Momjian 已提交
564
	char	   *oldUser = NULL;
565 566

	/* Can't do much if we're connected & don't have a superuser */
567
	/* Also, don't bother with triggers unless a data-only retore. */
B
Bruce Momjian 已提交
568
	if (!ropt->dataOnly || (_restoringToDB(AH) && !ropt->superuser))
569 570 571 572 573 574 575 576
		return;

	/*
	 * Reconnect as superuser if possible, since they are the only ones
	 * who can update pg_class...
	 */
	if (ropt->superuser)
	{
577 578
		if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH))
		{
B
Bruce Momjian 已提交
579 580 581 582
			/*
			 * If we're not allowing changes for ownership, then remember
			 * the user so we can change it back here. Otherwise, let
			 * _reconnectAsOwner do what it has to do
583 584 585
			 */
			if (ropt->noOwner)
				oldUser = strdup(ConnectedUser(AH));
586

587
			_reconnectAsUser(AH, NULL, ropt->superuser);
588
		}
589 590
	}

591
	ahlog(AH, 1, "enabling triggers\n");
592 593

	/*
B
Bruce Momjian 已提交
594 595
	 * Enable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
596
	 */
B
Bruce Momjian 已提交
597
	ahprintf(AH, "-- Enable triggers\n");
598 599 600
	if (te && te->name && strlen(te->name) > 0)
	{
		ahprintf(AH, "UPDATE pg_class SET reltriggers = "
B
Bruce Momjian 已提交
601
		"(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) "
602
				 "WHERE relname = '%s';\n\n",
B
Bruce Momjian 已提交
603 604 605 606
				 te->name);
	}
	else
	{
607
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = "
B
Bruce Momjian 已提交
608
		"(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) "
609 610
				 "WHERE \"relname\" !~ '^pg_';\n\n");
	}
B
Bruce Momjian 已提交
611

612
	/*
B
Bruce Momjian 已提交
613 614
	 * Restore the user connection from the start of this procedure if
	 * _reconnectAsOwner is disabled.
615 616 617
	 */
	if (ropt->noOwner && oldUser)
	{
618
		_reconnectAsUser(AH, NULL, oldUser);
619 620 621
		free(oldUser);
	}
}
B
Bruce Momjian 已提交
622 623

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

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

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

B
Bruce Momjian 已提交
636
	return (*AH->WriteDataPtr) (AH, data, dLen);
B
Bruce Momjian 已提交
637 638 639
}

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

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

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

	newToc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!newToc)
659
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
660 661 662 663 664 665 666

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

	newToc->id = AH->lastID;
667

B
Bruce Momjian 已提交
668 669
	newToc->name = strdup(name);
	newToc->desc = strdup(desc);
670 671 672 673 674 675

	newToc->oid = strdup(oid);
	newToc->depOid = deps;
	_fixupOidInfo(newToc);


B
Bruce Momjian 已提交
676 677
	newToc->defn = strdup(defn);
	newToc->dropStmt = strdup(dropStmt);
678
	newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
B
Bruce Momjian 已提交
679 680 681 682 683
	newToc->owner = strdup(owner);
	newToc->printed = 0;
	newToc->formatData = NULL;
	newToc->dataDumper = dumpFn,
		newToc->dataDumperArg = dumpArg;
B
Bruce Momjian 已提交
684

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

B
Bruce Momjian 已提交
687 688
	if (AH->ArchiveEntryPtr !=NULL)
		(*AH->ArchiveEntryPtr) (AH, newToc);
B
Bruce Momjian 已提交
689

B
Bruce Momjian 已提交
690 691 692 693
	/*
	 * printf("New toc owned by '%s', oid %d\n", newToc->owner,
	 * newToc->oidVal);
	 */
B
Bruce Momjian 已提交
694 695 696
}

/* Public */
B
Bruce Momjian 已提交
697 698
void
PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
699
{
B
Bruce Momjian 已提交
700 701 702 703
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	OutputContext sav;
	char	   *fmtName;
B
Bruce Momjian 已提交
704

B
Bruce Momjian 已提交
705 706
	if (ropt->filename)
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
707

708 709
	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 已提交
710
			 AH->archdbname, AH->tocCount, AH->compression);
711

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

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

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

B
Bruce Momjian 已提交
732 733 734 735
	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 已提交
736
		te = te->next;
B
Bruce Momjian 已提交
737
	}
B
Bruce Momjian 已提交
738

B
Bruce Momjian 已提交
739 740
	if (ropt->filename)
		ResetOutput(AH, sav);
B
Bruce Momjian 已提交
741 742
}

743 744 745 746 747
/***********
 * BLOB Archival
 ***********/

/* Called by a dumper to signal start of a BLOB */
B
Bruce Momjian 已提交
748
int
749
StartBlob(Archive *AHX, Oid oid)
750
{
B
Bruce Momjian 已提交
751
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
752

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

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

B
Bruce Momjian 已提交
758
	return 1;
759 760 761
}

/* Called by a dumper to signal end of a BLOB */
B
Bruce Momjian 已提交
762
int
763
EndBlob(Archive *AHX, Oid oid)
764
{
B
Bruce Momjian 已提交
765
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
766

B
Bruce Momjian 已提交
767 768
	if (AH->EndBlobPtr)
		(*AH->EndBlobPtr) (AH, AH->currToc, oid);
769

B
Bruce Momjian 已提交
770
	return 1;
771 772 773 774 775 776
}

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

777
/*
B
Bruce Momjian 已提交
778
 * Called by a format handler before any blobs are restored
779
 */
B
Bruce Momjian 已提交
780 781
void
StartRestoreBlobs(ArchiveHandle *AH)
782 783 784 785 786
{
	AH->blobCount = 0;
}

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

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

801
	ahlog(AH, 1, "restored %d large objects\n", AH->blobCount);
802 803 804
}


805 806 807
/*
 * Called by a format handler to initiate restoration of a blob
 */
B
Bruce Momjian 已提交
808
void
809
StartRestoreBlob(ArchiveHandle *AH, Oid oid)
810
{
811
	Oid			loOid;
812

813 814
	AH->blobCount++;

815 816 817
	if (!AH->createdBlobXref)
	{
		if (!AH->connection)
818
			die_horribly(AH, modulename, "cannot restore large objects without a database connection\n");
819 820 821 822 823

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

824 825 826 827 828
	/*
	 * Start long-running TXs if necessary
	 */
	if (!AH->txActive)
	{
829
		ahlog(AH, 2, "starting large object transactions\n");
830 831 832 833
		StartTransaction(AH);
	}
	if (!AH->blobTxActive)
		StartTransactionXref(AH);
834

835 836
	loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
	if (loOid == 0)
837
		die_horribly(AH, modulename, "could not create large object\n");
838

839
	ahlog(AH, 2, "restoring large object with oid %u as %u\n", oid, loOid);
840 841 842 843 844

	InsertBlobXref(AH, oid, loOid);

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

B
Bruce Momjian 已提交
847
	AH->writingBlob = 1;
848 849
}

B
Bruce Momjian 已提交
850
void
851
EndRestoreBlob(ArchiveHandle *AH, Oid oid)
852
{
B
Bruce Momjian 已提交
853 854
	lo_close(AH->connection, AH->loFd);
	AH->writingBlob = 0;
855

856 857 858
	/*
	 * Commit every BLOB_BATCH_SIZE blobs...
	 */
B
Bruce Momjian 已提交
859
	if (((AH->blobCount / BLOB_BATCH_SIZE) * BLOB_BATCH_SIZE) == AH->blobCount)
860
	{
861
		ahlog(AH, 2, "committing large object transactions\n");
862 863 864
		CommitTransaction(AH);
		CommitTransactionXref(AH);
	}
865 866
}

B
Bruce Momjian 已提交
867 868 869 870 871 872 873
/***********
 * Sorting and Reordering
 ***********/

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

B
Bruce Momjian 已提交
875
/* Public */
B
Bruce Momjian 已提交
876 877
void
MoveToStart(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
878
{
B
Bruce Momjian 已提交
879 880 881
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;
B
Bruce Momjian 已提交
882

B
Bruce Momjian 已提交
883 884
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
885 886 887
		te->_moved = 0;
		te = te->next;
	}
B
Bruce Momjian 已提交
888 889 890 891 892 893 894

	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 已提交
895
		te = newTe;
B
Bruce Momjian 已提交
896
	}
B
Bruce Momjian 已提交
897 898 899 900 901 902 903
}


/*
 * Move TOC entries of the specified type to the end of the TOC.
 */
/* Public */
B
Bruce Momjian 已提交
904 905
void
MoveToEnd(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
906
{
B
Bruce Momjian 已提交
907 908 909 910 911 912 913 914
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;

	while (te != AH->toc)
	{
		te->_moved = 0;
		te = te->next;
B
Bruce Momjian 已提交
915
	}
B
Bruce Momjian 已提交
916 917 918 919 920 921 922

	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 已提交
923
		te = newTe;
B
Bruce Momjian 已提交
924
	}
B
Bruce Momjian 已提交
925 926
}

B
Bruce Momjian 已提交
927
/*
B
Bruce Momjian 已提交
928 929 930
 * Sort TOC by OID
 */
/* Public */
B
Bruce Momjian 已提交
931 932
void
SortTocByOID(Archive *AHX)
B
Bruce Momjian 已提交
933
{
B
Bruce Momjian 已提交
934 935 936
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByOIDNum);
B
Bruce Momjian 已提交
937 938 939 940 941 942
}

/*
 * Sort TOC by ID
 */
/* Public */
B
Bruce Momjian 已提交
943 944
void
SortTocByID(Archive *AHX)
B
Bruce Momjian 已提交
945
{
B
Bruce Momjian 已提交
946 947 948
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByIDNum);
B
Bruce Momjian 已提交
949 950
}

B
Bruce Momjian 已提交
951 952
void
SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
953
{
B
Bruce Momjian 已提交
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
	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 已提交
978

B
Bruce Momjian 已提交
979 980
	/* Set prev entry as head of list */
	tePrev = AH->toc;
B
Bruce Momjian 已提交
981

B
Bruce Momjian 已提交
982 983 984
	/* Setup the file */
	fh = fopen(ropt->tocFile, PG_BINARY_R);
	if (!fh)
985
		die_horribly(AH, modulename, "could not open TOC file\n");
B
Bruce Momjian 已提交
986

B
Bruce Momjian 已提交
987
	while (fgets(buf, 1024, fh) != NULL)
B
Bruce Momjian 已提交
988
	{
B
Bruce Momjian 已提交
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
		/* 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)
		{
1006
			write_msg(modulename, "WARNING: line ignored: %s\n", buf);
B
Bruce Momjian 已提交
1007 1008
			continue;
		}
B
Bruce Momjian 已提交
1009

B
Bruce Momjian 已提交
1010 1011 1012
		/* Find TOC entry */
		te = _getTocEntry(AH, id);
		if (!te)
1013
			die_horribly(AH, modulename, "could not find entry for id %d\n", id);
B
Bruce Momjian 已提交
1014

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

B
Bruce Momjian 已提交
1017 1018 1019
		_moveAfter(AH, tePrev, te);
		tePrev = te;
	}
B
Bruce Momjian 已提交
1020

B
Bruce Momjian 已提交
1021
	if (fclose(fh) != 0)
1022
		die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
B
Bruce Momjian 已提交
1023 1024 1025 1026 1027 1028 1029 1030
}

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

/* Public */
B
Bruce Momjian 已提交
1031 1032 1033 1034
int
archputs(const char *s, Archive *AH)
{
	return WriteData(AH, s, strlen(s));
B
Bruce Momjian 已提交
1035 1036 1037
}

/* Public */
B
Bruce Momjian 已提交
1038 1039 1040 1041
int
archputc(const char c, Archive *AH)
{
	return WriteData(AH, &c, 1);
B
Bruce Momjian 已提交
1042 1043 1044
}

/* Public */
B
Bruce Momjian 已提交
1045 1046
int
archprintf(Archive *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1047
{
B
Bruce Momjian 已提交
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
	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 已提交
1063
	{
B
Bruce Momjian 已提交
1064 1065
		if (p != NULL)
			free(p);
1066
		bSize *= 2;
B
Bruce Momjian 已提交
1067
		p = (char *) malloc(bSize);
1068
		if (p == NULL)
1069
			exit_horribly(AH, modulename, "out of memory\n");
1070 1071 1072
		va_start(ap, fmt);
		cnt = vsnprintf(p, bSize, fmt, ap);
		va_end(ap);
B
Bruce Momjian 已提交
1073 1074 1075 1076
	}
	WriteData(AH, p, cnt);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1077 1078 1079 1080 1081 1082 1083
}


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

B
Bruce Momjian 已提交
1084 1085
OutputContext
SetOutput(ArchiveHandle *AH, char *filename, int compression)
B
Bruce Momjian 已提交
1086
{
B
Bruce Momjian 已提交
1087 1088
	OutputContext sav;

1089
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1090
	char		fmode[10];
B
Bruce Momjian 已提交
1091
#endif
B
Bruce Momjian 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
	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 */
1111
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1112 1113
	if (compression != 0)
	{
1114
		sprintf(fmode, "wb%d", compression);
B
Bruce Momjian 已提交
1115 1116 1117 1118
		if (fn)
		{
			AH->OF = gzdopen(dup(fn), fmode);	/* Don't use PG_BINARY_x
												 * since this is zlib */
1119
		}
B
Bruce Momjian 已提交
1120 1121
		else
			AH->OF = gzopen(filename, fmode);
1122
		AH->gzOut = 1;
B
Bruce Momjian 已提交
1123 1124 1125
	}
	else
	{							/* Use fopen */
B
Bruce Momjian 已提交
1126
#endif
B
Bruce Momjian 已提交
1127
		if (fn)
1128
			AH->OF = fdopen(dup(fn), PG_BINARY_W);
B
Bruce Momjian 已提交
1129
		else
1130 1131
			AH->OF = fopen(filename, PG_BINARY_W);
		AH->gzOut = 0;
1132
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1133
	}
B
Bruce Momjian 已提交
1134 1135
#endif

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

B
Bruce Momjian 已提交
1139
	return sav;
B
Bruce Momjian 已提交
1140 1141
}

B
Bruce Momjian 已提交
1142 1143
void
ResetOutput(ArchiveHandle *AH, OutputContext sav)
B
Bruce Momjian 已提交
1144
{
B
Bruce Momjian 已提交
1145
	int			res;
1146

B
Bruce Momjian 已提交
1147
	if (AH->gzOut)
1148
		res = GZCLOSE(AH->OF);
B
Bruce Momjian 已提交
1149
	else
1150 1151 1152
		res = fclose(AH->OF);

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

B
Bruce Momjian 已提交
1155 1156
	AH->gzOut = sav.gzOut;
	AH->OF = sav.OF;
B
Bruce Momjian 已提交
1157 1158 1159 1160 1161
}



/*
B
Bruce Momjian 已提交
1162
 *	Print formatted text to the output file (usually stdout).
B
Bruce Momjian 已提交
1163
 */
B
Bruce Momjian 已提交
1164 1165
int
ahprintf(ArchiveHandle *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1166
{
B
Bruce Momjian 已提交
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
	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))
1182
	{
B
Bruce Momjian 已提交
1183 1184
		if (p != NULL)
			free(p);
1185
		bSize *= 2;
B
Bruce Momjian 已提交
1186
		p = (char *) malloc(bSize);
1187
		if (p == NULL)
1188
			die_horribly(AH, modulename, "out of memory\n");
1189
		va_start(ap, fmt);
1190
		cnt = vsnprintf(p, bSize, fmt, ap);
1191
		va_end(ap);
B
Bruce Momjian 已提交
1192 1193 1194 1195
	}
	ahwrite(p, 1, cnt, AH);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1196 1197
}

B
Bruce Momjian 已提交
1198 1199
void
ahlog(ArchiveHandle *AH, int level, const char *fmt,...)
1200 1201 1202 1203 1204 1205 1206
{
	va_list		ap;

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

	va_start(ap, fmt);
1207
	_write_msg(NULL, fmt, ap);
1208 1209 1210
	va_end(ap);
}

1211 1212 1213
/*
 * Single place for logic which says 'We are restoring to a direct DB connection'.
 */
B
Bruce Momjian 已提交
1214 1215
int
RestoringToDB(ArchiveHandle *AH)
1216 1217 1218 1219
{
	return (AH->ropt && AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
1220
/*
B
Bruce Momjian 已提交
1221 1222 1223
 *	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
1224
 *	wants to generate a script (see TAR output).
B
Bruce Momjian 已提交
1225
 */
B
Bruce Momjian 已提交
1226 1227
int
ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
B
Bruce Momjian 已提交
1228
{
B
Bruce Momjian 已提交
1229
	int			res;
1230

B
Bruce Momjian 已提交
1231
	if (AH->writingBlob)
1232
	{
B
Bruce Momjian 已提交
1233
		res = lo_write(AH->connection, AH->loFd, (void *) ptr, size * nmemb);
1234 1235 1236
		ahlog(AH, 5, "wrote %d bytes of large object data (result = %d)\n",
			  (int) (size * nmemb), res);
		if (res != size * nmemb)
1237
			die_horribly(AH, modulename, "could not write to large object (result: %d, expected: %d)\n",
1238
						 res, (int) (size * nmemb));
1239

1240 1241
		return res;
	}
B
Bruce Momjian 已提交
1242
	else if (AH->gzOut)
1243
	{
B
Bruce Momjian 已提交
1244
		res = GZWRITE((void *) ptr, size, nmemb, AH->OF);
1245
		if (res != (nmemb * size))
1246
			die_horribly(AH, modulename, "could not write to compressed archive\n");
1247 1248
		return res;
	}
B
Bruce Momjian 已提交
1249
	else if (AH->CustomOutPtr)
1250
	{
B
Bruce Momjian 已提交
1251 1252
		res = AH->CustomOutPtr (AH, ptr, size * nmemb);

1253
		if (res != (nmemb * size))
1254
			die_horribly(AH, modulename, "could not write to custom output routine\n");
1255 1256
		return res;
	}
1257 1258 1259
	else
	{
		/*
B
Bruce Momjian 已提交
1260 1261 1262
		 * If we're doing a restore, and it's direct to DB, and we're
		 * connected then send it to the DB.
		 */
1263
		if (RestoringToDB(AH))
B
Bruce Momjian 已提交
1264
			return ExecuteSqlCommandBuf(AH, (void *) ptr, size * nmemb);		/* Always 1, currently */
1265
		else
1266
		{
B
Bruce Momjian 已提交
1267
			res = fwrite((void *) ptr, size, nmemb, AH->OF);
1268
			if (res != nmemb)
1269 1270
				die_horribly(AH, modulename, "could not write to output file (%d != %d)\n",
							 res, (int) nmemb);
1271 1272
			return res;
		}
1273
	}
B
Bruce Momjian 已提交
1274
}
1275 1276

/* Common exit code */
B
Bruce Momjian 已提交
1277
static void
1278
_write_msg(const char *modulename, const char *fmt, va_list ap)
1279
{
1280
	if (modulename)
1281
		fprintf(stderr, "%s: [%s] ", progname, gettext(modulename));
1282 1283 1284 1285 1286 1287
	else
		fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, gettext(fmt), ap);
}

void
1288
write_msg(const char *modulename, const char *fmt,...)
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
{
	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);
1302 1303
	if (AH->public.verbose)
		write_msg(NULL, "*** aborted because of error\n");
1304

B
Bruce Momjian 已提交
1305 1306 1307
	if (AH)
		if (AH->connection)
			PQfinish(AH->connection);
1308 1309
	if (AH->blobConnection)
		PQfinish(AH->blobConnection);
1310

B
Bruce Momjian 已提交
1311
	exit(1);
B
Bruce Momjian 已提交
1312 1313
}

1314
/* External use */
B
Bruce Momjian 已提交
1315
void
1316
exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
1317
{
B
Bruce Momjian 已提交
1318
	va_list		ap;
1319

B
Bruce Momjian 已提交
1320
	va_start(ap, fmt);
1321
	_die_horribly((ArchiveHandle *) AH, modulename, fmt, ap);
1322
}
B
Bruce Momjian 已提交
1323

1324
/* Archiver use (just different arg declaration) */
B
Bruce Momjian 已提交
1325
void
1326
die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...)
B
Bruce Momjian 已提交
1327
{
B
Bruce Momjian 已提交
1328
	va_list		ap;
B
Bruce Momjian 已提交
1329

B
Bruce Momjian 已提交
1330
	va_start(ap, fmt);
1331
	_die_horribly(AH, modulename, fmt, ap);
B
Bruce Momjian 已提交
1332 1333
}

1334

B
Bruce Momjian 已提交
1335 1336
static void
_moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1337
{
B
Bruce Momjian 已提交
1338 1339
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1340

B
Bruce Momjian 已提交
1341 1342
	te->prev = pos;
	te->next = pos->next;
B
Bruce Momjian 已提交
1343

B
Bruce Momjian 已提交
1344 1345
	pos->next->prev = te;
	pos->next = te;
B
Bruce Momjian 已提交
1346

B
Bruce Momjian 已提交
1347
	te->_moved = 1;
B
Bruce Momjian 已提交
1348 1349
}

B
Bruce Momjian 已提交
1350 1351
static void
_moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1352
{
B
Bruce Momjian 已提交
1353 1354
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1355

B
Bruce Momjian 已提交
1356 1357 1358 1359
	te->prev = pos->prev;
	te->next = pos;
	pos->prev->next = te;
	pos->prev = te;
B
Bruce Momjian 已提交
1360

B
Bruce Momjian 已提交
1361
	te->_moved = 1;
B
Bruce Momjian 已提交
1362 1363
}

B
Bruce Momjian 已提交
1364 1365
static TocEntry *
_getTocEntry(ArchiveHandle *AH, int id)
B
Bruce Momjian 已提交
1366
{
B
Bruce Momjian 已提交
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
	TocEntry   *te;

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

B
Bruce Momjian 已提交
1379 1380
int
TocIDRequired(ArchiveHandle *AH, int id, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1381
{
B
Bruce Momjian 已提交
1382
	TocEntry   *te = _getTocEntry(AH, id);
B
Bruce Momjian 已提交
1383

B
Bruce Momjian 已提交
1384 1385
	if (!te)
		return 0;
B
Bruce Momjian 已提交
1386

B
Bruce Momjian 已提交
1387
	return _tocEntryRequired(te, ropt);
B
Bruce Momjian 已提交
1388 1389
}

B
Bruce Momjian 已提交
1390 1391
int
WriteInt(ArchiveHandle *AH, int i)
B
Bruce Momjian 已提交
1392
{
B
Bruce Momjian 已提交
1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
	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);
1407
		i = -i;
B
Bruce Momjian 已提交
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
	}
	else
		(*AH->WriteBytePtr) (AH, 0);

	for (b = 0; b < AH->intSize; b++)
	{
		(*AH->WriteBytePtr) (AH, i & 0xFF);
		i = i / 256;
	}

	return AH->intSize + 1;
B
Bruce Momjian 已提交
1419 1420
}

B
Bruce Momjian 已提交
1421 1422
int
ReadInt(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1423
{
B
Bruce Momjian 已提交
1424 1425 1426 1427 1428
	int			res = 0;
	int			bv,
				b;
	int			sign = 0;		/* Default positive */
	int			bitShift = 0;
B
Bruce Momjian 已提交
1429

B
Bruce Momjian 已提交
1430
	if (AH->version > K_VERS_1_0)
1431
		/* Read a sign byte */
B
Bruce Momjian 已提交
1432
		sign = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
1433

B
Bruce Momjian 已提交
1434 1435 1436
	for (b = 0; b < AH->intSize; b++)
	{
		bv = (*AH->ReadBytePtr) (AH) & 0xFF;
1437 1438 1439
		if (bv != 0)
			res = res + (bv << bitShift);
		bitShift += 8;
B
Bruce Momjian 已提交
1440
	}
B
Bruce Momjian 已提交
1441

B
Bruce Momjian 已提交
1442 1443
	if (sign)
		res = -res;
B
Bruce Momjian 已提交
1444

B
Bruce Momjian 已提交
1445
	return res;
B
Bruce Momjian 已提交
1446 1447
}

B
Bruce Momjian 已提交
1448
int
1449
WriteStr(ArchiveHandle *AH, const char *c)
B
Bruce Momjian 已提交
1450
{
B
Bruce Momjian 已提交
1451
	int			res;
1452 1453 1454 1455

	if (c)
	{
		res = WriteInt(AH, strlen(c));
B
Bruce Momjian 已提交
1456
		res += (*AH->WriteBufPtr) (AH, c, strlen(c));
1457 1458 1459 1460
	}
	else
		res = WriteInt(AH, -1);

B
Bruce Momjian 已提交
1461
	return res;
B
Bruce Momjian 已提交
1462 1463
}

B
Bruce Momjian 已提交
1464 1465
char *
ReadStr(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1466
{
B
Bruce Momjian 已提交
1467 1468
	char	   *buf;
	int			l;
B
Bruce Momjian 已提交
1469

B
Bruce Momjian 已提交
1470
	l = ReadInt(AH);
1471 1472 1473 1474
	if (l == -1)
		buf = NULL;
	else
	{
B
Bruce Momjian 已提交
1475
		buf = (char *) malloc(l + 1);
1476
		if (!buf)
1477
			die_horribly(AH, modulename, "out of memory\n");
1478

B
Bruce Momjian 已提交
1479
		(*AH->ReadBufPtr) (AH, (void *) buf, l);
1480 1481
		buf[l] = '\0';
	}
B
Bruce Momjian 已提交
1482

B
Bruce Momjian 已提交
1483
	return buf;
B
Bruce Momjian 已提交
1484 1485
}

T
Tom Lane 已提交
1486
static int
B
Bruce Momjian 已提交
1487
_discoverArchiveFormat(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1488
{
B
Bruce Momjian 已提交
1489 1490 1491 1492
	FILE	   *fh;
	char		sig[6];			/* More than enough */
	int			cnt;
	int			wantClose = 0;
B
Bruce Momjian 已提交
1493

1494
#if 0
1495
	write_msg(modulename, "attempting to ascertain archive format\n");
1496
#endif
1497 1498 1499 1500 1501 1502 1503 1504

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

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

B
Bruce Momjian 已提交
1506 1507
	if (AH->fSpec)
	{
1508 1509
		wantClose = 1;
		fh = fopen(AH->fSpec, PG_BINARY_R);
B
Bruce Momjian 已提交
1510 1511
	}
	else
1512
		fh = stdin;
B
Bruce Momjian 已提交
1513

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

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

B
Bruce Momjian 已提交
1519
	if (cnt != 5)
1520 1521 1522 1523 1524 1525
	{
		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 已提交
1526

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

B
Bruce Momjian 已提交
1531
	if (strncmp(sig, "PGDMP", 5) == 0)
1532 1533 1534 1535 1536 1537 1538 1539 1540
	{
		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 已提交
1541 1542
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
		{
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
			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 已提交
1556 1557 1558 1559
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
	}
	else
	{
1560
		/*
B
Bruce Momjian 已提交
1561 1562
		 * *Maybe* we have a tar archive format file... So, read first 512
		 * byte header...
1563 1564 1565
		 */
		cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
		AH->lookaheadLen += cnt;
B
Bruce Momjian 已提交
1566

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

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

1573 1574
		AH->format = archTar;
	}
B
Bruce Momjian 已提交
1575

B
Bruce Momjian 已提交
1576 1577
	/* If we can't seek, then mark the header as read */
	if (fseek(fh, 0, SEEK_SET) != 0)
1578 1579
	{
		/*
B
Bruce Momjian 已提交
1580 1581
		 * NOTE: Formats that use the looahead buffer can unset this in
		 * their Init routine.
1582 1583 1584 1585
		 */
		AH->readHeader = 1;
	}
	else
B
Bruce Momjian 已提交
1586
		AH->lookaheadLen = 0;	/* Don't bother since we've reset the file */
1587

1588 1589 1590
#if 0
	write_msg(modulename, "read %d bytes into lookahead buffer\n", AH->lookaheadLen);
#endif
B
Bruce Momjian 已提交
1591

B
Bruce Momjian 已提交
1592 1593
	/* Close the file */
	if (wantClose)
1594
		if (fclose(fh) != 0)
1595 1596
			die_horribly(AH, modulename, "could not close the input file after reading header: %s\n",
						 strerror(errno));
B
Bruce Momjian 已提交
1597

B
Bruce Momjian 已提交
1598
	return AH->format;
B
Bruce Momjian 已提交
1599 1600 1601 1602 1603 1604
}


/*
 * Allocate an archive handle
 */
B
Bruce Momjian 已提交
1605 1606 1607
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
		 const int compression, ArchiveMode mode)
1608
{
B
Bruce Momjian 已提交
1609
	ArchiveHandle *AH;
B
Bruce Momjian 已提交
1610

1611
#if 0
1612
	write_msg(modulename, "allocating AH for %s, format %d\n", FileSpec, fmt);
1613
#endif
1614

B
Bruce Momjian 已提交
1615 1616
	AH = (ArchiveHandle *) calloc(1, sizeof(ArchiveHandle));
	if (!AH)
1617
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1618

B
Bruce Momjian 已提交
1619 1620
	AH->vmaj = K_VERS_MAJOR;
	AH->vmin = K_VERS_MINOR;
1621
	AH->vrev = K_VERS_REV;
B
Bruce Momjian 已提交
1622

1623 1624
	AH->createDate = time(NULL);

B
Bruce Momjian 已提交
1625 1626 1627 1628
	AH->intSize = sizeof(int);
	AH->lastID = 0;
	if (FileSpec)
	{
1629
		AH->fSpec = strdup(FileSpec);
B
Bruce Momjian 已提交
1630

1631 1632 1633
		/*
		 * Not used; maybe later....
		 *
B
Bruce Momjian 已提交
1634 1635
		 * AH->workDir = strdup(FileSpec); for(i=strlen(FileSpec) ; i > 0 ;
		 * i--) if (AH->workDir[i-1] == '/')
1636
		 */
B
Bruce Momjian 已提交
1637 1638
	}
	else
1639
		AH->fSpec = NULL;
B
Bruce Momjian 已提交
1640

B
Bruce Momjian 已提交
1641 1642
	AH->currUser = strdup("");	/* So it's valid, but we can free() it
								 * later if necessary */
B
Bruce Momjian 已提交
1643

B
Bruce Momjian 已提交
1644 1645
	AH->toc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!AH->toc)
1646
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1647

B
Bruce Momjian 已提交
1648 1649 1650 1651 1652
	AH->toc->next = AH->toc;
	AH->toc->prev = AH->toc;

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

1654 1655
	AH->pgCopyBuf = createPQExpBuffer();
	AH->sqlBuf = createPQExpBuffer();
B
Bruce Momjian 已提交
1656

B
Bruce Momjian 已提交
1657 1658 1659
	/* Open stdout with no compression for AH output handle */
	AH->gzOut = 0;
	AH->OF = stdout;
B
Bruce Momjian 已提交
1660

1661
#if 0
1662
	write_msg(modulename, "archive format is %d\n", fmt);
1663
#endif
1664

B
Bruce Momjian 已提交
1665
	if (fmt == archUnknown)
1666 1667 1668
		AH->format = _discoverArchiveFormat(AH);
	else
		AH->format = fmt;
B
Bruce Momjian 已提交
1669

B
Bruce Momjian 已提交
1670 1671
	switch (AH->format)
	{
B
Bruce Momjian 已提交
1672

1673 1674 1675
		case archCustom:
			InitArchiveFmt_Custom(AH);
			break;
B
Bruce Momjian 已提交
1676

1677 1678 1679
		case archFiles:
			InitArchiveFmt_Files(AH);
			break;
B
Bruce Momjian 已提交
1680

1681 1682 1683
		case archNull:
			InitArchiveFmt_Null(AH);
			break;
B
Bruce Momjian 已提交
1684

1685 1686 1687 1688 1689
		case archTar:
			InitArchiveFmt_Tar(AH);
			break;

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

B
Bruce Momjian 已提交
1693
	return AH;
B
Bruce Momjian 已提交
1694 1695 1696
}


B
Bruce Momjian 已提交
1697 1698
void
WriteDataChunks(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1699
{
B
Bruce Momjian 已提交
1700 1701 1702
	TocEntry   *te = AH->toc->next;
	StartDataPtr startPtr;
	EndDataPtr	endPtr;
B
Bruce Momjian 已提交
1703

B
Bruce Momjian 已提交
1704 1705 1706
	while (te != AH->toc)
	{
		if (te->dataDumper != NULL)
1707
		{
B
Bruce Momjian 已提交
1708 1709
			AH->currToc = te;
			/* printf("Writing data for %d (%x)\n", te->id, te); */
1710

B
Bruce Momjian 已提交
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
			if (strcmp(te->desc, "BLOBS") == 0)
			{
				startPtr = AH->StartBlobsPtr;
				endPtr = AH->EndBlobsPtr;
			}
			else
			{
				startPtr = AH->StartDataPtr;
				endPtr = AH->EndDataPtr;
			}
B
Bruce Momjian 已提交
1721

B
Bruce Momjian 已提交
1722 1723
			if (startPtr != NULL)
				(*startPtr) (AH, te);
B
Bruce Momjian 已提交
1724

B
Bruce Momjian 已提交
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
			/*
			 * 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;
		}
1740
		te = te->next;
B
Bruce Momjian 已提交
1741
	}
B
Bruce Momjian 已提交
1742 1743
}

B
Bruce Momjian 已提交
1744 1745
void
WriteToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1746
{
B
Bruce Momjian 已提交
1747
	TocEntry   *te = AH->toc->next;
1748 1749
	const char *dep;
	int			i;
B
Bruce Momjian 已提交
1750 1751 1752 1753 1754 1755 1756 1757 1758

	/* 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);
1759

B
Bruce Momjian 已提交
1760 1761 1762 1763 1764 1765
		WriteStr(AH, te->name);
		WriteStr(AH, te->desc);
		WriteStr(AH, te->defn);
		WriteStr(AH, te->dropStmt);
		WriteStr(AH, te->copyStmt);
		WriteStr(AH, te->owner);
1766 1767 1768 1769 1770

		/* Dump list of dependencies */
		if (te->depOid != NULL)
		{
			i = 0;
1771
			while ((dep = (*te->depOid)[i++]) != NULL)
1772 1773
				WriteStr(AH, dep);
		}
1774
		WriteStr(AH, NULL);		/* Terminate List */
1775

B
Bruce Momjian 已提交
1776 1777
		if (AH->WriteExtraTocPtr)
			(*AH->WriteExtraTocPtr) (AH, te);
1778
		te = te->next;
B
Bruce Momjian 已提交
1779
	}
B
Bruce Momjian 已提交
1780 1781
}

B
Bruce Momjian 已提交
1782 1783
void
ReadToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1784
{
B
Bruce Momjian 已提交
1785
	int			i;
1786
	char	   *((*deps)[]);
1787 1788
	int			depIdx;
	int			depSize;
B
Bruce Momjian 已提交
1789

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

B
Bruce Momjian 已提交
1792
	AH->tocCount = ReadInt(AH);
B
Bruce Momjian 已提交
1793

B
Bruce Momjian 已提交
1794 1795
	for (i = 0; i < AH->tocCount; i++)
	{
B
Bruce Momjian 已提交
1796

B
Bruce Momjian 已提交
1797
		te = (TocEntry *) calloc(1, sizeof(TocEntry));
1798 1799 1800 1801
		te->id = ReadInt(AH);

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

		te->hadDumper = ReadInt(AH);
		te->oid = ReadStr(AH);
1806 1807
		te->oidVal = atooid(te->oid);

1808 1809 1810 1811 1812 1813 1814 1815 1816 1817
		te->name = ReadStr(AH);
		te->desc = ReadStr(AH);
		te->defn = ReadStr(AH);
		te->dropStmt = ReadStr(AH);

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

		te->owner = ReadStr(AH);

1818 1819 1820 1821
		/* Read TOC entry dependencies */
		if (AH->version >= K_VERS_1_5)
		{
			depSize = 100;
1822
			deps = malloc(sizeof(char *) * depSize);
1823 1824 1825 1826 1827 1828
			depIdx = 0;
			do
			{
				if (depIdx > depSize)
				{
					depSize *= 2;
1829
					deps = realloc(deps, sizeof(char *) * depSize);
1830 1831
				}
				(*deps)[depIdx] = ReadStr(AH);
1832 1833 1834 1835 1836
#if 0
				if ((*deps)[depIdx])
					write_msg(modulename, "read dependency for %s -> %s\n",
							  te->name, (*deps)[depIdx]);
#endif
1837
			} while ((*deps)[depIdx++] != NULL);
1838

1839 1840
			if (depIdx > 1)		/* We have a non-null entry */
				te->depOid = realloc(deps, sizeof(char *) * depIdx);	/* trim it */
1841
			else
1842
				te->depOid = NULL;		/* no deps */
1843
		}
1844 1845
		else
			te->depOid = NULL;
1846 1847 1848 1849

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

B
Bruce Momjian 已提交
1850 1851
		if (AH->ReadExtraTocPtr)
			(*AH->ReadExtraTocPtr) (AH, te);
1852

1853
		ahlog(AH, 3, "read TOC entry %d (id %d) for %s %s\n", i, te->id, te->desc, te->name);
1854 1855 1856 1857 1858

		te->prev = AH->toc->prev;
		AH->toc->prev->next = te;
		AH->toc->prev = te;
		te->next = AH->toc;
B
Bruce Momjian 已提交
1859
	}
B
Bruce Momjian 已提交
1860 1861
}

1862
static teReqs
B
Bruce Momjian 已提交
1863
_tocEntryRequired(TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1864
{
1865
	teReqs		res = 3;		/* Schema = 1, Data = 2, Both = 3 */
B
Bruce Momjian 已提交
1866 1867 1868

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

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

B
Bruce Momjian 已提交
1874 1875 1876 1877
	/* Check if tablename only is wanted */
	if (ropt->selTypes)
	{
		if ((strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0))
1878 1879 1880 1881 1882
		{
			if (!ropt->selTable)
				return 0;
			if (ropt->tableNames && strcmp(ropt->tableNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1883 1884 1885
		}
		else if (strcmp(te->desc, "INDEX") == 0)
		{
1886 1887 1888 1889
			if (!ropt->selIndex)
				return 0;
			if (ropt->indexNames && strcmp(ropt->indexNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1890 1891 1892
		}
		else if (strcmp(te->desc, "FUNCTION") == 0)
		{
1893 1894 1895 1896
			if (!ropt->selFunction)
				return 0;
			if (ropt->functionNames && strcmp(ropt->functionNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1897 1898 1899
		}
		else if (strcmp(te->desc, "TRIGGER") == 0)
		{
1900 1901 1902 1903 1904
			if (!ropt->selTrigger)
				return 0;
			if (ropt->triggerNames && strcmp(ropt->triggerNames, te->name) != 0)
				return 0;
		}
B
Bruce Momjian 已提交
1905 1906
		else
			return 0;
B
Bruce Momjian 已提交
1907 1908
	}

1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921
	/*
	 * 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
1922 1923
			res = res & ~REQ_DATA;
	}
1924

1925 1926 1927 1928 1929
	/*
	 * 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))
1930 1931
		res = REQ_DATA;

B
Bruce Momjian 已提交
1932 1933
	/* Mask it if we only want schema */
	if (ropt->schemaOnly)
1934
		res = res & REQ_SCHEMA;
B
Bruce Momjian 已提交
1935

B
Bruce Momjian 已提交
1936
	/* Mask it we only want data */
1937
	if (ropt->dataOnly)
1938
		res = res & REQ_DATA;
B
Bruce Momjian 已提交
1939

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

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

B
Bruce Momjian 已提交
1948
	return res;
B
Bruce Momjian 已提交
1949 1950
}

1951 1952 1953 1954 1955 1956 1957 1958

/*
 * 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
1959
 * actuall establish a connection.	Otherwise it puts a \connect into
1960 1961
 * the script output.
 */
B
Bruce Momjian 已提交
1962
static void
1963
_reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user)
1964
{
1965 1966 1967
	if (!user || strlen(user) == 0
		|| (strcmp(AH->currUser, user) == 0 && !dbname))
		return;					/* no need to do anything */
1968

1969 1970 1971 1972
	/*
	 * Use SET SESSION AUTHORIZATION if allowed and no database change
	 * needed
	 */
1973
	if (!dbname && AH->ropt->use_setsessauth)
1974
	{
1975
		if (RestoringToDB(AH))
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985
		{
			PQExpBuffer qry = createPQExpBuffer();
			PGresult   *res;

			appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION '%s';", user);
			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",
							 user, PQerrorMessage(AH->connection));
1986

1987 1988 1989 1990 1991
			PQclear(res);
			destroyPQExpBuffer(qry);
		}
		else
			ahprintf(AH, "SET SESSION AUTHORIZATION '%s';\n\n", user);
B
Bruce Momjian 已提交
1992
	}
1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
	/* When -R was given, don't do anything. */
	else if (AH->ropt && AH->ropt->noReconnect)
		return;

	else if (RestoringToDB(AH))
		ReconnectToServer(AH, dbname, user);
	else
		/* FIXME: does not handle mixed case user names */
		ahprintf(AH, "\\connect %s %s\n\n",
				 dbname ? dbname : "-",
				 user ? user : "-");

2005 2006 2007 2008
	/*
	 * NOTE: currUser keeps track of what the imaginary session user in
	 * our script is
	 */
2009 2010 2011 2012
	if (AH->currUser)
		free(AH->currUser);

	AH->currUser = strdup(user);
2013 2014
}

2015 2016 2017 2018 2019 2020

/*
 * 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 已提交
2021 2022
static void
_reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te)
2023 2024 2025 2026 2027
{
	if (AH->ropt && AH->ropt->noOwner)
		return;

	_reconnectAsUser(AH, dbname, te->owner);
2028 2029
}

2030

B
Bruce Momjian 已提交
2031
static int
2032
_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData)
B
Bruce Momjian 已提交
2033
{
2034
	char	   *pfx;
2035 2036 2037

	if (isData)
		pfx = "Data for ";
2038
	else
2039 2040 2041 2042
		pfx = "";

	ahprintf(AH, "--\n-- %sTOC Entry ID %d (OID %s)\n--\n-- Name: %s Type: %s Owner: %s\n",
			 pfx, te->id, te->oid, te->name, te->desc, te->owner);
B
Bruce Momjian 已提交
2043 2044 2045
	if (AH->PrintExtraTocPtr !=NULL)
		(*AH->PrintExtraTocPtr) (AH, te);
	ahprintf(AH, "--\n\n");
B
Bruce Momjian 已提交
2046

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

B
Bruce Momjian 已提交
2049
	return 1;
B
Bruce Momjian 已提交
2050 2051
}

B
Bruce Momjian 已提交
2052 2053
void
WriteHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
2054
{
B
Bruce Momjian 已提交
2055
	struct tm	crtm;
2056

B
Bruce Momjian 已提交
2057 2058 2059 2060 2061 2062
	(*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 已提交
2063

2064
#ifndef HAVE_LIBZ
B
Bruce Momjian 已提交
2065
	if (AH->compression != 0)
2066 2067
		write_msg(modulename, "WARNING: requested compression not available in this "
				  "installation - archive will be uncompressed\n");
B
Bruce Momjian 已提交
2068

B
Bruce Momjian 已提交
2069
	AH->compression = 0;
2070
#endif
B
Bruce Momjian 已提交
2071

2072 2073 2074 2075 2076 2077 2078 2079 2080 2081
	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 已提交
2082
	WriteStr(AH, AH->dbname);
B
Bruce Momjian 已提交
2083 2084
}

B
Bruce Momjian 已提交
2085 2086
void
ReadHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
2087
{
B
Bruce Momjian 已提交
2088 2089
	char		tmpMag[7];
	int			fmt;
2090
	struct tm	crtm;
B
Bruce Momjian 已提交
2091

2092
	/* If we haven't already read the header... */
B
Bruce Momjian 已提交
2093 2094
	if (!AH->readHeader)
	{
B
Bruce Momjian 已提交
2095

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

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

B
Bruce Momjian 已提交
2101 2102
		AH->vmaj = (*AH->ReadBytePtr) (AH);
		AH->vmin = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
2103

B
Bruce Momjian 已提交
2104 2105 2106
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
			AH->vrev = (*AH->ReadBytePtr) (AH);
		else
2107
			AH->vrev = 0;
B
Bruce Momjian 已提交
2108

B
Bruce Momjian 已提交
2109
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
B
Bruce Momjian 已提交
2110 2111


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

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

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

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

2125
		if (AH->format != fmt)
2126 2127
			die_horribly(AH, modulename, "expected format (%d) differs from format found in file (%d)\n",
						 AH->format, fmt);
B
Bruce Momjian 已提交
2128
	}
B
Bruce Momjian 已提交
2129

B
Bruce Momjian 已提交
2130 2131
	if (AH->version >= K_VERS_1_2)
	{
2132
		if (AH->version < K_VERS_1_4)
B
Bruce Momjian 已提交
2133
			AH->compression = (*AH->ReadBytePtr) (AH);
2134 2135
		else
			AH->compression = ReadInt(AH);
B
Bruce Momjian 已提交
2136 2137
	}
	else
2138
		AH->compression = Z_DEFAULT_COMPRESSION;
B
Bruce Momjian 已提交
2139

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

2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158
	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 已提交
2159
		if (AH->createDate == (time_t) -1)
2160
			write_msg(modulename, "WARNING: invalid creation date in header\n");
2161 2162
	}

B
Bruce Momjian 已提交
2163 2164 2165
}


B
Bruce Momjian 已提交
2166 2167
static void
_SortToc(ArchiveHandle *AH, TocSortCompareFn fn)
B
Bruce Momjian 已提交
2168
{
B
Bruce Momjian 已提交
2169 2170 2171
	TocEntry  **tea;
	TocEntry   *te;
	int			i;
B
Bruce Momjian 已提交
2172

B
Bruce Momjian 已提交
2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186
	/* 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 已提交
2187

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

B
Bruce Momjian 已提交
2191 2192 2193 2194 2195 2196
	/* 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 已提交
2197 2198


B
Bruce Momjian 已提交
2199 2200 2201 2202 2203 2204 2205 2206 2207
	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 已提交
2208 2209


B
Bruce Momjian 已提交
2210 2211
	AH->toc->next = tea[1];
	AH->toc->prev = tea[AH->tocCount];
B
Bruce Momjian 已提交
2212 2213
}

B
Bruce Momjian 已提交
2214 2215
static int
_tocSortCompareByOIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2216
{
B
Bruce Momjian 已提交
2217 2218
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
2219 2220 2221
	Oid			id1 = te1->maxOidVal;
	Oid			id2 = te2->maxOidVal;
	int			cmpval;
B
Bruce Momjian 已提交
2222

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

2225 2226 2227 2228
	cmpval = oidcmp(id1, id2);

	/* If we have a deterministic answer, return it. */
	if (cmpval != 0)
2229
		return cmpval;
2230 2231

	/* More comparisons required */
2232
	if (oideq(id1, te1->maxDepOidVal))	/* maxOid1 came from deps */
2233
	{
2234 2235
		if (oideq(id2, te2->maxDepOidVal))		/* maxOid2 also came from
												 * deps */
2236
		{
2237 2238
			cmpval = oidcmp(te1->oidVal, te2->oidVal);	/* Just compare base
														 * OIDs */
2239
		}
2240 2241
		else
/* MaxOid2 was entry OID */
2242
		{
2243
			return 1;			/* entry1 > entry2 */
2244
		};
2245 2246 2247
	}
	else
/* must have oideq(id1, te1->oidVal) => maxOid1 = Oid1 */
2248
	{
2249
		if (oideq(id2, te2->maxDepOidVal))		/* maxOid2 came from deps */
2250
		{
2251
			return -1;			/* entry1 < entry2 */
2252
		}
2253 2254
		else
/* MaxOid2 was entry OID - deps don't matter */
2255 2256 2257 2258 2259
		{
			cmpval = 0;
		};
	};

2260 2261 2262
	/*
	 * If we get here, then we've done another comparison Once again, a 0
	 * result means we require even more
2263 2264 2265 2266
	 */
	if (cmpval != 0)
		return cmpval;

2267 2268 2269 2270
	/*
	 * Entire OID details match, so use ID number (ie. original pg_dump
	 * order)
	 */
2271
	return _tocSortCompareByIDNum(te1, te2);
B
Bruce Momjian 已提交
2272 2273
}

B
Bruce Momjian 已提交
2274 2275
static int
_tocSortCompareByIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2276
{
B
Bruce Momjian 已提交
2277 2278 2279 2280
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
	int			id1 = te1->id;
	int			id2 = te2->id;
B
Bruce Momjian 已提交
2281

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

B
Bruce Momjian 已提交
2284 2285 2286 2287 2288 2289
	if (id1 < id2)
		return -1;
	else if (id1 > id2)
		return 1;
	else
		return 0;
B
Bruce Momjian 已提交
2290 2291
}

2292 2293 2294 2295
/*
 * Assuming Oid and depOid are set, work out the various
 * Oid values used in sorting.
 */
2296
static void
2297 2298 2299 2300 2301 2302
_fixupOidInfo(TocEntry *te)
{
	te->oidVal = atooid(te->oid);
	te->maxDepOidVal = _findMaxOID(te->depOid);

	/* For the purpose of sorting, find the max OID. */
2303
	if (oidcmp(te->oidVal, te->maxDepOidVal) >= 0)
2304 2305 2306 2307 2308
		te->maxOidVal = te->oidVal;
	else
		te->maxOidVal = te->maxDepOidVal;
}

2309 2310
/*
 * Find the max OID value for a given list of string Oid values
2311 2312 2313 2314 2315 2316
 */
static Oid
_findMaxOID(const char *((*deps)[]))
{
	const char *dep;
	int			i;
2317
	Oid			maxOid = (Oid) 0;
2318 2319 2320 2321 2322 2323
	Oid			currOid;

	if (!deps)
		return maxOid;

	i = 0;
2324
	while ((dep = (*deps)[i++]) != NULL)
2325 2326 2327 2328 2329 2330 2331 2332 2333
	{
		currOid = atooid(dep);
		if (oidcmp(maxOid, currOid) < 0)
			maxOid = currOid;
	}

	return maxOid;
}

2334 2335 2336 2337 2338 2339 2340 2341
/*
 * 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;
 */