pg_backup_archiver.c 48.7 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.30 2001/08/12 19:02:39 petere 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 45 46 47 48 49 50 51 52 53
 * Modifications - 31-Mar-2001 - pjw@rhyme.com.au
 * 
 *	  - 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.
 *
 *	  - Treat OIDs with more respect (avoid using ints, use macros for 
 *		conversion & comparison).
 *
54 55 56 57 58 59
 * 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
 * 		routine (eg. SEQUENCE SET)
 *
B
Bruce Momjian 已提交
60 61 62 63 64
 *-------------------------------------------------------------------------
 */

#include "pg_backup.h"
#include "pg_backup_archiver.h"
65 66
#include "pg_backup_db.h"

67
#include <errno.h>
B
Bruce Momjian 已提交
68
#include <unistd.h>				/* for dup */
B
Bruce Momjian 已提交
69

70 71 72
#include "pqexpbuffer.h"
#include "libpq/libpq-fs.h"

B
Bruce Momjian 已提交
73 74 75 76 77
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);
78
static int	_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData);
79

B
Bruce Momjian 已提交
80 81
static void _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te);
static void _reconnectAsUser(ArchiveHandle *AH, const char *dbname, char *user);
82

B
Bruce Momjian 已提交
83 84 85 86 87 88 89
static int	_tocEntryRequired(TocEntry *te, RestoreOptions *ropt);
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);
90 91
static void _fixupOidInfo(TocEntry *te);
static Oid _findMaxOID(const char *((*deps)[]));
92

93
const char *progname;
94
static char *modulename = gettext_noop("archiver");
B
Bruce Momjian 已提交
95

96 97
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);
98

B
Bruce Momjian 已提交
99 100
static int	_canRestoreBlobs(ArchiveHandle *AH);
static int	_restoringToDB(ArchiveHandle *AH);
101

B
Bruce Momjian 已提交
102
/*
B
Bruce Momjian 已提交
103 104 105 106
 *	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 已提交
107 108 109 110 111 112
 *
 */


/* Create a new archive */
/* Public */
B
Bruce Momjian 已提交
113 114 115
Archive    *
CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
			  const int compression)
116

B
Bruce Momjian 已提交
117
{
B
Bruce Momjian 已提交
118 119 120
	ArchiveHandle *AH = _allocAH(FileSpec, fmt, compression, archModeWrite);

	return (Archive *) AH;
B
Bruce Momjian 已提交
121 122 123 124
}

/* Open an existing archive */
/* Public */
B
Bruce Momjian 已提交
125 126
Archive    *
OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
B
Bruce Momjian 已提交
127
{
B
Bruce Momjian 已提交
128 129 130
	ArchiveHandle *AH = _allocAH(FileSpec, fmt, 0, archModeRead);

	return (Archive *) AH;
B
Bruce Momjian 已提交
131 132 133
}

/* Public */
B
Bruce Momjian 已提交
134 135
void
CloseArchive(Archive *AHX)
B
Bruce Momjian 已提交
136
{
B
Bruce Momjian 已提交
137 138 139 140
	int			res = 0;
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

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

B
Bruce Momjian 已提交
142 143
	/* Close the output */
	if (AH->gzOut)
144
		res = GZCLOSE(AH->OF);
B
Bruce Momjian 已提交
145
	else if (AH->OF != stdout)
146 147 148
		res = fclose(AH->OF);

	if (res != 0)
149
		die_horribly(AH, modulename, "could not close the output file in CloseArchive\n");
B
Bruce Momjian 已提交
150 151 152
}

/* Public */
B
Bruce Momjian 已提交
153 154
void
RestoreArchive(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
155
{
B
Bruce Momjian 已提交
156 157 158 159 160
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	int			reqs;
	OutputContext sav;
	int			impliedDataOnly;
161
	bool		defnDumped;
B
Bruce Momjian 已提交
162

163 164
	AH->ropt = ropt;

165
	if (ropt->create && ropt->noReconnect)
166
		die_horribly(AH, modulename, "-C and -R are incompatible options\n");
167

168 169 170 171 172
	/*
	 * If we're using a DB connection, then connect it.
	 */
	if (ropt->useDB)
	{
173
		ahlog(AH, 1, "connecting to database for restore\n");
174
		if (AH->version < K_VERS_1_3)
175
			die_horribly(AH, modulename, "direct database connections are not supported in pre-1.3 archives\n");
176

177 178 179 180
		/* XXX Should get this from the archive */
		AHX->minRemoteVersion = 070100;
		AHX->maxRemoteVersion = 999999;

181
		ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport, ropt->username,
B
Bruce Momjian 已提交
182
						ropt->requirePassword, ropt->ignoreVersion);
183 184

		/*
B
Bruce Momjian 已提交
185 186
		 * If no superuser was specified then see if the current user will
		 * do...
187 188 189 190 191 192 193
		 */
		if (!ropt->superuser)
		{
			if (UserIsSuperuser(AH, ConnectedUser(AH)))
				ropt->superuser = strdup(ConnectedUser(AH));
		}

194 195
	}

196
	/*
B
Bruce Momjian 已提交
197 198 199 200
	 * 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.
201
	 *
B
Bruce Momjian 已提交
202
	 * We could scan for wanted TABLE entries, but that is not the same as
203
	 * dataOnly. At this stage, it seems unnecessary (6-Mar-2001).
B
Bruce Momjian 已提交
204 205 206
	 */
	if (!ropt->dataOnly)
	{
207 208
		te = AH->toc->next;
		impliedDataOnly = 1;
B
Bruce Momjian 已提交
209 210
		while (te != AH->toc)
		{
211
			reqs = _tocEntryRequired(te, ropt);
B
Bruce Momjian 已提交
212 213
			if ((reqs & 1) != 0)
			{					/* It's schema, and it's wanted */
214 215 216 217 218 219 220 221
				impliedDataOnly = 0;
				break;
			}
			te = te->next;
		}
		if (impliedDataOnly)
		{
			ropt->dataOnly = impliedDataOnly;
222
			ahlog(AH, 1, "implied data-only restore\n");
223
		}
B
Bruce Momjian 已提交
224
	}
225

226
	if (!ropt->superuser)
227 228 229 230
		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"
				  "  restoring into a database with already existing triggers.\n");
231

232
	/*
B
Bruce Momjian 已提交
233
	 * Setup the output file if necessary.
234
	 */
B
Bruce Momjian 已提交
235
	if (ropt->filename || ropt->compression)
236
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
237

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

B
Bruce Momjian 已提交
240 241
	/*
	 * Drop the items at the start, in reverse order
242
	 */
B
Bruce Momjian 已提交
243 244
	if (ropt->dropSchema)
	{
245
		te = AH->toc->prev;
B
Bruce Momjian 已提交
246 247
		while (te != AH->toc)
		{
248
			reqs = _tocEntryRequired(te, ropt);
B
Bruce Momjian 已提交
249
			if (((reqs & 1) != 0) && te->dropStmt)
250 251 252
			{
				/* We want the schema */
				ahlog(AH, 1, "dropping %s %s\n", te->desc, te->name);
253 254 255
				/* Reconnect if necessary */
				_reconnectAsOwner(AH, "-", te);
				/* Drop it */
256 257 258 259
				ahprintf(AH, "%s", te->dropStmt);
			}
			te = te->prev;
		}
B
Bruce Momjian 已提交
260
	}
B
Bruce Momjian 已提交
261

262 263 264
	/*
	 * Now process each TOC entry
	 */
B
Bruce Momjian 已提交
265 266 267
	te = AH->toc->next;
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
268

269 270 271
		/* Work out what, if anything, we want from this entry */
		reqs = _tocEntryRequired(te, ropt);

272 273 274 275
		/* Dump any relevant dump warnings to stderr */
		if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
		{
			if (!ropt->dataOnly && te->defn != NULL && strlen(te->defn) != 0) 
276 277 278
				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);
279 280
	 	}

281 282
		defnDumped = false;

B
Bruce Momjian 已提交
283
		if ((reqs & 1) != 0)	/* We want the schema */
284
		{
285 286 287
			/* Reconnect if necessary */
			_reconnectAsOwner(AH, "-", te);

288
			ahlog(AH, 1, "creating %s %s\n", te->desc, te->name);
289
			_printTocEntry(AH, te, ropt, false);
290
			defnDumped = true;
291 292

			/* If we created a DB, connect to it... */
B
Bruce Momjian 已提交
293
			if (strcmp(te->desc, "DATABASE") == 0)
294
			{
295
				ahlog(AH, 1, "connecting to new database %s as user %s\n", te->name, te->owner);
296 297
				_reconnectAsUser(AH, te->name, te->owner);
			}
298
		}	
B
Bruce Momjian 已提交
299

B
Bruce Momjian 已提交
300
		/*
301
		 * If we have a data component, then process it
302
		 */
303
		if ( (reqs & 2) != 0 )
B
Bruce Momjian 已提交
304
		{
305 306 307
			/* 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.
308
			 */
309
			if (te->hadDumper) 
310 311
			{
				/*
312
				 * If we can output the data, then restore it.
313
				 */
314 315 316 317
				if (AH->PrintTocDataPtr !=NULL && (reqs & 2) != 0)
				{
#ifndef HAVE_LIBZ
					if (AH->compression != 0)
318
						die_horribly(AH, modulename, "unable to restore from compressed archive (not configured for compression support)\n");
319
#endif
320

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
					_printTocEntry(AH, te, ropt, true);

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

						/*
						 * This is a bit nasty - we assume, for the moment, that
						 * if a custom output is used, then we don't want
						 * warnings.
						 */
						if (!AH->CustomOutPtr)
336
							write_msg(modulename, "WARNING: skipping BLOB restoration\n");
337 338 339 340 341 342 343 344 345 346 347 348 349

					}
					else
					{

						_disableTriggersIfNecessary(AH, te, ropt);

						/*
						 * Reconnect if necessary (_disableTriggers may have
						 * reconnected)
						 */
						_reconnectAsOwner(AH, "-", te);

350
						ahlog(AH, 1, "restoring data for table %s\n", te->name);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

						/*
						 * 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.
						 *
						 * For V1.3+, the table data MUST have a copy statement so
						 * that we can go into appropriate mode with libpq.
						 */
						if (te->copyStmt && strlen(te->copyStmt) > 0)
							ahprintf(AH, te->copyStmt);

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

						_enableTriggersIfNecessary(AH, te, ropt);
					}
				}
			} else if (!defnDumped) {
				/* If we haven't already dumped the defn part, do so now */ 
371
				ahlog(AH, 1, "executing %s %s\n", te->desc, te->name);
372
				_printTocEntry(AH, te, ropt, false);
373 374 375
			}
		}
		te = te->next;
B
Bruce Momjian 已提交
376
	}
B
Bruce Momjian 已提交
377

378
	/*
B
Bruce Momjian 已提交
379 380
	 * Now use blobs_xref (if used) to fixup any refs for tables that we
	 * loaded
381 382 383
	 */
	if (_canRestoreBlobs(AH) && AH->createdBlobXref)
	{
384
		/* NULL parameter means disable ALL user triggers */
385
		_disableTriggersIfNecessary(AH, NULL, ropt);
386

387
		te = AH->toc->next;
B
Bruce Momjian 已提交
388 389
		while (te != AH->toc)
		{
390 391

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

395
				ahlog(AH, 2, "checking whether we loaded %s\n", te->name);
396 397 398

				reqs = _tocEntryRequired(te, ropt);

B
Bruce Momjian 已提交
399
				if ((reqs & 2) != 0)	/* We loaded the data */
400
				{
401
					ahlog(AH, 1, "fixing up BLOB reference for %s\n", te->name);
402 403 404 405
					FixupBlobRefs(AH, te->name);
				}
			}
			else
406
				ahlog(AH, 2, "ignoring BLOB cross-references for %s %s\n", te->desc, te->name);
407 408 409

			te = te->next;
		}
410 411

		/* NULL parameter means enable ALL user triggers */
412
		_enableTriggersIfNecessary(AH, NULL, ropt);
413 414 415 416 417
	}

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

421 422 423 424
	if (ropt->useDB)
	{
		PQfinish(AH->connection);
		AH->connection = NULL;
425 426 427 428 429 430

		if (AH->blobConnection)
		{
			PQfinish(AH->blobConnection);
			AH->blobConnection = NULL;
		}
431
	}
B
Bruce Momjian 已提交
432 433
}

434 435 436 437
/*
 * Allocate a new RestoreOptions block.
 * This is mainly so we can initialize it, but also for future expansion,
 */
B
Bruce Momjian 已提交
438 439
RestoreOptions *
NewRestoreOptions(void)
B
Bruce Momjian 已提交
440
{
B
Bruce Momjian 已提交
441
	RestoreOptions *opts;
B
Bruce Momjian 已提交
442

B
Bruce Momjian 已提交
443
	opts = (RestoreOptions *) calloc(1, sizeof(RestoreOptions));
B
Bruce Momjian 已提交
444 445

	opts->format = archUnknown;
446
	opts->suppressDumpWarnings = false;
B
Bruce Momjian 已提交
447 448 449 450

	return opts;
}

B
Bruce Momjian 已提交
451 452
static int
_restoringToDB(ArchiveHandle *AH)
453 454 455 456
{
	return (AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
457 458
static int
_canRestoreBlobs(ArchiveHandle *AH)
459 460 461 462
{
	return _restoringToDB(AH);
}

B
Bruce Momjian 已提交
463 464
static void
_disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
465
{
B
Bruce Momjian 已提交
466
	char	   *oldUser = NULL;
467 468

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

	/*
B
Bruce Momjian 已提交
474
	 * Reconnect as superuser if possible, since they are the only ones
475 476 477 478
	 * who can update pg_class...
	 */
	if (ropt->superuser)
	{
479 480
		if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH))
		{
B
Bruce Momjian 已提交
481 482 483 484 485

			/*
			 * 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.
486 487 488 489 490
			 */
			if (ropt->noOwner)
				oldUser = strdup(ConnectedUser(AH));
			_reconnectAsUser(AH, "-", ropt->superuser);
		}
491 492
	}

493
	ahlog(AH, 1, "disabling triggers\n");
494 495

	/*
B
Bruce Momjian 已提交
496 497
	 * Disable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
498
	 */
B
Bruce Momjian 已提交
499
	ahprintf(AH, "-- Disable triggers\n");
500 501 502 503 504 505

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

	if (te && te->name && strlen(te->name) > 0)
B
Bruce Momjian 已提交
506 507
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" = '%s';\n\n",
				 te->name);
508 509
	else
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" !~ '^pg_';\n\n");
510 511

	/*
B
Bruce Momjian 已提交
512 513
	 * Restore the user connection from the start of this procedure if
	 * _reconnectAsOwner is disabled.
514 515 516 517 518 519
	 */
	if (ropt->noOwner && oldUser)
	{
		_reconnectAsUser(AH, "-", oldUser);
		free(oldUser);
	}
B
Bruce Momjian 已提交
520 521
}

B
Bruce Momjian 已提交
522 523
static void
_enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
524
{
B
Bruce Momjian 已提交
525
	char	   *oldUser = NULL;
526 527

	/* Can't do much if we're connected & don't have a superuser */
528
	/* Also, don't bother with triggers unless a data-only retore. */
B
Bruce Momjian 已提交
529
	if (!ropt->dataOnly || (_restoringToDB(AH) && !ropt->superuser))
530 531 532 533 534 535 536 537
		return;

	/*
	 * Reconnect as superuser if possible, since they are the only ones
	 * who can update pg_class...
	 */
	if (ropt->superuser)
	{
538 539
		if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH))
		{
B
Bruce Momjian 已提交
540 541 542 543 544

			/*
			 * 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
545 546 547
			 */
			if (ropt->noOwner)
				oldUser = strdup(ConnectedUser(AH));
548

549 550
			_reconnectAsUser(AH, "-", ropt->superuser);
		}
551 552
	}

553
	ahlog(AH, 1, "enabling triggers\n");
554 555

	/*
B
Bruce Momjian 已提交
556 557
	 * Enable them. This is a hack. Needs to be done via an appropriate
	 * 'SET' command when one is available.
558
	 */
B
Bruce Momjian 已提交
559
	ahprintf(AH, "-- Enable triggers\n");
560 561 562
	if (te && te->name && strlen(te->name) > 0)
	{
		ahprintf(AH, "UPDATE pg_class SET reltriggers = "
B
Bruce Momjian 已提交
563
		"(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) "
564
				 "WHERE relname = '%s';\n\n",
B
Bruce Momjian 已提交
565 566 567 568
				 te->name);
	}
	else
	{
569
		ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = "
B
Bruce Momjian 已提交
570
		"(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) "
571 572
				 "WHERE \"relname\" !~ '^pg_';\n\n");
	}
B
Bruce Momjian 已提交
573

574
	/*
B
Bruce Momjian 已提交
575 576
	 * Restore the user connection from the start of this procedure if
	 * _reconnectAsOwner is disabled.
577 578 579 580 581 582 583
	 */
	if (ropt->noOwner && oldUser)
	{
		_reconnectAsUser(AH, "-", oldUser);
		free(oldUser);
	}
}
B
Bruce Momjian 已提交
584 585

/*
586
 * This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
B
Bruce Momjian 已提交
587 588 589
 */

/* Public */
B
Bruce Momjian 已提交
590 591
int
WriteData(Archive *AHX, const void *data, int dLen)
B
Bruce Momjian 已提交
592
{
B
Bruce Momjian 已提交
593
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
B
Bruce Momjian 已提交
594

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

B
Bruce Momjian 已提交
598
	return (*AH->WriteDataPtr) (AH, data, dLen);
B
Bruce Momjian 已提交
599 600 601
}

/*
B
Bruce Momjian 已提交
602
 * Create a new TOC entry. The TOC was designed as a TOC, but is now the
B
Bruce Momjian 已提交
603 604 605 606
 * repository for all metadata. But the name has stuck.
 */

/* Public */
B
Bruce Momjian 已提交
607 608
void
ArchiveEntry(Archive *AHX, const char *oid, const char *name,
609
			 const char *desc, const char *((*deps)[]), const char *defn,
B
Bruce Momjian 已提交
610 611
		   const char *dropStmt, const char *copyStmt, const char *owner,
			 DataDumperPtr dumpFn, void *dumpArg)
B
Bruce Momjian 已提交
612
{
B
Bruce Momjian 已提交
613 614 615 616 617 618 619 620
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *newToc;

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

	newToc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!newToc)
621
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
622 623 624 625 626 627 628

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

	newToc->id = AH->lastID;
629

B
Bruce Momjian 已提交
630 631
	newToc->name = strdup(name);
	newToc->desc = strdup(desc);
632 633 634 635 636 637

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


B
Bruce Momjian 已提交
638 639
	newToc->defn = strdup(defn);
	newToc->dropStmt = strdup(dropStmt);
640
	newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL;
B
Bruce Momjian 已提交
641 642 643 644 645
	newToc->owner = strdup(owner);
	newToc->printed = 0;
	newToc->formatData = NULL;
	newToc->dataDumper = dumpFn,
		newToc->dataDumperArg = dumpArg;
B
Bruce Momjian 已提交
646

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

B
Bruce Momjian 已提交
649 650
	if (AH->ArchiveEntryPtr !=NULL)
		(*AH->ArchiveEntryPtr) (AH, newToc);
B
Bruce Momjian 已提交
651

B
Bruce Momjian 已提交
652 653 654 655
	/*
	 * printf("New toc owned by '%s', oid %d\n", newToc->owner,
	 * newToc->oidVal);
	 */
B
Bruce Momjian 已提交
656 657 658
}

/* Public */
B
Bruce Momjian 已提交
659 660
void
PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
661
{
B
Bruce Momjian 已提交
662 663 664 665
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	OutputContext sav;
	char	   *fmtName;
B
Bruce Momjian 已提交
666

B
Bruce Momjian 已提交
667 668
	if (ropt->filename)
		sav = SetOutput(AH, ropt->filename, ropt->compression);
B
Bruce Momjian 已提交
669

670 671
	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 已提交
672
			 AH->archdbname, AH->tocCount, AH->compression);
673

B
Bruce Momjian 已提交
674 675
	switch (AH->format)
	{
676 677 678 679 680 681 682 683 684 685 686 687
		case archFiles:
			fmtName = "FILES";
			break;
		case archCustom:
			fmtName = "CUSTOM";
			break;
		case archTar:
			fmtName = "TAR";
			break;
		default:
			fmtName = "UNKNOWN";
	}
688 689

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

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

B
Bruce Momjian 已提交
694 695 696 697
	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 已提交
698
		te = te->next;
B
Bruce Momjian 已提交
699
	}
B
Bruce Momjian 已提交
700

B
Bruce Momjian 已提交
701 702
	if (ropt->filename)
		ResetOutput(AH, sav);
B
Bruce Momjian 已提交
703 704
}

705 706 707 708 709
/***********
 * BLOB Archival
 ***********/

/* Called by a dumper to signal start of a BLOB */
B
Bruce Momjian 已提交
710
int
711
StartBlob(Archive *AHX, Oid oid)
712
{
B
Bruce Momjian 已提交
713
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
714

B
Bruce Momjian 已提交
715
	if (!AH->StartBlobPtr)
716
		die_horribly(AH, modulename, "BLOB output not supported in chosen format\n");
717

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

B
Bruce Momjian 已提交
720
	return 1;
721 722 723
}

/* Called by a dumper to signal end of a BLOB */
B
Bruce Momjian 已提交
724
int
725
EndBlob(Archive *AHX, Oid oid)
726
{
B
Bruce Momjian 已提交
727
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
728

B
Bruce Momjian 已提交
729 730
	if (AH->EndBlobPtr)
		(*AH->EndBlobPtr) (AH, AH->currToc, oid);
731

B
Bruce Momjian 已提交
732
	return 1;
733 734 735 736 737 738
}

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

739
/*
B
Bruce Momjian 已提交
740
 * Called by a format handler before any blobs are restored
741
 */
B
Bruce Momjian 已提交
742 743
void
StartRestoreBlobs(ArchiveHandle *AH)
744 745 746 747 748
{
	AH->blobCount = 0;
}

/*
B
Bruce Momjian 已提交
749
 * Called by a format handler after all blobs are restored
750
 */
B
Bruce Momjian 已提交
751 752
void
EndRestoreBlobs(ArchiveHandle *AH)
753 754 755
{
	if (AH->txActive)
	{
756
		ahlog(AH, 2, "committing BLOB transactions\n");
757 758 759 760 761 762
		CommitTransaction(AH);
	}

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

763
	ahlog(AH, 1, "restored %d BLOBs\n", AH->blobCount);
764 765 766
}


767 768 769
/*
 * Called by a format handler to initiate restoration of a blob
 */
B
Bruce Momjian 已提交
770
void
771
StartRestoreBlob(ArchiveHandle *AH, Oid oid)
772
{
773
	Oid			loOid;
774

775 776
	AH->blobCount++;

777 778 779
	if (!AH->createdBlobXref)
	{
		if (!AH->connection)
780
			die_horribly(AH, modulename, "cannot restore BLOBs without a database connection\n");
781 782 783 784 785

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

786 787 788 789 790
	/*
	 * Start long-running TXs if necessary
	 */
	if (!AH->txActive)
	{
791
		ahlog(AH, 2, "starting BLOB transactions\n");
792 793 794 795
		StartTransaction(AH);
	}
	if (!AH->blobTxActive)
		StartTransactionXref(AH);
796

797 798
	loOid = lo_creat(AH->connection, INV_READ | INV_WRITE);
	if (loOid == 0)
799
		die_horribly(AH, modulename, "could not create BLOB\n");
800

801
	ahlog(AH, 2, "restoring BLOB oid %u as %u\n", oid, loOid);
802 803 804 805 806

	InsertBlobXref(AH, oid, loOid);

	AH->loFd = lo_open(AH->connection, loOid, INV_WRITE);
	if (AH->loFd == -1)
807
		die_horribly(AH, modulename, "could not open BLOB\n");
808

B
Bruce Momjian 已提交
809
	AH->writingBlob = 1;
810 811
}

B
Bruce Momjian 已提交
812
void
813
EndRestoreBlob(ArchiveHandle *AH, Oid oid)
814
{
B
Bruce Momjian 已提交
815 816
	lo_close(AH->connection, AH->loFd);
	AH->writingBlob = 0;
817

818 819 820
	/*
	 * Commit every BLOB_BATCH_SIZE blobs...
	 */
B
Bruce Momjian 已提交
821
	if (((AH->blobCount / BLOB_BATCH_SIZE) * BLOB_BATCH_SIZE) == AH->blobCount)
822
	{
823
		ahlog(AH, 2, "committing BLOB transactions\n");
824 825 826
		CommitTransaction(AH);
		CommitTransactionXref(AH);
	}
827 828
}

B
Bruce Momjian 已提交
829 830 831 832 833 834 835
/***********
 * Sorting and Reordering
 ***********/

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

B
Bruce Momjian 已提交
837
/* Public */
B
Bruce Momjian 已提交
838 839
void
MoveToStart(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
840
{
B
Bruce Momjian 已提交
841 842 843
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;
B
Bruce Momjian 已提交
844

B
Bruce Momjian 已提交
845 846
	while (te != AH->toc)
	{
B
Bruce Momjian 已提交
847 848 849
		te->_moved = 0;
		te = te->next;
	}
B
Bruce Momjian 已提交
850 851 852 853 854 855 856

	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 已提交
857
		te = newTe;
B
Bruce Momjian 已提交
858
	}
B
Bruce Momjian 已提交
859 860 861 862 863 864 865
}


/*
 * Move TOC entries of the specified type to the end of the TOC.
 */
/* Public */
B
Bruce Momjian 已提交
866 867
void
MoveToEnd(Archive *AHX, char *oType)
B
Bruce Momjian 已提交
868
{
B
Bruce Momjian 已提交
869 870 871 872 873 874 875 876
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	TocEntry   *te = AH->toc->next;
	TocEntry   *newTe;

	while (te != AH->toc)
	{
		te->_moved = 0;
		te = te->next;
B
Bruce Momjian 已提交
877
	}
B
Bruce Momjian 已提交
878 879 880 881 882 883 884

	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 已提交
885
		te = newTe;
B
Bruce Momjian 已提交
886
	}
B
Bruce Momjian 已提交
887 888
}

B
Bruce Momjian 已提交
889
/*
B
Bruce Momjian 已提交
890 891 892
 * Sort TOC by OID
 */
/* Public */
B
Bruce Momjian 已提交
893 894
void
SortTocByOID(Archive *AHX)
B
Bruce Momjian 已提交
895
{
B
Bruce Momjian 已提交
896 897 898
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByOIDNum);
B
Bruce Momjian 已提交
899 900 901 902 903 904
}

/*
 * Sort TOC by ID
 */
/* Public */
B
Bruce Momjian 已提交
905 906
void
SortTocByID(Archive *AHX)
B
Bruce Momjian 已提交
907
{
B
Bruce Momjian 已提交
908 909 910
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	_SortToc(AH, _tocSortCompareByIDNum);
B
Bruce Momjian 已提交
911 912
}

B
Bruce Momjian 已提交
913 914
void
SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
B
Bruce Momjian 已提交
915
{
B
Bruce Momjian 已提交
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
	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 已提交
940

B
Bruce Momjian 已提交
941 942
	/* Set prev entry as head of list */
	tePrev = AH->toc;
B
Bruce Momjian 已提交
943

B
Bruce Momjian 已提交
944 945 946
	/* Setup the file */
	fh = fopen(ropt->tocFile, PG_BINARY_R);
	if (!fh)
947
		die_horribly(AH, modulename, "could not open TOC file\n");
B
Bruce Momjian 已提交
948

B
Bruce Momjian 已提交
949
	while (fgets(buf, 1024, fh) != NULL)
B
Bruce Momjian 已提交
950
	{
B
Bruce Momjian 已提交
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
		/* 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)
		{
968
			write_msg(modulename, "WARNING: line ignored: %s\n", buf);
B
Bruce Momjian 已提交
969 970
			continue;
		}
B
Bruce Momjian 已提交
971

B
Bruce Momjian 已提交
972 973 974
		/* Find TOC entry */
		te = _getTocEntry(AH, id);
		if (!te)
975
			die_horribly(AH, modulename, "could not find entry for id %d\n", id);
B
Bruce Momjian 已提交
976

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

B
Bruce Momjian 已提交
979 980 981
		_moveAfter(AH, tePrev, te);
		tePrev = te;
	}
B
Bruce Momjian 已提交
982

B
Bruce Momjian 已提交
983
	if (fclose(fh) != 0)
984
		die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
B
Bruce Momjian 已提交
985 986 987 988 989 990 991 992
}

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

/* Public */
B
Bruce Momjian 已提交
993 994 995 996
int
archputs(const char *s, Archive *AH)
{
	return WriteData(AH, s, strlen(s));
B
Bruce Momjian 已提交
997 998 999
}

/* Public */
B
Bruce Momjian 已提交
1000 1001 1002 1003
int
archputc(const char c, Archive *AH)
{
	return WriteData(AH, &c, 1);
B
Bruce Momjian 已提交
1004 1005 1006
}

/* Public */
B
Bruce Momjian 已提交
1007 1008
int
archprintf(Archive *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1009
{
B
Bruce Momjian 已提交
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
	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 已提交
1025
	{
B
Bruce Momjian 已提交
1026 1027
		if (p != NULL)
			free(p);
1028
		bSize *= 2;
B
Bruce Momjian 已提交
1029
		p = (char *) malloc(bSize);
1030
		if (p == NULL)
1031
			exit_horribly(AH, modulename, "out of memory\n");
1032 1033 1034
		va_start(ap, fmt);
		cnt = vsnprintf(p, bSize, fmt, ap);
		va_end(ap);
B
Bruce Momjian 已提交
1035 1036 1037 1038
	}
	WriteData(AH, p, cnt);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1039 1040 1041 1042 1043 1044 1045
}


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

B
Bruce Momjian 已提交
1046 1047
OutputContext
SetOutput(ArchiveHandle *AH, char *filename, int compression)
B
Bruce Momjian 已提交
1048
{
B
Bruce Momjian 已提交
1049 1050
	OutputContext sav;

1051
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1052 1053
	char		fmode[10];

B
Bruce Momjian 已提交
1054
#endif
B
Bruce Momjian 已提交
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
	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 */
1074
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1075 1076
	if (compression != 0)
	{
1077
		sprintf(fmode, "wb%d", compression);
B
Bruce Momjian 已提交
1078 1079 1080 1081
		if (fn)
		{
			AH->OF = gzdopen(dup(fn), fmode);	/* Don't use PG_BINARY_x
												 * since this is zlib */
1082
		}
B
Bruce Momjian 已提交
1083 1084
		else
			AH->OF = gzopen(filename, fmode);
1085
		AH->gzOut = 1;
B
Bruce Momjian 已提交
1086 1087 1088
	}
	else
	{							/* Use fopen */
B
Bruce Momjian 已提交
1089
#endif
B
Bruce Momjian 已提交
1090
		if (fn)
1091
			AH->OF = fdopen(dup(fn), PG_BINARY_W);
B
Bruce Momjian 已提交
1092
		else
1093 1094
			AH->OF = fopen(filename, PG_BINARY_W);
		AH->gzOut = 0;
1095
#ifdef HAVE_LIBZ
B
Bruce Momjian 已提交
1096
	}
B
Bruce Momjian 已提交
1097 1098
#endif

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

B
Bruce Momjian 已提交
1102
	return sav;
B
Bruce Momjian 已提交
1103 1104
}

B
Bruce Momjian 已提交
1105 1106
void
ResetOutput(ArchiveHandle *AH, OutputContext sav)
B
Bruce Momjian 已提交
1107
{
B
Bruce Momjian 已提交
1108
	int			res;
1109

B
Bruce Momjian 已提交
1110
	if (AH->gzOut)
1111
		res = GZCLOSE(AH->OF);
B
Bruce Momjian 已提交
1112
	else
1113 1114 1115
		res = fclose(AH->OF);

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

B
Bruce Momjian 已提交
1118 1119
	AH->gzOut = sav.gzOut;
	AH->OF = sav.OF;
B
Bruce Momjian 已提交
1120 1121 1122 1123 1124
}



/*
B
Bruce Momjian 已提交
1125
 *	Print formatted text to the output file (usually stdout).
B
Bruce Momjian 已提交
1126
 */
B
Bruce Momjian 已提交
1127 1128
int
ahprintf(ArchiveHandle *AH, const char *fmt,...)
B
Bruce Momjian 已提交
1129
{
B
Bruce Momjian 已提交
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
	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))
1145
	{
B
Bruce Momjian 已提交
1146 1147
		if (p != NULL)
			free(p);
1148
		bSize *= 2;
B
Bruce Momjian 已提交
1149
		p = (char *) malloc(bSize);
1150
		if (p == NULL)
1151
			die_horribly(AH, modulename, "out of memory\n");
1152
		va_start(ap, fmt);
1153
		cnt = vsnprintf(p, bSize, fmt, ap);
1154
		va_end(ap);
B
Bruce Momjian 已提交
1155 1156 1157 1158
	}
	ahwrite(p, 1, cnt, AH);
	free(p);
	return cnt;
B
Bruce Momjian 已提交
1159 1160
}

B
Bruce Momjian 已提交
1161 1162
void
ahlog(ArchiveHandle *AH, int level, const char *fmt,...)
1163 1164 1165 1166 1167 1168 1169
{
	va_list		ap;

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

	va_start(ap, fmt);
1170
	_write_msg(NULL, fmt, ap);
1171 1172 1173
	va_end(ap);
}

1174 1175 1176
/*
 * Single place for logic which says 'We are restoring to a direct DB connection'.
 */
B
Bruce Momjian 已提交
1177 1178
int
RestoringToDB(ArchiveHandle *AH)
1179 1180 1181 1182
{
	return (AH->ropt && AH->ropt->useDB && AH->connection);
}

B
Bruce Momjian 已提交
1183
/*
B
Bruce Momjian 已提交
1184 1185 1186
 *	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
1187
 *	wants to generate a script (see TAR output).
B
Bruce Momjian 已提交
1188
 */
B
Bruce Momjian 已提交
1189 1190
int
ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
B
Bruce Momjian 已提交
1191
{
B
Bruce Momjian 已提交
1192
	int			res;
1193

B
Bruce Momjian 已提交
1194
	if (AH->writingBlob)
1195
	{
B
Bruce Momjian 已提交
1196
		res = lo_write(AH->connection, AH->loFd, (void *) ptr, size * nmemb);
1197
		ahlog(AH, 5, "wrote %d bytes of BLOB data (result = %d)\n", size * nmemb, res);
1198
		if (res < size * nmemb)
1199 1200
			die_horribly(AH, modulename, "could not write to large object (result: %d, expected: %d)\n",
						 res, size * nmemb);
1201

1202 1203
		return res;
	}
B
Bruce Momjian 已提交
1204
	else if (AH->gzOut)
1205
	{
B
Bruce Momjian 已提交
1206
		res = GZWRITE((void *) ptr, size, nmemb, AH->OF);
1207
		if (res != (nmemb * size))
1208
			die_horribly(AH, modulename, "could not write to compressed archive\n");
1209 1210
		return res;
	}
B
Bruce Momjian 已提交
1211
	else if (AH->CustomOutPtr)
1212
	{
B
Bruce Momjian 已提交
1213 1214
		res = AH->CustomOutPtr (AH, ptr, size * nmemb);

1215
		if (res != (nmemb * size))
1216
			die_horribly(AH, modulename, "could not write to custom output routine\n");
1217 1218
		return res;
	}
1219 1220
	else
	{
B
Bruce Momjian 已提交
1221

1222
		/*
B
Bruce Momjian 已提交
1223 1224 1225
		 * If we're doing a restore, and it's direct to DB, and we're
		 * connected then send it to the DB.
		 */
1226
		if (RestoringToDB(AH))
B
Bruce Momjian 已提交
1227
			return ExecuteSqlCommandBuf(AH, (void *) ptr, size * nmemb);		/* Always 1, currently */
1228
		else
1229
		{
B
Bruce Momjian 已提交
1230
			res = fwrite((void *) ptr, size, nmemb, AH->OF);
1231
			if (res != nmemb)
1232
				die_horribly(AH, modulename, "could not write to output file (%d != %d)\n", res, nmemb);
1233 1234
			return res;
		}
1235
	}
B
Bruce Momjian 已提交
1236
}
1237 1238

/* Common exit code */
B
Bruce Momjian 已提交
1239
static void
1240
_write_msg(const char *modulename, const char *fmt, va_list ap)
1241
{
1242
	if (modulename)
1243
		fprintf(stderr, "%s: [%s] ", progname, gettext(modulename));
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
	else
		fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, gettext(fmt), ap);
}

void
write_msg(const char *modulename, const char *fmt, ...)
{
	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);
1264 1265
	if (AH->public.verbose)
		write_msg(NULL, "*** aborted because of error\n");
1266

B
Bruce Momjian 已提交
1267 1268 1269
	if (AH)
		if (AH->connection)
			PQfinish(AH->connection);
1270 1271
	if (AH->blobConnection)
		PQfinish(AH->blobConnection);
1272

B
Bruce Momjian 已提交
1273
	exit(1);
B
Bruce Momjian 已提交
1274 1275
}

1276
/* External use */
B
Bruce Momjian 已提交
1277
void
1278
exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
1279
{
B
Bruce Momjian 已提交
1280
	va_list		ap;
1281

B
Bruce Momjian 已提交
1282
	va_start(ap, fmt);
1283
	_die_horribly((ArchiveHandle *) AH, modulename, fmt, ap);
1284
}
B
Bruce Momjian 已提交
1285

1286
/* Archiver use (just different arg declaration) */
B
Bruce Momjian 已提交
1287
void
1288
die_horribly(ArchiveHandle *AH, const char *modulename, const char *fmt,...)
B
Bruce Momjian 已提交
1289
{
B
Bruce Momjian 已提交
1290
	va_list		ap;
B
Bruce Momjian 已提交
1291

B
Bruce Momjian 已提交
1292
	va_start(ap, fmt);
1293
	_die_horribly(AH, modulename, fmt, ap);
B
Bruce Momjian 已提交
1294 1295
}

1296

B
Bruce Momjian 已提交
1297 1298
static void
_moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1299
{
B
Bruce Momjian 已提交
1300 1301
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1302

B
Bruce Momjian 已提交
1303 1304
	te->prev = pos;
	te->next = pos->next;
B
Bruce Momjian 已提交
1305

B
Bruce Momjian 已提交
1306 1307
	pos->next->prev = te;
	pos->next = te;
B
Bruce Momjian 已提交
1308

B
Bruce Momjian 已提交
1309
	te->_moved = 1;
B
Bruce Momjian 已提交
1310 1311
}

B
Bruce Momjian 已提交
1312 1313
static void
_moveBefore(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
B
Bruce Momjian 已提交
1314
{
B
Bruce Momjian 已提交
1315 1316
	te->prev->next = te->next;
	te->next->prev = te->prev;
B
Bruce Momjian 已提交
1317

B
Bruce Momjian 已提交
1318 1319 1320 1321
	te->prev = pos->prev;
	te->next = pos;
	pos->prev->next = te;
	pos->prev = te;
B
Bruce Momjian 已提交
1322

B
Bruce Momjian 已提交
1323
	te->_moved = 1;
B
Bruce Momjian 已提交
1324 1325
}

B
Bruce Momjian 已提交
1326 1327
static TocEntry *
_getTocEntry(ArchiveHandle *AH, int id)
B
Bruce Momjian 已提交
1328
{
B
Bruce Momjian 已提交
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
	TocEntry   *te;

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

B
Bruce Momjian 已提交
1341 1342
int
TocIDRequired(ArchiveHandle *AH, int id, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1343
{
B
Bruce Momjian 已提交
1344
	TocEntry   *te = _getTocEntry(AH, id);
B
Bruce Momjian 已提交
1345

B
Bruce Momjian 已提交
1346 1347
	if (!te)
		return 0;
B
Bruce Momjian 已提交
1348

B
Bruce Momjian 已提交
1349
	return _tocEntryRequired(te, ropt);
B
Bruce Momjian 已提交
1350 1351
}

B
Bruce Momjian 已提交
1352 1353
int
WriteInt(ArchiveHandle *AH, int i)
B
Bruce Momjian 已提交
1354
{
B
Bruce Momjian 已提交
1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
	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);
1369
		i = -i;
B
Bruce Momjian 已提交
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
	}
	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 已提交
1381 1382
}

B
Bruce Momjian 已提交
1383 1384
int
ReadInt(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1385
{
B
Bruce Momjian 已提交
1386 1387 1388 1389 1390
	int			res = 0;
	int			bv,
				b;
	int			sign = 0;		/* Default positive */
	int			bitShift = 0;
B
Bruce Momjian 已提交
1391

B
Bruce Momjian 已提交
1392
	if (AH->version > K_VERS_1_0)
1393
		/* Read a sign byte */
B
Bruce Momjian 已提交
1394
		sign = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
1395

B
Bruce Momjian 已提交
1396 1397 1398
	for (b = 0; b < AH->intSize; b++)
	{
		bv = (*AH->ReadBytePtr) (AH) & 0xFF;
1399 1400 1401
		if (bv != 0)
			res = res + (bv << bitShift);
		bitShift += 8;
B
Bruce Momjian 已提交
1402
	}
B
Bruce Momjian 已提交
1403

B
Bruce Momjian 已提交
1404 1405
	if (sign)
		res = -res;
B
Bruce Momjian 已提交
1406

B
Bruce Momjian 已提交
1407
	return res;
B
Bruce Momjian 已提交
1408 1409
}

B
Bruce Momjian 已提交
1410
int
1411
WriteStr(ArchiveHandle *AH, const char *c)
B
Bruce Momjian 已提交
1412
{
B
Bruce Momjian 已提交
1413
	int			res;
1414 1415 1416 1417

	if (c)
	{
		res = WriteInt(AH, strlen(c));
B
Bruce Momjian 已提交
1418
		res += (*AH->WriteBufPtr) (AH, c, strlen(c));
1419 1420 1421 1422
	}
	else
		res = WriteInt(AH, -1);

B
Bruce Momjian 已提交
1423
	return res;
B
Bruce Momjian 已提交
1424 1425
}

B
Bruce Momjian 已提交
1426 1427
char *
ReadStr(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1428
{
B
Bruce Momjian 已提交
1429 1430
	char	   *buf;
	int			l;
B
Bruce Momjian 已提交
1431

B
Bruce Momjian 已提交
1432
	l = ReadInt(AH);
1433 1434 1435 1436
	if (l == -1)
		buf = NULL;
	else
	{
B
Bruce Momjian 已提交
1437
		buf = (char *) malloc(l + 1);
1438
		if (!buf)
1439
			die_horribly(AH, modulename, "out of memory\n");
1440

B
Bruce Momjian 已提交
1441
		(*AH->ReadBufPtr) (AH, (void *) buf, l);
1442 1443
		buf[l] = '\0';
	}
B
Bruce Momjian 已提交
1444

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

T
Tom Lane 已提交
1448
static int
B
Bruce Momjian 已提交
1449
_discoverArchiveFormat(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1450
{
B
Bruce Momjian 已提交
1451 1452 1453 1454
	FILE	   *fh;
	char		sig[6];			/* More than enough */
	int			cnt;
	int			wantClose = 0;
B
Bruce Momjian 已提交
1455

1456 1457 1458
#if 0
	 write_msg(modulename, "attempting to ascertain archive format\n");
#endif
1459 1460 1461 1462 1463 1464 1465 1466

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

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

B
Bruce Momjian 已提交
1468 1469
	if (AH->fSpec)
	{
1470 1471
		wantClose = 1;
		fh = fopen(AH->fSpec, PG_BINARY_R);
B
Bruce Momjian 已提交
1472 1473
	}
	else
1474
		fh = stdin;
B
Bruce Momjian 已提交
1475

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

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

B
Bruce Momjian 已提交
1481
	if (cnt != 5)
1482 1483 1484 1485 1486 1487
	{
		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 已提交
1488

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

B
Bruce Momjian 已提交
1493
	if (strncmp(sig, "PGDMP", 5) == 0)
1494 1495 1496 1497 1498 1499 1500 1501 1502
	{
		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 已提交
1503 1504
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
		{
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
			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 已提交
1518 1519 1520 1521 1522
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
	}
	else
	{

1523
		/*
B
Bruce Momjian 已提交
1524 1525
		 * *Maybe* we have a tar archive format file... So, read first 512
		 * byte header...
1526 1527 1528
		 */
		cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
		AH->lookaheadLen += cnt;
B
Bruce Momjian 已提交
1529

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

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

1536 1537
		AH->format = archTar;
	}
B
Bruce Momjian 已提交
1538

B
Bruce Momjian 已提交
1539 1540
	/* If we can't seek, then mark the header as read */
	if (fseek(fh, 0, SEEK_SET) != 0)
1541
	{
B
Bruce Momjian 已提交
1542

1543
		/*
B
Bruce Momjian 已提交
1544 1545
		 * NOTE: Formats that use the looahead buffer can unset this in
		 * their Init routine.
1546 1547 1548 1549
		 */
		AH->readHeader = 1;
	}
	else
B
Bruce Momjian 已提交
1550
		AH->lookaheadLen = 0;	/* Don't bother since we've reset the file */
1551

1552 1553 1554
#if 0
	write_msg(modulename, "read %d bytes into lookahead buffer\n", AH->lookaheadLen);
#endif
B
Bruce Momjian 已提交
1555

B
Bruce Momjian 已提交
1556 1557
	/* Close the file */
	if (wantClose)
1558
		if (fclose(fh) != 0)
1559 1560
			die_horribly(AH, modulename, "could not close the input file after reading header: %s\n",
						 strerror(errno));
B
Bruce Momjian 已提交
1561

B
Bruce Momjian 已提交
1562
	return AH->format;
B
Bruce Momjian 已提交
1563 1564 1565 1566 1567 1568
}


/*
 * Allocate an archive handle
 */
B
Bruce Momjian 已提交
1569 1570 1571
static ArchiveHandle *
_allocAH(const char *FileSpec, const ArchiveFormat fmt,
		 const int compression, ArchiveMode mode)
1572
{
B
Bruce Momjian 已提交
1573
	ArchiveHandle *AH;
B
Bruce Momjian 已提交
1574

1575 1576 1577
#if 0
	 write_msg(modulename, "allocating AH for %s, format %d\n", FileSpec, fmt);
#endif
1578

B
Bruce Momjian 已提交
1579 1580
	AH = (ArchiveHandle *) calloc(1, sizeof(ArchiveHandle));
	if (!AH)
1581
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1582

B
Bruce Momjian 已提交
1583 1584
	AH->vmaj = K_VERS_MAJOR;
	AH->vmin = K_VERS_MINOR;
1585
	AH->vrev = K_VERS_REV;
B
Bruce Momjian 已提交
1586

1587 1588
	AH->createDate = time(NULL);

B
Bruce Momjian 已提交
1589 1590 1591 1592
	AH->intSize = sizeof(int);
	AH->lastID = 0;
	if (FileSpec)
	{
1593
		AH->fSpec = strdup(FileSpec);
B
Bruce Momjian 已提交
1594

1595 1596 1597
		/*
		 * Not used; maybe later....
		 *
B
Bruce Momjian 已提交
1598 1599
		 * AH->workDir = strdup(FileSpec); for(i=strlen(FileSpec) ; i > 0 ;
		 * i--) if (AH->workDir[i-1] == '/')
1600
		 */
B
Bruce Momjian 已提交
1601 1602
	}
	else
1603
		AH->fSpec = NULL;
B
Bruce Momjian 已提交
1604

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

B
Bruce Momjian 已提交
1608 1609
	AH->toc = (TocEntry *) calloc(1, sizeof(TocEntry));
	if (!AH->toc)
1610
		die_horribly(AH, modulename, "out of memory\n");
B
Bruce Momjian 已提交
1611

B
Bruce Momjian 已提交
1612 1613 1614 1615 1616
	AH->toc->next = AH->toc;
	AH->toc->prev = AH->toc;

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

1618 1619
	AH->pgCopyBuf = createPQExpBuffer();
	AH->sqlBuf = createPQExpBuffer();
B
Bruce Momjian 已提交
1620

B
Bruce Momjian 已提交
1621 1622 1623
	/* Open stdout with no compression for AH output handle */
	AH->gzOut = 0;
	AH->OF = stdout;
B
Bruce Momjian 已提交
1624

1625 1626 1627
#if 0
	 write_msg(modulename, "archive format is %d\n", fmt);
#endif
1628

B
Bruce Momjian 已提交
1629
	if (fmt == archUnknown)
1630 1631 1632
		AH->format = _discoverArchiveFormat(AH);
	else
		AH->format = fmt;
B
Bruce Momjian 已提交
1633

B
Bruce Momjian 已提交
1634 1635
	switch (AH->format)
	{
B
Bruce Momjian 已提交
1636

1637 1638 1639
		case archCustom:
			InitArchiveFmt_Custom(AH);
			break;
B
Bruce Momjian 已提交
1640

1641 1642 1643
		case archFiles:
			InitArchiveFmt_Files(AH);
			break;
B
Bruce Momjian 已提交
1644

1645 1646 1647
		case archNull:
			InitArchiveFmt_Null(AH);
			break;
B
Bruce Momjian 已提交
1648

1649 1650 1651 1652 1653
		case archTar:
			InitArchiveFmt_Tar(AH);
			break;

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

B
Bruce Momjian 已提交
1657
	return AH;
B
Bruce Momjian 已提交
1658 1659 1660
}


B
Bruce Momjian 已提交
1661 1662
void
WriteDataChunks(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1663
{
B
Bruce Momjian 已提交
1664 1665 1666
	TocEntry   *te = AH->toc->next;
	StartDataPtr startPtr;
	EndDataPtr	endPtr;
B
Bruce Momjian 已提交
1667

B
Bruce Momjian 已提交
1668 1669 1670
	while (te != AH->toc)
	{
		if (te->dataDumper != NULL)
1671
		{
B
Bruce Momjian 已提交
1672 1673
			AH->currToc = te;
			/* printf("Writing data for %d (%x)\n", te->id, te); */
1674

B
Bruce Momjian 已提交
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
			if (strcmp(te->desc, "BLOBS") == 0)
			{
				startPtr = AH->StartBlobsPtr;
				endPtr = AH->EndBlobsPtr;
			}
			else
			{
				startPtr = AH->StartDataPtr;
				endPtr = AH->EndDataPtr;
			}
B
Bruce Momjian 已提交
1685

B
Bruce Momjian 已提交
1686 1687
			if (startPtr != NULL)
				(*startPtr) (AH, te);
B
Bruce Momjian 已提交
1688

B
Bruce Momjian 已提交
1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
			/*
			 * 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;
		}
1704
		te = te->next;
B
Bruce Momjian 已提交
1705
	}
B
Bruce Momjian 已提交
1706 1707
}

B
Bruce Momjian 已提交
1708 1709
void
WriteToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1710
{
B
Bruce Momjian 已提交
1711
	TocEntry   *te = AH->toc->next;
1712 1713
	const char *dep;
	int			i;
B
Bruce Momjian 已提交
1714 1715 1716 1717 1718 1719 1720 1721 1722

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

B
Bruce Momjian 已提交
1724 1725 1726 1727 1728 1729
		WriteStr(AH, te->name);
		WriteStr(AH, te->desc);
		WriteStr(AH, te->defn);
		WriteStr(AH, te->dropStmt);
		WriteStr(AH, te->copyStmt);
		WriteStr(AH, te->owner);
1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741

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

B
Bruce Momjian 已提交
1742 1743
		if (AH->WriteExtraTocPtr)
			(*AH->WriteExtraTocPtr) (AH, te);
1744
		te = te->next;
B
Bruce Momjian 已提交
1745
	}
B
Bruce Momjian 已提交
1746 1747
}

B
Bruce Momjian 已提交
1748 1749
void
ReadToc(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1750
{
B
Bruce Momjian 已提交
1751
	int			i;
1752 1753 1754
	char		*((*deps)[]);
	int			depIdx;
	int			depSize;
B
Bruce Momjian 已提交
1755

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

B
Bruce Momjian 已提交
1758
	AH->tocCount = ReadInt(AH);
B
Bruce Momjian 已提交
1759

B
Bruce Momjian 已提交
1760 1761
	for (i = 0; i < AH->tocCount; i++)
	{
B
Bruce Momjian 已提交
1762

B
Bruce Momjian 已提交
1763
		te = (TocEntry *) calloc(1, sizeof(TocEntry));
1764 1765 1766 1767
		te->id = ReadInt(AH);

		/* Sanity check */
		if (te->id <= 0 || te->id > AH->tocCount)
1768
			die_horribly(AH, modulename, "failed sanity check (bad entry id) - perhaps a corrupt TOC\n");
1769 1770 1771

		te->hadDumper = ReadInt(AH);
		te->oid = ReadStr(AH);
1772 1773
		te->oidVal = atooid(te->oid);

1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
		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);

1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797
		/* Read TOC entry dependencies */
		if (AH->version >= K_VERS_1_5)
		{
			depSize = 100;
			deps = malloc(sizeof(char*) * depSize);
			depIdx = 0;
			do
			{
				if (depIdx > depSize)
				{
					depSize *= 2;
					deps = realloc(deps, sizeof(char*) * depSize);
				}
				(*deps)[depIdx] = ReadStr(AH);
1798 1799 1800 1801 1802
#if 0
				if ((*deps)[depIdx])
					write_msg(modulename, "read dependency for %s -> %s\n",
							  te->name, (*deps)[depIdx]);
#endif
1803 1804 1805
			} while ( (*deps)[depIdx++] != NULL);

			if (depIdx > 1) /* We have a non-null entry */
1806 1807 1808
				te->depOid = realloc(deps, sizeof(char*) * depIdx);	/* trim it */
			else
				te->depOid = NULL; /* no deps */
1809
		}
1810 1811
		else
			te->depOid = NULL;
1812 1813 1814 1815

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

B
Bruce Momjian 已提交
1816 1817
		if (AH->ReadExtraTocPtr)
			(*AH->ReadExtraTocPtr) (AH, te);
1818

1819
		ahlog(AH, 3, "read TOC entry %d (id %d) for %s %s\n", i, te->id, te->desc, te->name);
1820 1821 1822 1823 1824

		te->prev = AH->toc->prev;
		AH->toc->prev->next = te;
		AH->toc->prev = te;
		te->next = AH->toc;
B
Bruce Momjian 已提交
1825
	}
B
Bruce Momjian 已提交
1826 1827
}

B
Bruce Momjian 已提交
1828 1829
static int
_tocEntryRequired(TocEntry *te, RestoreOptions *ropt)
B
Bruce Momjian 已提交
1830
{
B
Bruce Momjian 已提交
1831 1832 1833 1834
	int			res = 3;		/* Schema = 1, Data = 2, Both = 3 */

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

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

B
Bruce Momjian 已提交
1840 1841 1842 1843
	/* Check if tablename only is wanted */
	if (ropt->selTypes)
	{
		if ((strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0))
1844 1845 1846 1847 1848
		{
			if (!ropt->selTable)
				return 0;
			if (ropt->tableNames && strcmp(ropt->tableNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1849 1850 1851
		}
		else if (strcmp(te->desc, "INDEX") == 0)
		{
1852 1853 1854 1855
			if (!ropt->selIndex)
				return 0;
			if (ropt->indexNames && strcmp(ropt->indexNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1856 1857 1858
		}
		else if (strcmp(te->desc, "FUNCTION") == 0)
		{
1859 1860 1861 1862
			if (!ropt->selFunction)
				return 0;
			if (ropt->functionNames && strcmp(ropt->functionNames, te->name) != 0)
				return 0;
B
Bruce Momjian 已提交
1863 1864 1865
		}
		else if (strcmp(te->desc, "TRIGGER") == 0)
		{
1866 1867 1868 1869 1870
			if (!ropt->selTrigger)
				return 0;
			if (ropt->triggerNames && strcmp(ropt->triggerNames, te->name) != 0)
				return 0;
		}
B
Bruce Momjian 已提交
1871 1872
		else
			return 0;
B
Bruce Momjian 已提交
1873 1874
	}

1875 1876 1877
	/* Special Case: If 'SEQUENCE SET' then it is considered a data entry */
	if (strcmp(te->desc, "SEQUENCE SET") == 0)
		res = res & 2;
1878

B
Bruce Momjian 已提交
1879 1880
	/* Mask it if we only want schema */
	if (ropt->schemaOnly)
1881
		res = res & 1;
B
Bruce Momjian 已提交
1882

B
Bruce Momjian 已提交
1883
	/* Mask it we only want data */
1884
	if (ropt->dataOnly)
B
Bruce Momjian 已提交
1885
		res = res & 2;
B
Bruce Momjian 已提交
1886

B
Bruce Momjian 已提交
1887 1888
	/* Mask it if we don't have a schema contribition */
	if (!te->defn || strlen(te->defn) == 0)
1889
		res = res & 2;
B
Bruce Momjian 已提交
1890

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

B
Bruce Momjian 已提交
1895
	return res;
B
Bruce Momjian 已提交
1896 1897
}

B
Bruce Momjian 已提交
1898 1899
static void
_reconnectAsUser(ArchiveHandle *AH, const char *dbname, char *user)
1900
{
1901 1902 1903 1904
	if (AH->ropt && AH->ropt->noReconnect)
		return;

	if (user && strlen(user) != 0
B
Bruce Momjian 已提交
1905
	&& ((strcmp(AH->currUser, user) != 0) || (strcmp(dbname, "-") != 0)))
1906
	{
1907
		if (RestoringToDB(AH))
1908
			ReconnectDatabase(AH, dbname, user);
1909
		else
1910
			ahprintf(AH, "\\connect %s %s\n", dbname, user);
B
Bruce Momjian 已提交
1911
		if (AH->currUser)
1912 1913 1914
			free(AH->currUser);

		AH->currUser = strdup(user);
B
Bruce Momjian 已提交
1915
	}
1916 1917
}

B
Bruce Momjian 已提交
1918 1919
static void
_reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te)
1920 1921 1922 1923 1924
{
	if (AH->ropt && AH->ropt->noOwner)
		return;

	_reconnectAsUser(AH, dbname, te->owner);
1925 1926
}

B
Bruce Momjian 已提交
1927
static int
1928
_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData)
B
Bruce Momjian 已提交
1929
{
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940
	char	*pfx;

	if (isData)
	{
		pfx = "Data for ";
	} else {
		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 已提交
1941 1942 1943
	if (AH->PrintExtraTocPtr !=NULL)
		(*AH->PrintExtraTocPtr) (AH, te);
	ahprintf(AH, "--\n\n");
B
Bruce Momjian 已提交
1944

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

B
Bruce Momjian 已提交
1947
	return 1;
B
Bruce Momjian 已提交
1948 1949
}

B
Bruce Momjian 已提交
1950 1951
void
WriteHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1952
{
B
Bruce Momjian 已提交
1953
	struct tm	crtm;
1954

B
Bruce Momjian 已提交
1955 1956 1957 1958 1959 1960
	(*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 已提交
1961

1962
#ifndef HAVE_LIBZ
B
Bruce Momjian 已提交
1963
	if (AH->compression != 0)
1964 1965
		write_msg(modulename, "WARNING: requested compression not available in this "
				  "installation - archive will be uncompressed\n");
B
Bruce Momjian 已提交
1966

B
Bruce Momjian 已提交
1967
	AH->compression = 0;
1968
#endif
B
Bruce Momjian 已提交
1969

1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
	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 已提交
1980
	WriteStr(AH, AH->dbname);
B
Bruce Momjian 已提交
1981 1982
}

B
Bruce Momjian 已提交
1983 1984
void
ReadHead(ArchiveHandle *AH)
B
Bruce Momjian 已提交
1985
{
B
Bruce Momjian 已提交
1986 1987
	char		tmpMag[7];
	int			fmt;
1988
	struct tm	crtm;
B
Bruce Momjian 已提交
1989

1990
	/* If we haven't already read the header... */
B
Bruce Momjian 已提交
1991 1992
	if (!AH->readHeader)
	{
B
Bruce Momjian 已提交
1993

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

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

B
Bruce Momjian 已提交
1999 2000
		AH->vmaj = (*AH->ReadBytePtr) (AH);
		AH->vmin = (*AH->ReadBytePtr) (AH);
B
Bruce Momjian 已提交
2001

B
Bruce Momjian 已提交
2002 2003 2004
		if (AH->vmaj > 1 || ((AH->vmaj == 1) && (AH->vmin > 0)))		/* Version > 1.0 */
			AH->vrev = (*AH->ReadBytePtr) (AH);
		else
2005
			AH->vrev = 0;
B
Bruce Momjian 已提交
2006

B
Bruce Momjian 已提交
2007
		AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
B
Bruce Momjian 已提交
2008 2009


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

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

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

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

2023
		if (AH->format != fmt)
2024 2025
			die_horribly(AH, modulename, "expected format (%d) differs from format found in file (%d)\n",
						 AH->format, fmt);
B
Bruce Momjian 已提交
2026
	}
B
Bruce Momjian 已提交
2027

B
Bruce Momjian 已提交
2028 2029
	if (AH->version >= K_VERS_1_2)
	{
2030
		if (AH->version < K_VERS_1_4)
B
Bruce Momjian 已提交
2031
			AH->compression = (*AH->ReadBytePtr) (AH);
2032 2033
		else
			AH->compression = ReadInt(AH);
B
Bruce Momjian 已提交
2034 2035
	}
	else
2036
		AH->compression = Z_DEFAULT_COMPRESSION;
B
Bruce Momjian 已提交
2037

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

2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056
	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 已提交
2057
		if (AH->createDate == (time_t) -1)
2058
			write_msg(modulename, "WARNING: invalid creation date in header\n");
2059 2060
	}

B
Bruce Momjian 已提交
2061 2062 2063
}


B
Bruce Momjian 已提交
2064 2065
static void
_SortToc(ArchiveHandle *AH, TocSortCompareFn fn)
B
Bruce Momjian 已提交
2066
{
B
Bruce Momjian 已提交
2067 2068 2069
	TocEntry  **tea;
	TocEntry   *te;
	int			i;
B
Bruce Momjian 已提交
2070

B
Bruce Momjian 已提交
2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085
	/* 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 已提交
2086

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

B
Bruce Momjian 已提交
2090 2091 2092 2093 2094 2095
	/* 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 已提交
2096 2097


B
Bruce Momjian 已提交
2098 2099 2100
	te = AH->toc;
	for (i = 0; i <= AH->tocCount + 1; i++)
	{
B
Bruce Momjian 已提交
2101

B
Bruce Momjian 已提交
2102 2103 2104 2105 2106 2107
		/*
		 * printf("%d: %x (%x, %x) - %d\n", i, te, te->prev, te->next,
		 * te->oidVal);
		 */
		te = te->next;
	}
B
Bruce Momjian 已提交
2108 2109


B
Bruce Momjian 已提交
2110 2111
	AH->toc->next = tea[1];
	AH->toc->prev = tea[AH->tocCount];
B
Bruce Momjian 已提交
2112 2113
}

B
Bruce Momjian 已提交
2114 2115
static int
_tocSortCompareByOIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2116
{
B
Bruce Momjian 已提交
2117 2118
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
2119 2120 2121
	Oid			id1 = te1->maxOidVal;
	Oid			id2 = te2->maxOidVal;
	int			cmpval;
B
Bruce Momjian 已提交
2122

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

2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162
	cmpval = oidcmp(id1, id2);

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

	/* More comparisons required */
	if ( oideq(id1, te1->maxDepOidVal) ) /* maxOid1 came from deps */
	{
		if ( oideq(id2, te2->maxDepOidVal) ) /* maxOid2 also came from deps */
		{
			cmpval = oidcmp(te1->oidVal, te2->oidVal); /* Just compare base OIDs */
		}
		else /* MaxOid2 was entry OID */
		{
			return 1; /* entry1 > entry2 */
		};
	} 
	else /* must have oideq(id1, te1->oidVal) => maxOid1 = Oid1 */
	{
		if ( oideq(id2, te2->maxDepOidVal) ) /* maxOid2 came from deps */
		{
			return -1; /* entry1 < entry2 */
		}
		else /* MaxOid2 was entry OID - deps don't matter */
		{
			cmpval = 0;
		};
	};

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

	/* Entire OID details match, so use ID number (ie. original pg_dump order) */
	return _tocSortCompareByIDNum(te1, te2);
B
Bruce Momjian 已提交
2163 2164
}

B
Bruce Momjian 已提交
2165 2166
static int
_tocSortCompareByIDNum(const void *p1, const void *p2)
B
Bruce Momjian 已提交
2167
{
B
Bruce Momjian 已提交
2168 2169 2170 2171
	TocEntry   *te1 = *(TocEntry **) p1;
	TocEntry   *te2 = *(TocEntry **) p2;
	int			id1 = te1->id;
	int			id2 = te2->id;
B
Bruce Momjian 已提交
2172

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

B
Bruce Momjian 已提交
2175 2176 2177 2178 2179 2180
	if (id1 < id2)
		return -1;
	else if (id1 > id2)
		return 1;
	else
		return 0;
B
Bruce Momjian 已提交
2181 2182
}

2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224
/*
 * Assuming Oid and depOid are set, work out the various
 * Oid values used in sorting.
 */
static void 
_fixupOidInfo(TocEntry *te)
{
	te->oidVal = atooid(te->oid);
	te->maxDepOidVal = _findMaxOID(te->depOid);

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

/* 
 * Find the max OID value for a given list of string Oid values 
 */
static Oid
_findMaxOID(const char *((*deps)[]))
{
	const char *dep;
	int			i;
	Oid			maxOid = (Oid)0;
	Oid			currOid;

	if (!deps)
		return maxOid;

	i = 0;
	while( (dep = (*deps)[i++]) != NULL)
	{
		currOid = atooid(dep);
		if (oidcmp(maxOid, currOid) < 0)
			maxOid = currOid;
	}

	return maxOid;
}

2225 2226 2227 2228 2229 2230 2231 2232
/*
 * 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;
 */