pg_dump.c 230 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * pg_dump.c
4
 *	  pg_dump is a utility for dumping out a postgres database
5
 *	  into a script file.
6
 *
7
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
8
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10 11 12
 *	pg_dump will read the system catalogs in a database and dump out a
 *	script that reproduces the schema in terms of SQL that is understood
 *	by PostgreSQL
13 14
 *
 * IDENTIFICATION
15
 *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.417 2005/08/15 02:36:29 tgl Exp $
16
 *
17
 *-------------------------------------------------------------------------
18 19
 */

20 21 22 23 24 25 26
/*
 * Although this is not a backend module, we must include postgres.h anyway
 * so that we can include a bunch of backend include files.  pg_dump has
 * never pretended to be very independent of the backend anyhow ...
 */
#include "postgres.h"

27
#include <unistd.h>
28
#include <ctype.h>
29 30 31
#ifdef ENABLE_NLS
#include <locale.h>
#endif
32 33 34 35
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

36 37 38 39
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

40
#include "getopt_long.h"
41

42
#ifndef HAVE_INT_OPTRESET
Bruce Momjian's avatar
Bruce Momjian committed
43
int			optreset;
44 45
#endif

46
#include "access/attnum.h"
47
#include "access/htup.h"
48
#include "catalog/pg_class.h"
49
#include "catalog/pg_proc.h"
50
#include "catalog/pg_trigger.h"
51
#include "catalog/pg_type.h"
52

53 54
#include "commands/sequence.h"

55
#include "libpq-fe.h"
56
#include "libpq/libpq-fs.h"
57 58

#include "pg_dump.h"
59
#include "pg_backup.h"
60
#include "pg_backup_archiver.h"
61
#include "dumputils.h"
62

Bruce Momjian's avatar
Bruce Momjian committed
63
extern char *optarg;
64
extern int	optind,
Bruce Momjian's avatar
Bruce Momjian committed
65
			opterr;
66

67

68 69 70 71 72 73 74 75 76
typedef struct
{
	const char *descr;			/* comment for an object */
	Oid			classoid;		/* object class (catalog OID) */
	Oid			objoid;			/* object OID */
	int			objsubid;		/* subobject (table column #) */
} CommentItem;


77
/* global decls */
78
bool		g_verbose;			/* User wants verbose narration of our
Bruce Momjian's avatar
Bruce Momjian committed
79
								 * activities. */
80
Archive    *g_fout;				/* the script file */
Bruce Momjian's avatar
Bruce Momjian committed
81 82
PGconn	   *g_conn;				/* the database connection */

83
/* various user-settable parameters */
84
bool		dumpInserts;		/* dump data using proper insert strings */
85 86 87
bool		attrNames;			/* put attr names into insert strings */
bool		schemaOnly;
bool		dataOnly;
88
bool		aclsSkip;
89

90 91 92
/* subquery used to convert user ID (eg, datdba) to user name */
static const char *username_subquery;

93 94 95
/* obsolete as of 7.3: */
static Oid	g_last_builtin_oid; /* value of the last builtin oid */

Bruce Momjian's avatar
Bruce Momjian committed
96 97
static char *selectTableName = NULL;	/* name of a single table to dump */
static char *selectSchemaName = NULL;	/* name of a single schema to dump */
98

Bruce Momjian's avatar
Bruce Momjian committed
99
char		g_opaque_type[10];	/* name for the opaque type */
100 101

/* placeholders for the delimiters for comments */
102 103
char		g_comment_start[10];
char		g_comment_end[10];
104

Bruce Momjian's avatar
Bruce Momjian committed
105
static const CatalogId nilCatalogId = {0, 0};
106

107 108 109
/* these are to avoid passing around info for findNamespace() */
static NamespaceInfo *g_namespaces;
static int	g_numNamespaces;
110

111
/* flag to turn on/off dollar quoting */
Bruce Momjian's avatar
Bruce Momjian committed
112
static int	disable_dollar_quoting = 0;
113

114

115 116 117 118 119 120
static void help(const char *progname);
static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
static void dumpComment(Archive *fout, const char *target,
			const char *namespace, const char *owner,
			CatalogId catalogId, int subid, DumpId dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
121 122
static int findComments(Archive *fout, Oid classoid, Oid objoid,
			 CommentItem **items);
123
static int	collectComments(Archive *fout, CommentItem **items);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
static void dumpDomain(Archive *fout, TypeInfo *tinfo);
static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
static void dumpProcLang(Archive *fout, ProcLangInfo *plang);
static void dumpFunc(Archive *fout, FuncInfo *finfo);
static void dumpCast(Archive *fout, CastInfo *cast);
static void dumpOpr(Archive *fout, OprInfo *oprinfo);
static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
static void dumpConversion(Archive *fout, ConvInfo *convinfo);
static void dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
static void dumpSequence(Archive *fout, TableInfo *tbinfo);
static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
145
static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
146 147 148 149 150 151 152 153 154

static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
		const char *type, const char *name,
		const char *tag, const char *nspname, const char *owner,
		const char *acls);

static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
155 156 157 158 159
static char *format_function_arguments(FuncInfo *finfo, int nallargs,
						  char **allargtypes,
						  char **argmodes,
						  char **argnames);
static char *format_function_signature(FuncInfo *finfo, bool honor_quotes);
160 161 162 163 164 165 166 167
static const char *convertRegProcReference(const char *proc);
static const char *convertOperatorReference(const char *opr);
static Oid	findLastBuiltinOid_V71(const char *);
static Oid	findLastBuiltinOid_V70(void);
static void selectSourceSchema(const char *schemaName);
static char *getFormattedTypeName(Oid oid, OidOptions opts);
static char *myFormatType(const char *typname, int32 typmod);
static const char *fmtQualifiedId(const char *schema, const char *id);
Tom Lane's avatar
Tom Lane committed
168
static bool hasBlobs(Archive *AH);
169
static int	dumpBlobs(Archive *AH, void *arg);
Tom Lane's avatar
Tom Lane committed
170
static int	dumpBlobComments(Archive *AH, void *arg);
171
static void dumpDatabase(Archive *AH);
172
static void dumpEncoding(Archive *AH);
173 174 175 176
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
static const char *fmtCopyColumnList(const TableInfo *ti);
static void do_sql_command(PGconn *conn, const char *query);
static void check_sql_result(PGresult *res, PGconn *conn, const char *query,
Bruce Momjian's avatar
Bruce Momjian committed
177
				 ExecStatusType expected);
178 179


180 181
int
main(int argc, char **argv)
182
{
183 184 185 186 187 188 189
	int			c;
	const char *filename = NULL;
	const char *format = "p";
	const char *dbname = NULL;
	const char *pghost = NULL;
	const char *pgport = NULL;
	const char *username = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
190
	const char *dumpencoding = NULL;
191
	bool		oids = false;
192
	TableInfo  *tblinfo;
193
	int			numTables;
194 195 196
	DumpableObject **dobjs;
	int			numObjs;
	int			i;
197 198 199 200 201 202
	bool		force_password = false;
	int			compressLevel = -1;
	bool		ignore_version = false;
	int			plainText = 0;
	int			outputClean = 0;
	int			outputCreate = 0;
203
	bool		outputBlobs = true;
204 205 206 207
	int			outputNoOwner = 0;
	static int	use_setsessauth = 0;
	static int	disable_triggers = 0;
	char	   *outputSuperuser = NULL;
208

209
	RestoreOptions *ropt;
Bruce Momjian's avatar
Hi,  
Bruce Momjian committed
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
	static struct option long_options[] = {
		{"data-only", no_argument, NULL, 'a'},
		{"blobs", no_argument, NULL, 'b'},
		{"clean", no_argument, NULL, 'c'},
		{"create", no_argument, NULL, 'C'},
		{"file", required_argument, NULL, 'f'},
		{"format", required_argument, NULL, 'F'},
		{"inserts", no_argument, NULL, 'd'},
		{"attribute-inserts", no_argument, NULL, 'D'},
		{"column-inserts", no_argument, NULL, 'D'},
		{"host", required_argument, NULL, 'h'},
		{"ignore-version", no_argument, NULL, 'i'},
		{"no-reconnect", no_argument, NULL, 'R'},
		{"oids", no_argument, NULL, 'o'},
		{"no-owner", no_argument, NULL, 'O'},
		{"port", required_argument, NULL, 'p'},
Bruce Momjian's avatar
Bruce Momjian committed
227
		{"schema", required_argument, NULL, 'n'},
228 229 230 231 232 233 234 235 236
		{"schema-only", no_argument, NULL, 's'},
		{"superuser", required_argument, NULL, 'S'},
		{"table", required_argument, NULL, 't'},
		{"password", no_argument, NULL, 'W'},
		{"username", required_argument, NULL, 'U'},
		{"verbose", no_argument, NULL, 'v'},
		{"no-privileges", no_argument, NULL, 'x'},
		{"no-acl", no_argument, NULL, 'x'},
		{"compress", required_argument, NULL, 'Z'},
Bruce Momjian's avatar
Bruce Momjian committed
237
		{"encoding", required_argument, NULL, 'E'},
238 239
		{"help", no_argument, NULL, '?'},
		{"version", no_argument, NULL, 'V'},
240

241 242 243 244
		/*
		 * the following options don't have an equivalent short option
		 * letter, but are available as '-X long-name'
		 */
245
		{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
246
		{"disable-triggers", no_argument, &disable_triggers, 1},
247
		{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
248

249 250 251
		{NULL, 0, NULL, 0}
	};
	int			optindex;
252

253
	set_pglocale_pgservice(argv[0], "pg_dump");
254

255
	g_verbose = false;
256

257 258 259
	strcpy(g_comment_start, "-- ");
	g_comment_end[0] = '\0';
	strcpy(g_opaque_type, "opaque");
260

261
	dataOnly = schemaOnly = dumpInserts = attrNames = false;
262

263
	progname = get_progname(argv[0]);
264

265 266 267 268 269
	/* Set default options based on progname */
	if (strcmp(progname, "pg_backup") == 0)
		format = "c";

	if (argc > 1)
270
	{
271
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
272
		{
273 274
			help(progname);
			exit(0);
275
		}
276
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
277
		{
278 279
			puts("pg_dump (PostgreSQL) " PG_VERSION);
			exit(0);
280 281
		}
	}
282

Bruce Momjian's avatar
Bruce Momjian committed
283
	while ((c = getopt_long(argc, argv, "abcCdDE:f:F:h:in:oOp:RsS:t:uU:vWxX:Z:",
284
							long_options, &optindex)) != -1)
285 286 287 288 289 290
	{
		switch (c)
		{
			case 'a':			/* Dump data only */
				dataOnly = true;
				break;
291

292
			case 'b':			/* Dump blobs */
293
				/* this is now default, so just ignore the switch */
294
				break;
295

296 297 298 299
			case 'c':			/* clean (i.e., drop) schema prior to
								 * create */
				outputClean = 1;
				break;
300

301 302 303
			case 'C':			/* Create DB */
				outputCreate = 1;
				break;
304

305
			case 'd':			/* dump data as proper insert strings */
306
				dumpInserts = true;
307
				break;
308

309 310
			case 'D':			/* dump data as proper insert strings with
								 * attr names */
311
				dumpInserts = true;
312 313
				attrNames = true;
				break;
314

Bruce Momjian's avatar
Bruce Momjian committed
315 316 317 318
			case 'E':			/* Dump encoding */
				dumpencoding = optarg;
				break;

319 320 321
			case 'f':
				filename = optarg;
				break;
322

323 324 325
			case 'F':
				format = optarg;
				break;
326

327 328 329
			case 'h':			/* server host */
				pghost = optarg;
				break;
330

331 332 333
			case 'i':			/* ignore database version mismatch */
				ignore_version = true;
				break;
334

Bruce Momjian's avatar
Bruce Momjian committed
335 336 337 338
			case 'n':			/* Dump data for this schema only */
				selectSchemaName = strdup(optarg);
				break;

339 340 341
			case 'o':			/* Dump oids */
				oids = true;
				break;
342

343 344 345
			case 'O':			/* Don't reconnect to match owner */
				outputNoOwner = 1;
				break;
346

347 348 349
			case 'p':			/* server port */
				pgport = optarg;
				break;
350

351 352
			case 'R':
				/* no-op, still accepted for backwards compatibility */
353
				break;
354

355 356
			case 's':			/* dump schema only */
				schemaOnly = true;
Tom Lane's avatar
Tom Lane committed
357
				outputBlobs = false;
358
				break;
359

360 361 362 363
			case 'S':			/* Username for superuser in plain text
								 * output */
				outputSuperuser = strdup(optarg);
				break;
364

365
			case 't':			/* Dump data for this table only */
Bruce Momjian's avatar
Bruce Momjian committed
366
				selectTableName = strdup(optarg);
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
				break;

			case 'u':
				force_password = true;
				username = simple_prompt("User name: ", 100, true);
				break;

			case 'U':
				username = optarg;
				break;

			case 'v':			/* verbose */
				g_verbose = true;
				break;

			case 'W':
				force_password = true;
				break;

			case 'x':			/* skip ACL dump */
				aclsSkip = true;
				break;

				/*
				 * Option letters were getting scarce, so I invented this
				 * new scheme: '-X feature' turns on some feature. Compare
				 * to the -f option in GCC.  You should also add an
				 * equivalent GNU-style option --feature.  Features that
				 * require arguments should use '-X feature=foo'.
				 */
			case 'X':
398 399
				if (strcmp(optarg, "disable-dollar-quoting") == 0)
					disable_dollar_quoting = 1;
400 401
				else if (strcmp(optarg, "disable-triggers") == 0)
					disable_triggers = 1;
402
				else if (strcmp(optarg, "use-set-session-authorization") == 0)
403
					use_setsessauth = 1;
404 405 406
				else
				{
					fprintf(stderr,
407
							_("%s: invalid -X option -- %s\n"),
408
							progname, optarg);
409
					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
410 411 412
					exit(1);
				}
				break;
413

414 415 416 417
			case 'Z':			/* Compression Level */
				compressLevel = atoi(optarg);
				break;
				/* This covers the long options equivalent to -X xxx. */
418

419 420
			case 0:
				break;
421

422
			default:
423
				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
424
				exit(1);
425
		}
426
	}
427

428 429
	if (optind < (argc - 1))
	{
430 431 432 433
		fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
				progname, argv[optind + 1]);
		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
				progname);
434 435
		exit(1);
	}
436

437
	/* Get database name from command line */
438 439
	if (optind < argc)
		dbname = argv[optind];
440

441 442
	if (dataOnly && schemaOnly)
	{
443
		write_msg(NULL, "options \"schema only\" (-s) and \"data only\" (-a) cannot be used together\n");
444 445
		exit(1);
	}
446

447 448
	if (dataOnly && outputClean)
	{
449
		write_msg(NULL, "options \"clean\" (-c) and \"data only\" (-a) cannot be used together\n");
450 451 452
		exit(1);
	}

453 454
	if (selectTableName != NULL || selectSchemaName != NULL)
		outputBlobs = false;
Bruce Momjian's avatar
Bruce Momjian committed
455

456
	if (dumpInserts == true && oids == true)
457
	{
458
		write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together\n");
459
		write_msg(NULL, "(The INSERT command cannot set OIDs.)\n");
460 461 462
		exit(1);
	}

463 464 465 466 467 468 469
	/* open the output file */
	switch (format[0])
	{
		case 'c':
		case 'C':
			g_fout = CreateArchive(filename, archCustom, compressLevel);
			break;
470

471 472 473 474
		case 'f':
		case 'F':
			g_fout = CreateArchive(filename, archFiles, compressLevel);
			break;
475

476 477 478 479 480
		case 'p':
		case 'P':
			plainText = 1;
			g_fout = CreateArchive(filename, archNull, 0);
			break;
481

482 483 484 485
		case 't':
		case 'T':
			g_fout = CreateArchive(filename, archTar, compressLevel);
			break;
486

487
		default:
488
			write_msg(NULL, "invalid output format \"%s\" specified\n", format);
489 490
			exit(1);
	}
491

492 493
	if (g_fout == NULL)
	{
494
		write_msg(NULL, "could not open output file \"%s\" for writing\n", filename);
495 496
		exit(1);
	}
Bruce Momjian's avatar
Hi,  
Bruce Momjian committed
497

498 499
	/* Let the archiver know how noisy to be */
	g_fout->verbose = g_verbose;
500

501 502 503 504
	g_fout->minRemoteVersion = 70000;	/* we can handle back to 7.0 */
	g_fout->maxRemoteVersion = parse_version(PG_VERSION);
	if (g_fout->maxRemoteVersion < 0)
	{
505
		write_msg(NULL, "could not parse version string \"%s\"\n", PG_VERSION);
506 507 508
		exit(1);
	}

509 510 511 512
	/*
	 * Open the database using the Archiver, so it knows about it. Errors
	 * mean death.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
513 514
	g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport,
							 username, force_password, ignore_version);
515

516
	/*
517
	 * Start serializable transaction to dump consistent data.
518
	 */
519
	do_sql_command(g_conn, "BEGIN");
520

521
	do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
522

Bruce Momjian's avatar
Bruce Momjian committed
523
	/* Set the datestyle to ISO to ensure the dump's portability */
524
	do_sql_command(g_conn, "SET DATESTYLE = ISO");
525

Bruce Momjian's avatar
Bruce Momjian committed
526 527 528 529 530 531 532 533 534
	/* Set the client encoding */
	if (dumpencoding)
	{
		char *cmd = malloc(strlen(dumpencoding) + 32);
		sprintf(cmd,"SET client_encoding='%s'", dumpencoding);
		do_sql_command(g_conn, cmd);
		free(cmd);
	}

535 536 537 538 539 540 541 542
	/* Select the appropriate subquery to convert user IDs to names */
	if (g_fout->remoteVersion >= 80100)
		username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =";
	else if (g_fout->remoteVersion >= 70300)
		username_subquery = "SELECT usename FROM pg_catalog.pg_user WHERE usesysid =";
	else
		username_subquery = "SELECT usename FROM pg_user WHERE usesysid =";

543 544 545 546 547
	/*
	 * If supported, set extra_float_digits so that we can dump float data
	 * exactly (given correctly implemented float I/O code, anyway)
	 */
	if (g_fout->remoteVersion >= 70400)
548
		do_sql_command(g_conn, "SET extra_float_digits TO 2");
549

550
	/* Find the last built-in OID, if needed */
551
	if (g_fout->remoteVersion < 70300)
552
	{
553
		if (g_fout->remoteVersion >= 70100)
554
			g_last_builtin_oid = findLastBuiltinOid_V71(PQdb(g_conn));
555 556 557
		else
			g_last_builtin_oid = findLastBuiltinOid_V70();
		if (g_verbose)
558
			write_msg(NULL, "last built-in OID is %u\n", g_last_builtin_oid);
559 560
	}

561 562 563 564 565 566 567 568 569
	/*
	 * Now scan the database and create DumpableObject structs for all the
	 * objects we intend to dump.
	 */
	tblinfo = getSchemaData(&numTables, schemaOnly, dataOnly);

	if (!schemaOnly)
		getTableData(tblinfo, numTables, oids);

Tom Lane's avatar
Tom Lane committed
570
	if (outputBlobs && hasBlobs(g_fout))
571
	{
Tom Lane's avatar
Tom Lane committed
572
		/* Add placeholders to allow correct sorting of blobs */
573 574 575 576 577 578 579
		DumpableObject *blobobj;

		blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
		blobobj->objType = DO_BLOBS;
		blobobj->catId = nilCatalogId;
		AssignDumpId(blobobj);
		blobobj->name = strdup("BLOBS");
Tom Lane's avatar
Tom Lane committed
580 581 582 583 584 585

		blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
		blobobj->objType = DO_BLOB_COMMENTS;
		blobobj->catId = nilCatalogId;
		AssignDumpId(blobobj);
		blobobj->name = strdup("BLOB COMMENTS");
586 587
	}

588 589 590 591 592 593 594
	/*
	 * Collect dependency data to assist in ordering the objects.
	 */
	getDependencies();

	/*
	 * Sort the objects into a safe dump order (no forward references).
595 596 597
	 *
	 * In 7.3 or later, we can rely on dependency information to help us
	 * determine a safe order, so the initial sort is mostly for cosmetic
Bruce Momjian's avatar
Bruce Momjian committed
598 599 600 601
	 * purposes: we sort by name to ensure that logically identical
	 * schemas will dump identically.  Before 7.3 we don't have
	 * dependencies and we use OID ordering as an (unreliable) guide to
	 * creation order.
602 603 604
	 */
	getDumpableObjects(&dobjs, &numObjs);

605 606 607 608 609
	if (g_fout->remoteVersion >= 70300)
		sortDumpableObjectsByTypeName(dobjs, numObjs);
	else
		sortDumpableObjectsByTypeOid(dobjs, numObjs);

610 611 612
	sortDumpableObjects(dobjs, numObjs);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
613 614
	 * Create archive TOC entries for all the objects to be dumped, in a
	 * safe order.
615 616
	 */

617 618 619 620
	/* First the special encoding entry. */
	dumpEncoding(g_fout);

	/* The database item is always second. */
621 622
	if (!dataOnly)
		dumpDatabase(g_fout);
623

624 625 626
	/* Now the rearrangeable objects. */
	for (i = 0; i < numObjs; i++)
		dumpDumpableObject(g_fout, dobjs[i]);
627

628 629 630
	/*
	 * And finally we can do the actual output.
	 */
631 632 633 634 635 636 637 638 639 640
	if (plainText)
	{
		ropt = NewRestoreOptions();
		ropt->filename = (char *) filename;
		ropt->dropSchema = outputClean;
		ropt->aclsSkip = aclsSkip;
		ropt->superuser = outputSuperuser;
		ropt->create = outputCreate;
		ropt->noOwner = outputNoOwner;
		ropt->disable_triggers = disable_triggers;
641
		ropt->use_setsessauth = use_setsessauth;
642
		ropt->dataOnly = dataOnly;
643

644 645 646 647
		if (compressLevel == -1)
			ropt->compression = 0;
		else
			ropt->compression = compressLevel;
648

649 650
		ropt->suppressDumpWarnings = true;		/* We've already shown
												 * them */
651

652 653
		RestoreArchive(g_fout, ropt);
	}
654

655
	CloseArchive(g_fout);
656

657
	PQfinish(g_conn);
658

659 660
	exit(0);
}
661 662


663 664 665
static void
help(const char *progname)
{
666 667
	printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
	printf(_("Usage:\n"));
668
	printf(_("  %s [OPTION]... [DBNAME]\n"), progname);
669

670 671 672 673 674 675 676 677 678 679 680
	printf(_("\nGeneral options:\n"));
	printf(_("  -f, --file=FILENAME      output file name\n"));
	printf(_("  -F, --format=c|t|p       output file format (custom, tar, plain text)\n"));
	printf(_("  -i, --ignore-version     proceed even when server version mismatches\n"
			 "                           pg_dump version\n"));
	printf(_("  -v, --verbose            verbose mode\n"));
	printf(_("  -Z, --compress=0-9       compression level for compressed formats\n"));
	printf(_("  --help                   show this help, then exit\n"));
	printf(_("  --version                output version information, then exit\n"));

	printf(_("\nOptions controlling the output content:\n"));
Bruce Momjian's avatar
Bruce Momjian committed
681 682 683 684 685
	printf(_("  -a, --data-only          dump only the data, not the schema\n"));
	printf(_("  -c, --clean              clean (drop) schema prior to create\n"));
	printf(_("  -C, --create             include commands to create database in dump\n"));
	printf(_("  -d, --inserts            dump data as INSERT, rather than COPY, commands\n"));
	printf(_("  -D, --column-inserts     dump data as INSERT commands with column names\n"));
Bruce Momjian's avatar
Bruce Momjian committed
686
	printf(_("  -E, --encoding=ENCODING  dump the data in encoding ENCODING\n"));
687
	printf(_("  -n, --schema=SCHEMA      dump the named schema only\n"));
Bruce Momjian's avatar
Bruce Momjian committed
688
	printf(_("  -o, --oids               include OIDs in dump\n"));
689
	printf(_("  -O, --no-owner           skip restoration of object ownership\n"
690
			 "                           in plain text format\n"));
Bruce Momjian's avatar
Bruce Momjian committed
691 692 693
	printf(_("  -s, --schema-only        dump only the schema, no data\n"));
	printf(_("  -S, --superuser=NAME     specify the superuser user name to use in\n"
			 "                           plain text format\n"));
694
	printf(_("  -t, --table=TABLE        dump the named table only\n"));
Bruce Momjian's avatar
Bruce Momjian committed
695
	printf(_("  -x, --no-privileges      do not dump privileges (grant/revoke)\n"));
696 697
	printf(_("  -X disable-dollar-quoting, --disable-dollar-quoting\n"
			 "                           disable dollar quoting, use SQL standard quoting\n"));
Bruce Momjian's avatar
Bruce Momjian committed
698 699
	printf(_("  -X disable-triggers, --disable-triggers\n"
			 "                           disable triggers during data-only restore\n"));
700 701 702
	printf(_("  -X use-set-session-authorization, --use-set-session-authorization\n"
			 "                           use SESSION AUTHORIZATION commands instead of\n"
			 "                           OWNER TO commands\n"));
703 704

	printf(_("\nConnection options:\n"));
705
	printf(_("  -h, --host=HOSTNAME      database server host or socket directory\n"));
706 707 708
	printf(_("  -p, --port=PORT          database server port number\n"));
	printf(_("  -U, --username=NAME      connect as specified database user\n"));
	printf(_("  -W, --password           force password prompt (should happen automatically)\n"));
709

710
	printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
711 712
			 "variable value is used.\n\n"));
	printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
713
}
714

715 716 717 718 719 720 721 722
void
exit_nicely(void)
{
	PQfinish(g_conn);
	if (g_verbose)
		write_msg(NULL, "*** aborted because of error\n");
	exit(1);
}
723

724 725 726 727 728 729 730 731 732
/*
 * selectDumpableNamespace: policy-setting subroutine
 *		Mark a namespace as to be dumped or not
 */
static void
selectDumpableNamespace(NamespaceInfo *nsinfo)
{
	/*
	 * If a specific table is being dumped, do not dump any complete
Bruce Momjian's avatar
Bruce Momjian committed
733 734
	 * namespaces.	If a specific namespace is being dumped, dump just
	 * that namespace. Otherwise, dump all non-system namespaces.
735
	 */
Bruce Momjian's avatar
Bruce Momjian committed
736
	if (selectTableName != NULL)
737
		nsinfo->dump = false;
Bruce Momjian's avatar
Bruce Momjian committed
738 739
	else if (selectSchemaName != NULL)
	{
740
		if (strcmp(nsinfo->dobj.name, selectSchemaName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
741 742 743 744
			nsinfo->dump = true;
		else
			nsinfo->dump = false;
	}
745 746
	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
747 748 749 750
		nsinfo->dump = false;
	else
		nsinfo->dump = true;
}
751

752 753 754 755 756 757 758 759 760
/*
 * selectDumpableTable: policy-setting subroutine
 *		Mark a table as to be dumped or not
 */
static void
selectDumpableTable(TableInfo *tbinfo)
{
	/*
	 * Always dump if dumping parent namespace; else, if a particular
Bruce Momjian's avatar
Bruce Momjian committed
761 762
	 * tablename has been specified, dump matching table name; else, do
	 * not dump.
763
	 */
764
	tbinfo->dump = false;
765
	if (tbinfo->dobj.namespace->dump)
766
		tbinfo->dump = true;
767
	else if (selectTableName != NULL &&
768
			 strcmp(tbinfo->dobj.name, selectTableName) == 0)
769 770 771 772
	{
		/* If both -s and -t specified, must match both to dump */
		if (selectSchemaName == NULL)
			tbinfo->dump = true;
773
		else if (strcmp(tbinfo->dobj.namespace->dobj.name, selectSchemaName) == 0)
774 775
			tbinfo->dump = true;
	}
776
}
777

778 779 780 781 782
/*
 *	Dump a table's contents for loading using the COPY command
 *	- this routine is called by the Archiver when it wants the table
 *	  to be dumped.
 */
783

784
#define COPYBUFSIZ		8192
785

786
static int
787
dumpTableData_copy(Archive *fout, void *dcontext)
788
{
789 790
	TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
	TableInfo  *tbinfo = tdinfo->tdtable;
791
	const char *classname = tbinfo->dobj.name;
792
	const bool	hasoids = tbinfo->hasoids;
793
	const bool	oids = tdinfo->oids;
794 795 796 797 798
	PQExpBuffer q = createPQExpBuffer();
	PGresult   *res;
	int			ret;
	bool		copydone;
	char		copybuf[COPYBUFSIZ];
Bruce Momjian's avatar
Bruce Momjian committed
799
	const char *column_list;
800

801
	if (g_verbose)
802
		write_msg(NULL, "dumping contents of table %s\n", classname);
803

804 805 806 807 808 809
	/*
	 * Make sure we are in proper schema.  We will qualify the table name
	 * below anyway (in case its name conflicts with a pg_catalog table);
	 * but this ensures reproducible results in case the table contains
	 * regproc, regclass, etc columns.
	 */
810
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
811

812 813 814 815 816 817 818 819 820 821
	/*
	 * If possible, specify the column list explicitly so that we have no
	 * possibility of retrieving data in the wrong column order.  (The
	 * default column ordering of COPY will not be what we want in certain
	 * corner cases involving ADD COLUMN and inheritance.)
	 */
	if (g_fout->remoteVersion >= 70300)
		column_list = fmtCopyColumnList(tbinfo);
	else
		column_list = "";		/* can't select columns in COPY */
822

823
	if (oids && hasoids)
824
	{
825
		appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
Bruce Momjian's avatar
Bruce Momjian committed
826 827
						fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
									   classname),
828
						  column_list);
829
	}
830
	else
831
	{
832
		appendPQExpBuffer(q, "COPY %s %s TO stdout;",
Bruce Momjian's avatar
Bruce Momjian committed
833 834
						fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
									   classname),
835
						  column_list);
836
	}
837
	res = PQexec(g_conn, q->data);
838
	check_sql_result(res, g_conn, q->data, PGRES_COPY_OUT);
839

840
	copydone = false;
841

842
	while (!copydone)
843
	{
844
		ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
845

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
		if (copybuf[0] == '\\' &&
			copybuf[1] == '.' &&
			copybuf[2] == '\0')
		{
			copydone = true;	/* don't print this... */
		}
		else
		{
			archputs(copybuf, fout);
			switch (ret)
			{
				case EOF:
					copydone = true;
					/* FALLTHROUGH */
				case 0:
861
					archputs("\n", fout);
862 863 864 865 866
					break;
				case 1:
					break;
			}
		}
867

868 869 870
		/*
		 * THROTTLE:
		 *
Bruce Momjian's avatar
Bruce Momjian committed
871 872 873 874
		 * There was considerable discussion in late July, 2000 regarding
		 * slowing down pg_dump when backing up large tables. Users with
		 * both slow & fast (muti-processor) machines experienced
		 * performance degradation when doing a backup.
875
		 *
Bruce Momjian's avatar
Bruce Momjian committed
876 877 878 879
		 * Initial attempts based on sleeping for a number of ms for each ms
		 * of work were deemed too complex, then a simple 'sleep in each
		 * loop' implementation was suggested. The latter failed because
		 * the loop was too tight. Finally, the following was implemented:
880
		 *
Bruce Momjian's avatar
Bruce Momjian committed
881 882 883
		 * If throttle is non-zero, then See how long since the last sleep.
		 * Work out how long to sleep (based on ratio). If sleep is more
		 * than 100ms, then sleep reset timer EndIf EndIf
884
		 *
Bruce Momjian's avatar
Bruce Momjian committed
885 886
		 * where the throttle value was the number of ms to sleep per ms of
		 * work. The calculation was done in each loop.
887
		 *
Bruce Momjian's avatar
Bruce Momjian committed
888 889 890 891 892
		 * Most of the hard work is done in the backend, and this solution
		 * still did not work particularly well: on slow machines, the
		 * ratio was 50:1, and on medium paced machines, 1:1, and on fast
		 * multi-processor machines, it had little or no effect, for
		 * reasons that were unclear.
893 894 895
		 *
		 * Further discussion ensued, and the proposal was dropped.
		 *
Bruce Momjian's avatar
Bruce Momjian committed
896 897 898 899 900
		 * For those people who want this feature, it can be implemented
		 * using gettimeofday in each loop, calculating the time since
		 * last sleep, multiplying that by the sleep ratio, then if the
		 * result is more than a preset 'minimum sleep time' (say 100ms),
		 * call the 'select' function to sleep for a subsecond period ie.
901 902 903
		 *
		 * select(0, NULL, NULL, NULL, &tvi);
		 *
Bruce Momjian's avatar
Bruce Momjian committed
904
		 * This will return after the interval specified in the structure
905
		 * tvi. Finally, call gettimeofday again to save the 'last sleep
Bruce Momjian's avatar
Bruce Momjian committed
906
		 * time'.
907
		 */
908
	}
909
	archprintf(fout, "\\.\n\n\n");
910

911 912
	ret = PQendcopy(g_conn);
	if (ret != 0)
913
	{
914 915 916 917
		write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname);
		write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
		write_msg(NULL, "The command was: %s\n", q->data);
		exit_nicely();
918 919
	}

920
	PQclear(res);
921 922 923
	destroyPQExpBuffer(q);
	return 1;
}
924

925
static int
926
dumpTableData_insert(Archive *fout, void *dcontext)
927
{
928 929
	TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
	TableInfo  *tbinfo = tdinfo->tdtable;
930
	const char *classname = tbinfo->dobj.name;
931 932 933
	PQExpBuffer q = createPQExpBuffer();
	PGresult   *res;
	int			tuple;
934
	int			nfields;
935
	int			field;
936

937
	/*
938 939 940 941
	 * Make sure we are in proper schema.  We will qualify the table name
	 * below anyway (in case its name conflicts with a pg_catalog table);
	 * but this ensures reproducible results in case the table contains
	 * regproc, regclass, etc columns.
942
	 */
943
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
944

945 946 947 948
	if (fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
						  "SELECT * FROM ONLY %s",
Bruce Momjian's avatar
Bruce Momjian committed
949 950
						fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
									   classname));
951
	}
952
	else
953 954 955
	{
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
						  "SELECT * FROM %s",
Bruce Momjian's avatar
Bruce Momjian committed
956 957
						fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
									   classname));
958
	}
959

960
	res = PQexec(g_conn, q->data);
961
	check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
962

963 964 965
	do
	{
		PQclear(res);
966

967
		res = PQexec(g_conn, "FETCH 100 FROM _pg_dump_cursor");
968 969
		check_sql_result(res, g_conn, "FETCH 100 FROM _pg_dump_cursor",
						 PGRES_TUPLES_OK);
970
		nfields = PQnfields(res);
971 972
		for (tuple = 0; tuple < PQntuples(res); tuple++)
		{
973
			archprintf(fout, "INSERT INTO %s ", fmtId(classname));
974 975 976 977 978 979
			if (nfields == 0)
			{
				/* corner case for zero-column table */
				archprintf(fout, "DEFAULT VALUES;\n");
				continue;
			}
980 981 982 983
			if (attrNames == true)
			{
				resetPQExpBuffer(q);
				appendPQExpBuffer(q, "(");
984
				for (field = 0; field < nfields; field++)
985 986
				{
					if (field > 0)
987
						appendPQExpBuffer(q, ", ");
988
					appendPQExpBufferStr(q, fmtId(PQfname(res, field)));
989 990
				}
				appendPQExpBuffer(q, ") ");
991
				archputs(q->data, fout);
992 993
			}
			archprintf(fout, "VALUES (");
994
			for (field = 0; field < nfields; field++)
995 996
			{
				if (field > 0)
997
					archprintf(fout, ", ");
998 999 1000 1001 1002
				if (PQgetisnull(res, tuple, field))
				{
					archprintf(fout, "NULL");
					continue;
				}
1003 1004

				/* XXX This code is partially duplicated in ruleutils.c */
1005 1006 1007 1008
				switch (PQftype(res, field))
				{
					case INT2OID:
					case INT4OID:
1009 1010
					case INT8OID:
					case OIDOID:
1011
					case FLOAT4OID:
1012 1013
					case FLOAT8OID:
					case NUMERICOID:
Bruce Momjian's avatar
Bruce Momjian committed
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
						{
							/*
							 * These types are printed without quotes
							 * unless they contain values that aren't
							 * accepted by the scanner unquoted (e.g.,
							 * 'NaN').	Note that strtod() and friends
							 * might accept NaN, so we can't use that to
							 * test.
							 *
							 * In reality we only need to defend against
							 * infinity and NaN, so we need not get too
							 * crazy about pattern matching here.
							 */
							const char *s = PQgetvalue(res, tuple, field);

							if (strspn(s, "0123456789 +-eE.") == strlen(s))
								archprintf(fout, "%s", s);
							else
								archprintf(fout, "'%s'", s);
						}
						break;
1035

1036 1037 1038 1039 1040
					case BITOID:
					case VARBITOID:
						archprintf(fout, "B'%s'",
								   PQgetvalue(res, tuple, field));
						break;
1041

1042
					case BOOLOID:
Bruce Momjian's avatar
Bruce Momjian committed
1043
						if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
1044 1045 1046 1047 1048 1049 1050
							archprintf(fout, "true");
						else
							archprintf(fout, "false");
						break;

					default:
						/* All other types are printed as string literals. */
1051
						resetPQExpBuffer(q);
1052
						appendStringLiteral(q, PQgetvalue(res, tuple, field), false);
1053
						archputs(q->data, fout);
1054 1055 1056 1057 1058 1059
						break;
				}
			}
			archprintf(fout, ");\n");
		}
	} while (PQntuples(res) > 0);
1060

1061
	PQclear(res);
1062

1063 1064 1065
	archprintf(fout, "\n\n");

	do_sql_command(g_conn, "CLOSE _pg_dump_cursor");
1066

1067 1068 1069
	destroyPQExpBuffer(q);
	return 1;
}
1070

1071

1072
/*
1073 1074 1075 1076
 * dumpTableData -
 *	  dump the contents of a single table
 *
 * Actually, this just makes an ArchiveEntry for the table contents.
1077 1078
 */
static void
1079
dumpTableData(Archive *fout, TableDataInfo *tdinfo)
1080
{
1081
	TableInfo  *tbinfo = tdinfo->tdtable;
Bruce Momjian's avatar
Bruce Momjian committed
1082
	PQExpBuffer copyBuf = createPQExpBuffer();
1083 1084
	DataDumperPtr dumpFn;
	char	   *copyStmt;
1085 1086 1087 1088 1089 1090 1091

	if (!dumpInserts)
	{
		/* Dump/restore using COPY */
		dumpFn = dumpTableData_copy;
		/* must use 2 steps here 'cause fmtId is nonreentrant */
		appendPQExpBuffer(copyBuf, "COPY %s ",
1092
						  fmtId(tbinfo->dobj.name));
1093 1094
		appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
						  fmtCopyColumnList(tbinfo),
Bruce Momjian's avatar
Bruce Momjian committed
1095
				  (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
		copyStmt = copyBuf->data;
	}
	else
	{
		/* Restore using INSERT */
		dumpFn = dumpTableData_insert;
		copyStmt = NULL;
	}

	ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
1106 1107
				 tbinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
1108
				 NULL,
1109
				 tbinfo->rolname, false,
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
				 "TABLE DATA", "", "", copyStmt,
				 tdinfo->dobj.dependencies, tdinfo->dobj.nDeps,
				 dumpFn, tdinfo);

	destroyPQExpBuffer(copyBuf);
}

/*
 * getTableData -
 *	  set up dumpable objects representing the contents of tables
 */
static void
getTableData(TableInfo *tblinfo, int numTables, bool oids)
{
1124
	int			i;
1125

1126 1127
	for (i = 0; i < numTables; i++)
	{
1128
		/* Skip VIEWs (no data to dump) */
1129 1130
		if (tblinfo[i].relkind == RELKIND_VIEW)
			continue;
1131 1132
		/* Skip SEQUENCEs (handled elsewhere) */
		if (tblinfo[i].relkind == RELKIND_SEQUENCE)
1133
			continue;
1134

1135 1136
		if (tblinfo[i].dump)
		{
1137
			TableDataInfo *tdinfo;
1138

1139
			tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
1140

1141
			tdinfo->dobj.objType = DO_TABLE_DATA;
Bruce Momjian's avatar
Bruce Momjian committed
1142

1143 1144 1145 1146 1147 1148 1149
			/*
			 * Note: use tableoid 0 so that this object won't be mistaken
			 * for something that pg_depend entries apply to.
			 */
			tdinfo->dobj.catId.tableoid = 0;
			tdinfo->dobj.catId.oid = tblinfo[i].dobj.catId.oid;
			AssignDumpId(&tdinfo->dobj);
1150 1151
			tdinfo->dobj.name = tblinfo[i].dobj.name;
			tdinfo->dobj.namespace = tblinfo[i].dobj.namespace;
1152 1153 1154
			tdinfo->tdtable = &(tblinfo[i]);
			tdinfo->oids = oids;
			addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
1155 1156
		}
	}
1157 1158
}

1159

1160 1161 1162 1163
/*
 * dumpDatabase:
 *	dump the database definition
 */
1164
static void
1165 1166
dumpDatabase(Archive *AH)
{
1167 1168 1169 1170 1171
	PQExpBuffer dbQry = createPQExpBuffer();
	PQExpBuffer delQry = createPQExpBuffer();
	PQExpBuffer creaQry = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
1172 1173 1174
	int			i_tableoid,
				i_oid,
				i_dba,
1175
				i_encoding,
1176
				i_tablespace;
1177 1178
	CatalogId	dbCatId;
	DumpId		dbDumpId;
1179 1180 1181
	const char *datname,
			   *dba,
			   *encoding,
1182
			   *tablespace;
1183 1184

	datname = PQdb(g_conn);
1185 1186

	if (g_verbose)
1187
		write_msg(NULL, "saving database definition\n");
1188

1189 1190 1191
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

1192
	/* Get the database owner and parameters from pg_database */
1193
	if (g_fout->remoteVersion >= 80000)
1194
	{
1195
		appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
1196
						  "(%s datdba) as dba, "
1197
						  "pg_encoding_to_char(encoding) as encoding, "
1198 1199
						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace "
						  "FROM pg_database "
1200 1201
						  "WHERE datname = ",
						  username_subquery);
1202 1203 1204 1205 1206
		appendStringLiteral(dbQry, datname, true);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
1207
						  "(%s datdba) as dba, "
1208 1209
						  "pg_encoding_to_char(encoding) as encoding, "
						  "NULL as tablespace "
1210
						  "FROM pg_database "
1211 1212
						  "WHERE datname = ",
						  username_subquery);
1213
		appendStringLiteral(dbQry, datname, true);
1214
	}
1215 1216 1217 1218 1219
	else
	{
		appendPQExpBuffer(dbQry, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_database') AS tableoid, "
						  "oid, "
1220
						  "(%s datdba) as dba, "
1221
						  "pg_encoding_to_char(encoding) as encoding, "
1222
						  "NULL as tablespace "
1223
						  "FROM pg_database "
1224 1225
						  "WHERE datname = ",
						  username_subquery);
1226 1227 1228 1229 1230
		appendStringLiteral(dbQry, datname, true);
	}

	res = PQexec(g_conn, dbQry->data);
	check_sql_result(res, g_conn, dbQry->data, PGRES_TUPLES_OK);
1231 1232 1233

	ntups = PQntuples(res);

1234 1235
	if (ntups <= 0)
	{
1236 1237
		write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
				  datname);
1238
		exit_nicely();
1239 1240
	}

1241 1242
	if (ntups != 1)
	{
1243
		write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
1244
				  ntups, datname);
1245
		exit_nicely();
1246 1247
	}

1248 1249
	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
1250
	i_dba = PQfnumber(res, "dba");
1251
	i_encoding = PQfnumber(res, "encoding");
1252
	i_tablespace = PQfnumber(res, "tablespace");
1253 1254 1255

	dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
	dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
1256 1257
	dba = PQgetvalue(res, 0, i_dba);
	encoding = PQgetvalue(res, 0, i_encoding);
1258
	tablespace = PQgetvalue(res, 0, i_tablespace);
1259 1260

	appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
1261
					  fmtId(datname));
1262 1263 1264 1265 1266
	if (strlen(encoding) > 0)
	{
		appendPQExpBuffer(creaQry, " ENCODING = ");
		appendStringLiteral(creaQry, encoding, true);
	}
1267
	if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0)
1268 1269
		appendPQExpBuffer(creaQry, " TABLESPACE = %s",
						  fmtId(tablespace));
1270 1271 1272
	appendPQExpBuffer(creaQry, ";\n");

	appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
1273
					  fmtId(datname));
1274

1275 1276 1277 1278 1279
	dbDumpId = createDumpId();

	ArchiveEntry(AH,
				 dbCatId,		/* catalog ID */
				 dbDumpId,		/* dump ID */
1280 1281
				 datname,		/* Name */
				 NULL,			/* Namespace */
1282
				 NULL, 			/* Tablespace */
1283
				 dba,			/* Owner */
1284
				 false,			/* with oids */
1285
				 "DATABASE",	/* Desc */
Bruce Momjian's avatar
Bruce Momjian committed
1286
				 creaQry->data, /* Create */
1287 1288
				 delQry->data,	/* Del */
				 NULL,			/* Copy */
1289 1290
				 NULL,			/* Deps */
				 0,				/* # Deps */
1291 1292
				 NULL,			/* Dumper */
				 NULL);			/* Dumper Arg */
1293

1294 1295 1296 1297 1298 1299
	/* Dump DB comment if any */
	resetPQExpBuffer(dbQry);
	appendPQExpBuffer(dbQry, "DATABASE %s", fmtId(datname));
	dumpComment(AH, dbQry->data, NULL, "",
				dbCatId, 0, dbDumpId);

1300 1301
	PQclear(res);

1302 1303 1304
	destroyPQExpBuffer(dbQry);
	destroyPQExpBuffer(delQry);
	destroyPQExpBuffer(creaQry);
1305 1306 1307
}


1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
/*
 * dumpEncoding: put the correct encoding into the archive
 */
static void
dumpEncoding(Archive *AH)
{
	PQExpBuffer qry;
	PGresult   *res;

	/* Can't read the encoding from pre-7.3 servers (SHOW isn't a query) */
	if (AH->remoteVersion < 70300)
		return;

	if (g_verbose)
		write_msg(NULL, "saving encoding\n");

	qry = createPQExpBuffer();

	appendPQExpBuffer(qry, "SHOW client_encoding");

	res = PQexec(g_conn, qry->data);

	check_sql_result(res, g_conn, qry->data, PGRES_TUPLES_OK);

	resetPQExpBuffer(qry);

	appendPQExpBuffer(qry, "SET client_encoding = ");
	appendStringLiteral(qry, PQgetvalue(res, 0, 0), true);
	appendPQExpBuffer(qry, ";\n");

	ArchiveEntry(AH, nilCatalogId, createDumpId(),
1339
				 "ENCODING", NULL, NULL, "",
1340
				 false, "ENCODING", qry->data, "", NULL,
1341 1342 1343 1344 1345 1346 1347 1348 1349
				 NULL, 0,
				 NULL, NULL);

	PQclear(res);

	destroyPQExpBuffer(qry);
}


Tom Lane's avatar
Tom Lane committed
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
/*
 * hasBlobs:
 *	Test whether database contains any large objects
 */
static bool
hasBlobs(Archive *AH)
{
	bool		result;
	const char *blobQry;
	PGresult   *res;

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	/* Check for BLOB OIDs */
	if (AH->remoteVersion >= 70100)
		blobQry = "SELECT loid FROM pg_largeobject LIMIT 1";
	else
		blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1";

	res = PQexec(g_conn, blobQry);
	check_sql_result(res, g_conn, blobQry, PGRES_TUPLES_OK);

	result = PQntuples(res) > 0;

	PQclear(res);

	return result;
}

1380 1381 1382 1383
/*
 * dumpBlobs:
 *	dump all blobs
 */
1384
static int
1385
dumpBlobs(Archive *AH, void *arg)
1386
{
Tom Lane's avatar
Tom Lane committed
1387 1388
	const char *blobQry;
	const char *blobFetchQry;
1389
	PGresult   *res;
1390
	char		buf[LOBBUFSIZE];
Tom Lane's avatar
Tom Lane committed
1391
	int			i;
1392
	int			cnt;
1393 1394

	if (g_verbose)
1395
		write_msg(NULL, "saving large objects\n");
1396

1397 1398 1399
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

Tom Lane's avatar
Tom Lane committed
1400
	/* Cursor to get all BLOB OIDs */
1401
	if (AH->remoteVersion >= 70100)
Tom Lane's avatar
Tom Lane committed
1402
		blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
1403
	else
Tom Lane's avatar
Tom Lane committed
1404
		blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'";
1405

Tom Lane's avatar
Tom Lane committed
1406 1407
	res = PQexec(g_conn, blobQry);
	check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
1408

Tom Lane's avatar
Tom Lane committed
1409 1410
	/* Command to fetch from cursor */
	blobFetchQry = "FETCH 1000 IN bloboid";
1411

1412 1413
	do
	{
1414 1415
		PQclear(res);

1416
		/* Do a fetch */
Tom Lane's avatar
Tom Lane committed
1417 1418
		res = PQexec(g_conn, blobFetchQry);
		check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
1419 1420 1421 1422

		/* Process the tuples, if any */
		for (i = 0; i < PQntuples(res); i++)
		{
Tom Lane's avatar
Tom Lane committed
1423 1424 1425
			Oid			blobOid;
			int			loFd;

1426
			blobOid = atooid(PQgetvalue(res, i, 0));
1427 1428 1429 1430
			/* Open the BLOB */
			loFd = lo_open(g_conn, blobOid, INV_READ);
			if (loFd == -1)
			{
1431 1432
				write_msg(NULL, "dumpBlobs(): could not open large object: %s",
						  PQerrorMessage(g_conn));
1433
				exit_nicely();
1434 1435 1436 1437
			}

			StartBlob(AH, blobOid);

1438 1439 1440
			/* Now read it in chunks, sending data to archive */
			do
			{
1441
				cnt = lo_read(g_conn, loFd, buf, LOBBUFSIZE);
1442 1443 1444 1445 1446 1447
				if (cnt < 0)
				{
					write_msg(NULL, "dumpBlobs(): error reading large object: %s",
							  PQerrorMessage(g_conn));
					exit_nicely();
				}
1448

1449 1450
				WriteData(AH, buf, cnt);
			} while (cnt > 0);
1451

1452
			lo_close(g_conn, loFd);
1453

1454 1455 1456
			EndBlob(AH, blobOid);
		}
	} while (PQntuples(res) > 0);
1457

1458 1459
	PQclear(res);

Tom Lane's avatar
Tom Lane committed
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534
	return 1;
}

/*
 * dumpBlobComments
 *	dump all blob comments
 *
 * Since we don't provide any way to be selective about dumping blobs,
 * there's no need to be selective about their comments either.  We put
 * all the comments into one big TOC entry.
 */
static int
dumpBlobComments(Archive *AH, void *arg)
{
	const char *blobQry;
	const char *blobFetchQry;
	PQExpBuffer commentcmd = createPQExpBuffer();
	PGresult   *res;
	int			i;

	if (g_verbose)
		write_msg(NULL, "saving large object comments\n");

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	/* Cursor to get all BLOB comments */
	if (AH->remoteVersion >= 70200)
		blobQry = "DECLARE blobcmt CURSOR FOR SELECT DISTINCT loid, obj_description(loid, 'pg_largeobject') FROM pg_largeobject";
	else if (AH->remoteVersion >= 70100)
		blobQry = "DECLARE blobcmt CURSOR FOR SELECT DISTINCT loid, obj_description(loid) FROM pg_largeobject";
	else
		blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, (SELECT description FROM pg_description pd WHERE pd.objoid=pc.oid) FROM pg_class pc WHERE relkind = 'l'";

	res = PQexec(g_conn, blobQry);
	check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);

	/* Command to fetch from cursor */
	blobFetchQry = "FETCH 100 IN blobcmt";

	do
	{
		PQclear(res);

		/* Do a fetch */
		res = PQexec(g_conn, blobFetchQry);
		check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);

		/* Process the tuples, if any */
		for (i = 0; i < PQntuples(res); i++)
		{
			Oid		blobOid;
			char	*comment;

			/* ignore blobs without comments */
			if (PQgetisnull(res, i, 1))
				continue;

			blobOid = atooid(PQgetvalue(res, i, 0));
			comment = PQgetvalue(res, i, 1);

			printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
							  blobOid);
			appendStringLiteral(commentcmd, comment, false);
			appendPQExpBuffer(commentcmd, ";\n");

			archputs(commentcmd->data, AH);
		}
	} while (PQntuples(res) > 0);

	PQclear(res);

	archputs("\n", AH);

	destroyPQExpBuffer(commentcmd);
1535

1536
	return 1;
1537 1538 1539
}

/*
1540 1541 1542
 * getNamespaces:
 *	  read all namespaces in the system catalogs and return them in the
 * NamespaceInfo* structure
1543
 *
1544
 *	numNamespaces is set to the number of namespaces read in
1545
 */
1546 1547
NamespaceInfo *
getNamespaces(int *numNamespaces)
1548
{
1549
	PGresult   *res;
1550 1551
	int			ntups;
	int			i;
1552 1553
	PQExpBuffer query;
	NamespaceInfo *nsinfo;
1554
	int			i_tableoid;
1555
	int			i_oid;
1556
	int			i_nspname;
1557
	int			i_rolname;
1558
	int			i_nspacl;
1559 1560

	/*
1561 1562
	 * Before 7.3, there are no real namespaces; create two dummy entries,
	 * one for user stuff and one for system stuff.
1563
	 */
1564 1565 1566 1567
	if (g_fout->remoteVersion < 70300)
	{
		nsinfo = (NamespaceInfo *) malloc(2 * sizeof(NamespaceInfo));

1568 1569 1570 1571
		nsinfo[0].dobj.objType = DO_NAMESPACE;
		nsinfo[0].dobj.catId.tableoid = 0;
		nsinfo[0].dobj.catId.oid = 0;
		AssignDumpId(&nsinfo[0].dobj);
1572
		nsinfo[0].dobj.name = strdup("public");
1573
		nsinfo[0].rolname = strdup("");
1574 1575 1576 1577
		nsinfo[0].nspacl = strdup("");

		selectDumpableNamespace(&nsinfo[0]);

1578 1579 1580 1581
		nsinfo[1].dobj.objType = DO_NAMESPACE;
		nsinfo[1].dobj.catId.tableoid = 0;
		nsinfo[1].dobj.catId.oid = 1;
		AssignDumpId(&nsinfo[1].dobj);
1582
		nsinfo[1].dobj.name = strdup("pg_catalog");
1583
		nsinfo[1].rolname = strdup("");
1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597
		nsinfo[1].nspacl = strdup("");

		selectDumpableNamespace(&nsinfo[1]);

		g_namespaces = nsinfo;
		g_numNamespaces = *numNamespaces = 2;

		return nsinfo;
	}

	query = createPQExpBuffer();

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
1598

1599 1600 1601 1602
	/*
	 * we fetch all namespaces including system ones, so that every object
	 * we read in can be linked to a containing namespace.
	 */
1603
	appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
1604 1605 1606
					  "(%s nspowner) as rolname, "
					  "nspacl FROM pg_namespace",
					  username_subquery);
1607

Bruce Momjian's avatar
Bruce Momjian committed
1608
	res = PQexec(g_conn, query->data);
1609
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1610 1611 1612

	ntups = PQntuples(res);

1613
	nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
1614

1615
	i_tableoid = PQfnumber(res, "tableoid");
1616
	i_oid = PQfnumber(res, "oid");
1617
	i_nspname = PQfnumber(res, "nspname");
1618
	i_rolname = PQfnumber(res, "rolname");
1619
	i_nspacl = PQfnumber(res, "nspacl");
1620 1621 1622

	for (i = 0; i < ntups; i++)
	{
1623 1624 1625 1626
		nsinfo[i].dobj.objType = DO_NAMESPACE;
		nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&nsinfo[i].dobj);
1627
		nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
1628
		nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1629
		nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
1630

1631 1632
		/* Decide whether to dump this namespace */
		selectDumpableNamespace(&nsinfo[i]);
1633

1634
		if (strlen(nsinfo[i].rolname) == 0)
1635
			write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
1636
					  nsinfo[i].dobj.name);
1637 1638
	}

Bruce Momjian's avatar
Bruce Momjian committed
1639
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1640 1641
	 * If the user attempted to dump a specific namespace, check to ensure
	 * that the specified namespace actually exists.
Bruce Momjian's avatar
Bruce Momjian committed
1642 1643 1644 1645
	 */
	if (selectSchemaName)
	{
		for (i = 0; i < ntups; i++)
1646
			if (strcmp(nsinfo[i].dobj.name, selectSchemaName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1647 1648 1649 1650 1651
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
1652
			write_msg(NULL, "specified schema \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
1653 1654 1655 1656 1657
					  selectSchemaName);
			exit_nicely();
		}
	}

1658
	PQclear(res);
1659 1660
	destroyPQExpBuffer(query);

1661 1662
	g_namespaces = nsinfo;
	g_numNamespaces = *numNamespaces = ntups;
1663

1664
	return nsinfo;
1665 1666
}

1667 1668 1669 1670 1671 1672
/*
 * findNamespace:
 *		given a namespace OID and an object OID, look up the info read by
 *		getNamespaces
 *
 * NB: for pre-7.3 source database, we use object OID to guess whether it's
Bruce Momjian's avatar
Bruce Momjian committed
1673
 * a system object or not.	In 7.3 and later there is no guessing.
1674 1675
 */
static NamespaceInfo *
1676
findNamespace(Oid nsoid, Oid objoid)
1677
{
1678
	int			i;
1679

1680
	if (g_fout->remoteVersion >= 70300)
1681
	{
1682
		for (i = 0; i < g_numNamespaces; i++)
1683
		{
Bruce Momjian's avatar
Bruce Momjian committed
1684
			NamespaceInfo *nsinfo = &g_namespaces[i];
1685

1686
			if (nsoid == nsinfo->dobj.catId.oid)
1687
				return nsinfo;
1688
		}
1689
		write_msg(NULL, "schema with OID %u does not exist\n", nsoid);
1690
		exit_nicely();
1691
	}
1692 1693 1694
	else
	{
		/* This code depends on the layout set up by getNamespaces. */
1695
		if (objoid > g_last_builtin_oid)
1696 1697 1698 1699
			i = 0;				/* user object */
		else
			i = 1;				/* system object */
		return &g_namespaces[i];
1700 1701
	}

1702
	return NULL;				/* keep compiler quiet */
1703
}
1704 1705

/*
1706 1707 1708
 * getTypes:
 *	  read all types in the system catalogs and return them in the
 * TypeInfo* structure
1709
 *
1710
 *	numTypes is set to the number of types read in
1711 1712 1713
 *
 * NB: this must run after getFuncs() because we assume we can do
 * findFuncByOid().
1714
 */
1715 1716
TypeInfo *
getTypes(int *numTypes)
1717
{
Bruce Momjian's avatar
Bruce Momjian committed
1718
	PGresult   *res;
1719 1720
	int			ntups;
	int			i;
1721
	PQExpBuffer query = createPQExpBuffer();
1722
	TypeInfo   *tinfo;
1723
	int			i_tableoid;
1724
	int			i_oid;
1725 1726
	int			i_typname;
	int			i_typnamespace;
1727
	int			i_rolname;
1728 1729
	int			i_typinput;
	int			i_typoutput;
1730 1731
	int			i_typelem;
	int			i_typrelid;
Bruce Momjian's avatar
Bruce Momjian committed
1732
	int			i_typrelkind;
1733 1734
	int			i_typtype;
	int			i_typisdefined;
1735

1736 1737 1738 1739 1740
	/*
	 * we include even the built-in types because those may be used as
	 * array elements by user-defined types
	 *
	 * we filter out the built-in types when we dump out the types
1741 1742
	 *
	 * same approach for undefined (shell) types
1743
	 */
1744

1745 1746 1747 1748
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
1749
	{
1750
		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
1751
						  "typnamespace, "
1752
						  "(%s typowner) as rolname, "
1753
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1754
					   "typoutput::oid as typoutput, typelem, typrelid, "
1755 1756 1757
						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
						  "typtype, typisdefined "
1758 1759
						  "FROM pg_type",
						  username_subquery);
1760 1761 1762 1763 1764
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
						  "0::oid as typnamespace, "
1765
						  "(%s typowner) as rolname, "
1766
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1767
					   "typoutput::oid as typoutput, typelem, typrelid, "
1768 1769
						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
Bruce Momjian's avatar
Bruce Momjian committed
1770
						  "typtype, typisdefined "
1771 1772
						  "FROM pg_type",
						  username_subquery);
1773
	}
1774 1775
	else
	{
1776 1777 1778
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_type') AS tableoid, "
						  "oid, typname, "
1779
						  "0::oid as typnamespace, "
1780
						  "(%s typowner) as rolname, "
1781
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1782
					   "typoutput::oid as typoutput, typelem, typrelid, "
1783 1784
						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
Bruce Momjian's avatar
Bruce Momjian committed
1785
						  "typtype, typisdefined "
1786 1787
						  "FROM pg_type",
						  username_subquery);
1788
	}
1789

Bruce Momjian's avatar
Bruce Momjian committed
1790
	res = PQexec(g_conn, query->data);
1791
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1792 1793 1794

	ntups = PQntuples(res);

1795
	tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
1796

1797
	i_tableoid = PQfnumber(res, "tableoid");
1798
	i_oid = PQfnumber(res, "oid");
1799 1800
	i_typname = PQfnumber(res, "typname");
	i_typnamespace = PQfnumber(res, "typnamespace");
1801
	i_rolname = PQfnumber(res, "rolname");
1802 1803
	i_typinput = PQfnumber(res, "typinput");
	i_typoutput = PQfnumber(res, "typoutput");
1804 1805
	i_typelem = PQfnumber(res, "typelem");
	i_typrelid = PQfnumber(res, "typrelid");
Bruce Momjian's avatar
Bruce Momjian committed
1806
	i_typrelkind = PQfnumber(res, "typrelkind");
1807 1808
	i_typtype = PQfnumber(res, "typtype");
	i_typisdefined = PQfnumber(res, "typisdefined");
1809 1810 1811

	for (i = 0; i < ntups; i++)
	{
1812 1813 1814 1815 1816 1817 1818
		Oid			typoutput;
		FuncInfo   *funcInfo;

		tinfo[i].dobj.objType = DO_TYPE;
		tinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		tinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&tinfo[i].dobj);
1819 1820
		tinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_typname));
		tinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
1821
												tinfo[i].dobj.catId.oid);
1822
		tinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1823 1824 1825 1826
		tinfo[i].typinput = atooid(PQgetvalue(res, i, i_typinput));
		typoutput = atooid(PQgetvalue(res, i, i_typoutput));
		tinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
		tinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
Bruce Momjian's avatar
Bruce Momjian committed
1827
		tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
1828 1829
		tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);

1830 1831
		/*
		 * If it's a table's rowtype, use special type code to facilitate
Bruce Momjian's avatar
Bruce Momjian committed
1832
		 * sorting into the desired order.	(We don't want to consider it
1833 1834 1835 1836 1837 1838
		 * an ordinary type because that would bring the table up into the
		 * datatype part of the dump order.)
		 */
		if (OidIsValid(tinfo[i].typrelid) && tinfo[i].typrelkind != 'c')
			tinfo[i].dobj.objType = DO_TABLE_TYPE;

1839 1840 1841
		/*
		 * check for user-defined array types, omit system generated ones
		 */
1842
		if (OidIsValid(tinfo[i].typelem) &&
1843
			tinfo[i].dobj.name[0] != '_')
1844 1845 1846
			tinfo[i].isArray = true;
		else
			tinfo[i].isArray = false;
1847

1848 1849 1850 1851
		if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
			tinfo[i].isDefined = true;
		else
			tinfo[i].isDefined = false;
1852

1853 1854 1855 1856 1857 1858 1859 1860 1861 1862
		/*
		 * If it's a domain, fetch info about its constraints, if any
		 */
		tinfo[i].nDomChecks = 0;
		tinfo[i].domChecks = NULL;
		if (tinfo[i].typtype == 'd')
			getDomainConstraints(&(tinfo[i]));

		/*
		 * Make sure there are dependencies from the type to its input and
Bruce Momjian's avatar
Bruce Momjian committed
1863 1864 1865
		 * output functions.  (We don't worry about typsend, typreceive,
		 * or typanalyze since those are only valid in 7.4 and later,
		 * wherein the standard dependency mechanism will pick them up.)
1866 1867 1868 1869 1870 1871 1872 1873 1874 1875
		 */
		funcInfo = findFuncByOid(tinfo[i].typinput);
		if (funcInfo)
			addObjectDependency(&tinfo[i].dobj,
								funcInfo->dobj.dumpId);
		funcInfo = findFuncByOid(typoutput);
		if (funcInfo)
			addObjectDependency(&tinfo[i].dobj,
								funcInfo->dobj.dumpId);

1876
		if (strlen(tinfo[i].rolname) == 0 && tinfo[i].isDefined)
1877
			write_msg(NULL, "WARNING: owner of data type \"%s\" appears to be invalid\n",
1878
					  tinfo[i].dobj.name);
1879 1880
	}

1881 1882
	*numTypes = ntups;

1883 1884
	PQclear(res);

1885 1886
	destroyPQExpBuffer(query);

1887
	return tinfo;
1888 1889 1890
}

/*
1891 1892 1893
 * getOperators:
 *	  read all operators in the system catalogs and return them in the
 * OprInfo* structure
1894
 *
1895
 *	numOprs is set to the number of operators read in
1896
 */
1897 1898
OprInfo *
getOperators(int *numOprs)
1899
{
1900
	PGresult   *res;
1901 1902
	int			ntups;
	int			i;
1903
	PQExpBuffer query = createPQExpBuffer();
1904
	OprInfo    *oprinfo;
1905
	int			i_tableoid;
1906
	int			i_oid;
1907 1908
	int			i_oprname;
	int			i_oprnamespace;
1909
	int			i_rolname;
1910
	int			i_oprcode;
1911

1912
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1913 1914
	 * find all operators, including builtin operators; we filter out
	 * system-defined operators at dump-out time.
1915
	 */
1916

1917 1918 1919 1920
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
1921
	{
1922
		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
1923
						  "oprnamespace, "
1924
						  "(%s oprowner) as rolname, "
1925
						  "oprcode::oid as oprcode "
1926 1927
						  "FROM pg_operator",
						  username_subquery);
1928 1929 1930 1931 1932
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
						  "0::oid as oprnamespace, "
1933
						  "(%s oprowner) as rolname, "
1934
						  "oprcode::oid as oprcode "
1935 1936
						  "FROM pg_operator",
						  username_subquery);
1937 1938 1939
	}
	else
	{
1940 1941 1942
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_operator') AS tableoid, "
						  "oid, oprname, "
1943
						  "0::oid as oprnamespace, "
1944
						  "(%s oprowner) as rolname, "
1945
						  "oprcode::oid as oprcode "
1946 1947
						  "FROM pg_operator",
						  username_subquery);
1948
	}
1949

Bruce Momjian's avatar
Bruce Momjian committed
1950
	res = PQexec(g_conn, query->data);
1951
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1952 1953

	ntups = PQntuples(res);
1954
	*numOprs = ntups;
1955

1956
	oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
1957

1958
	i_tableoid = PQfnumber(res, "tableoid");
1959
	i_oid = PQfnumber(res, "oid");
1960 1961
	i_oprname = PQfnumber(res, "oprname");
	i_oprnamespace = PQfnumber(res, "oprnamespace");
1962
	i_rolname = PQfnumber(res, "rolname");
1963
	i_oprcode = PQfnumber(res, "oprcode");
1964 1965 1966

	for (i = 0; i < ntups; i++)
	{
1967 1968 1969 1970
		oprinfo[i].dobj.objType = DO_OPERATOR;
		oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&oprinfo[i].dobj);
1971 1972
		oprinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_oprname));
		oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
1973
											  oprinfo[i].dobj.catId.oid);
1974
		oprinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1975
		oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
1976

1977
		if (strlen(oprinfo[i].rolname) == 0)
1978
			write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
1979
					  oprinfo[i].dobj.name);
1980 1981 1982 1983
	}

	PQclear(res);

1984
	destroyPQExpBuffer(query);
1985

1986
	return oprinfo;
1987 1988
}

1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002
/*
 * getConversions:
 *	  read all conversions in the system catalogs and return them in the
 * ConvInfo* structure
 *
 *	numConversions is set to the number of conversions read in
 */
ConvInfo *
getConversions(int *numConversions)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
2003
	ConvInfo   *convinfo;
2004
	int			i_tableoid;
2005 2006 2007
	int			i_oid;
	int			i_conname;
	int			i_connamespace;
2008
	int			i_rolname;
2009 2010

	/* Conversions didn't exist pre-7.3 */
Bruce Momjian's avatar
Bruce Momjian committed
2011 2012
	if (g_fout->remoteVersion < 70300)
	{
2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
		*numConversions = 0;
		return NULL;
	}

	/*
	 * find all conversions, including builtin conversions; we filter out
	 * system-defined conversions at dump-out time.
	 */

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

2025
	appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
2026
					  "connamespace, "
2027 2028 2029
					  "(%s conowner) as rolname "
					  "FROM pg_conversion",
					  username_subquery);
2030 2031

	res = PQexec(g_conn, query->data);
2032
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2033 2034 2035 2036 2037 2038

	ntups = PQntuples(res);
	*numConversions = ntups;

	convinfo = (ConvInfo *) malloc(ntups * sizeof(ConvInfo));

2039
	i_tableoid = PQfnumber(res, "tableoid");
2040 2041 2042
	i_oid = PQfnumber(res, "oid");
	i_conname = PQfnumber(res, "conname");
	i_connamespace = PQfnumber(res, "connamespace");
2043
	i_rolname = PQfnumber(res, "rolname");
2044 2045 2046

	for (i = 0; i < ntups; i++)
	{
2047 2048 2049 2050
		convinfo[i].dobj.objType = DO_CONVERSION;
		convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&convinfo[i].dobj);
2051 2052
		convinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2053
											 convinfo[i].dobj.catId.oid);
2054
		convinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2055 2056 2057 2058 2059 2060 2061 2062 2063
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return convinfo;
}

2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077
/*
 * getOpclasses:
 *	  read all opclasses in the system catalogs and return them in the
 * OpclassInfo* structure
 *
 *	numOpclasses is set to the number of opclasses read in
 */
OpclassInfo *
getOpclasses(int *numOpclasses)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
2078
	OpclassInfo *opcinfo;
2079
	int			i_tableoid;
2080 2081 2082
	int			i_oid;
	int			i_opcname;
	int			i_opcnamespace;
2083
	int			i_rolname;
2084 2085

	/*
Bruce Momjian's avatar
Bruce Momjian committed
2086 2087
	 * find all opclasses, including builtin opclasses; we filter out
	 * system-defined opclasses at dump-out time.
2088 2089 2090 2091 2092 2093 2094
	 */

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
	{
2095
		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
2096
						  "opcnamespace, "
2097 2098 2099
						  "(%s opcowner) as rolname "
						  "FROM pg_opclass",
						  username_subquery);
2100 2101 2102 2103 2104
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
						  "0::oid as opcnamespace, "
2105
						  "''::name as rolname "
2106
						  "FROM pg_opclass");
2107 2108 2109
	}
	else
	{
2110 2111 2112
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_opclass') AS tableoid, "
						  "oid, opcname, "
2113
						  "0::oid as opcnamespace, "
2114
						  "''::name as rolname "
2115
						  "FROM pg_opclass");
2116 2117 2118
	}

	res = PQexec(g_conn, query->data);
2119
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2120 2121 2122 2123 2124 2125

	ntups = PQntuples(res);
	*numOpclasses = ntups;

	opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));

2126
	i_tableoid = PQfnumber(res, "tableoid");
2127 2128 2129
	i_oid = PQfnumber(res, "oid");
	i_opcname = PQfnumber(res, "opcname");
	i_opcnamespace = PQfnumber(res, "opcnamespace");
2130
	i_rolname = PQfnumber(res, "rolname");
2131 2132 2133

	for (i = 0; i < ntups; i++)
	{
2134 2135 2136 2137
		opcinfo[i].dobj.objType = DO_OPCLASS;
		opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&opcinfo[i].dobj);
2138 2139
		opcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_opcname));
		opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2140
											  opcinfo[i].dobj.catId.oid);
2141
		opcinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2142 2143 2144

		if (g_fout->remoteVersion >= 70300)
		{
2145
			if (strlen(opcinfo[i].rolname) == 0)
2146
				write_msg(NULL, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
2147
						  opcinfo[i].dobj.name);
2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return opcinfo;
}

2158
/*
2159 2160 2161
 * getAggregates:
 *	  read all the user-defined aggregates in the system catalogs and
 * return them in the AggInfo* structure
2162
 *
2163
 * numAggs is set to the number of aggregates read in
2164
 */
2165 2166
AggInfo *
getAggregates(int *numAggs)
2167
{
2168
	PGresult   *res;
2169 2170
	int			ntups;
	int			i;
2171
	PQExpBuffer query = createPQExpBuffer();
2172
	AggInfo    *agginfo;
2173
	int			i_tableoid;
2174 2175 2176
	int			i_oid;
	int			i_aggname;
	int			i_aggnamespace;
2177
	int			i_aggbasetype;
2178
	int			i_rolname;
2179
	int			i_aggacl;
2180

2181 2182
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
2183

2184 2185 2186
	/* find all user-defined aggregates */

	if (g_fout->remoteVersion >= 70300)
2187
	{
2188
		appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
2189
						  "pronamespace as aggnamespace, "
2190
						  "proargtypes[0] as aggbasetype, "
2191
						  "(%s proowner) as rolname, "
2192
						  "proacl as aggacl "
2193 2194 2195
						  "FROM pg_proc "
						  "WHERE proisagg "
						  "AND pronamespace != "
2196 2197
		  "(select oid from pg_namespace where nspname = 'pg_catalog')",
						  username_subquery);
2198
	}
2199 2200 2201 2202 2203
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, aggname, "
						  "0::oid as aggnamespace, "
						  "aggbasetype, "
2204
						  "(%s aggowner) as rolname, "
2205 2206 2207
						  "'{=X}' as aggacl "
						  "FROM pg_aggregate "
						  "where oid > '%u'::oid",
2208
						  username_subquery,
2209 2210
						  g_last_builtin_oid);
	}
2211 2212
	else
	{
2213 2214 2215
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_aggregate') AS tableoid, "
						  "oid, aggname, "
2216
						  "0::oid as aggnamespace, "
2217
						  "aggbasetype, "
2218
						  "(%s aggowner) as rolname, "
2219
						  "'{=X}' as aggacl "
2220
						  "FROM pg_aggregate "
2221
						  "where oid > '%u'::oid",
2222
						  username_subquery,
2223
						  g_last_builtin_oid);
2224
	}
2225

Bruce Momjian's avatar
Bruce Momjian committed
2226
	res = PQexec(g_conn, query->data);
2227
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2228 2229

	ntups = PQntuples(res);
2230
	*numAggs = ntups;
2231

2232
	agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
2233

2234
	i_tableoid = PQfnumber(res, "tableoid");
2235 2236 2237
	i_oid = PQfnumber(res, "oid");
	i_aggname = PQfnumber(res, "aggname");
	i_aggnamespace = PQfnumber(res, "aggnamespace");
2238
	i_aggbasetype = PQfnumber(res, "aggbasetype");
2239
	i_rolname = PQfnumber(res, "rolname");
2240
	i_aggacl = PQfnumber(res, "aggacl");
2241 2242 2243

	for (i = 0; i < ntups; i++)
	{
2244 2245 2246 2247
		agginfo[i].aggfn.dobj.objType = DO_AGG;
		agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&agginfo[i].aggfn.dobj);
2248 2249
		agginfo[i].aggfn.dobj.name = strdup(PQgetvalue(res, i, i_aggname));
		agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2250
										agginfo[i].aggfn.dobj.catId.oid);
2251 2252
		agginfo[i].aggfn.rolname = strdup(PQgetvalue(res, i, i_rolname));
		if (strlen(agginfo[i].aggfn.rolname) == 0)
2253
			write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
2254
					  agginfo[i].aggfn.dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
2255 2256
		agginfo[i].aggfn.lang = InvalidOid;		/* not currently
												 * interesting */
2257 2258 2259
		agginfo[i].aggfn.nargs = 1;
		agginfo[i].aggfn.argtypes = (Oid *) malloc(sizeof(Oid));
		agginfo[i].aggfn.argtypes[0] = atooid(PQgetvalue(res, i, i_aggbasetype));
Bruce Momjian's avatar
Bruce Momjian committed
2260
		agginfo[i].aggfn.prorettype = InvalidOid;		/* not saved */
2261
		agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
2262
		agginfo[i].anybasetype = false; /* computed when it's dumped */
Bruce Momjian's avatar
Bruce Momjian committed
2263
		agginfo[i].fmtbasetype = NULL;	/* computed when it's dumped */
2264
	}
2265

2266
	PQclear(res);
2267

2268
	destroyPQExpBuffer(query);
2269

2270 2271
	return agginfo;
}
2272

2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287
/*
 * getFuncs:
 *	  read all the user-defined functions in the system catalogs and
 * return them in the FuncInfo* structure
 *
 * numFuncs is set to the number of functions read in
 */
FuncInfo *
getFuncs(int *numFuncs)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
	FuncInfo   *finfo;
2288
	int			i_tableoid;
2289 2290 2291
	int			i_oid;
	int			i_proname;
	int			i_pronamespace;
2292
	int			i_rolname;
2293 2294 2295 2296
	int			i_prolang;
	int			i_pronargs;
	int			i_proargtypes;
	int			i_prorettype;
2297
	int			i_proacl;
2298
	int         i_is_pl_handler;
2299

2300 2301
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
Bruce Momjian's avatar
Bruce Momjian committed
2302

2303
	/* find all user-defined funcs */
2304

2305 2306
	if (g_fout->remoteVersion >= 70300)
	{
2307 2308 2309 2310 2311
		/*
		 * We now collect info on pg_catalog resident functions, but
		 * only if they are language call handlers or validators, and
		 * only for non-default languages (i.e. not internal/C/SQL).
		 */
2312
		appendPQExpBuffer(query,
2313
						  "SELECT tableoid, oid, proname, prolang, "
2314
						  "pronargs, proargtypes, prorettype, proacl, "
2315
						  "pronamespace, "
2316
						  "(%s proowner) as rolname, "
2317 2318 2319 2320 2321 2322 2323
						  "CASE WHEN oid IN "
						  "  (select lanplcallfoid from pg_language "
						  "   where lanplcallfoid != 0) THEN true "
						  " WHEN oid IN "
						  "  (select lanvalidator from pg_language "
						  "   where lanplcallfoid != 0) THEN true "
						  " ELSE false END AS is_pl_handler "
2324 2325
						  "FROM pg_proc "
						  "WHERE NOT proisagg "
2326 2327 2328 2329 2330 2331 2332 2333
						  "AND (pronamespace != "
						  "    (select oid from pg_namespace "
						  "     where nspname = 'pg_catalog')"
						  "  OR oid IN "
						  "    (select lanplcallfoid from pg_language "
						  "     where lanplcallfoid != 0) "
						  "  OR oid IN "
						  "    (select lanvalidator from pg_language "
2334 2335
						  "     where lanplcallfoid != 0))",
						  username_subquery);
2336
	}
2337 2338 2339 2340 2341 2342 2343
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, proname, prolang, "
						  "pronargs, proargtypes, prorettype, "
						  "'{=X}' as proacl, "
						  "0::oid as pronamespace, "
2344
						  "(%s proowner) as rolname, "
2345
						  "false AS is_pl_handler "
2346 2347
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
2348
						  username_subquery,
2349 2350
						  g_last_builtin_oid);
	}
2351 2352 2353
	else
	{
		appendPQExpBuffer(query,
2354
						  "SELECT "
2355 2356
						  "(SELECT oid FROM pg_class "
						  " WHERE relname = 'pg_proc') AS tableoid, "
2357
						  "oid, proname, prolang, "
2358
						  "pronargs, proargtypes, prorettype, "
2359
						  "'{=X}' as proacl, "
2360
						  "0::oid as pronamespace, "
2361
						  "(%s proowner) as rolname, "
2362
						  "false AS is_pl_handler "
2363 2364
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
2365
						  username_subquery,
2366 2367
						  g_last_builtin_oid);
	}
2368

2369
	res = PQexec(g_conn, query->data);
2370
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2371

2372
	ntups = PQntuples(res);
Bruce Momjian's avatar
Bruce Momjian committed
2373

2374
	*numFuncs = ntups;
2375

2376
	finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
2377

2378
	i_tableoid = PQfnumber(res, "tableoid");
2379 2380 2381
	i_oid = PQfnumber(res, "oid");
	i_proname = PQfnumber(res, "proname");
	i_pronamespace = PQfnumber(res, "pronamespace");
2382
	i_rolname = PQfnumber(res, "rolname");
2383 2384 2385 2386
	i_prolang = PQfnumber(res, "prolang");
	i_pronargs = PQfnumber(res, "pronargs");
	i_proargtypes = PQfnumber(res, "proargtypes");
	i_prorettype = PQfnumber(res, "prorettype");
2387
	i_proacl = PQfnumber(res, "proacl");
2388
	i_is_pl_handler = PQfnumber(res,"is_pl_handler");
2389

2390 2391
	for (i = 0; i < ntups; i++)
	{
2392 2393 2394 2395
		finfo[i].dobj.objType = DO_FUNC;
		finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&finfo[i].dobj);
2396
		finfo[i].dobj.name = strdup(PQgetvalue(res, i, i_proname));
2397 2398
		finfo[i].dobj.namespace = 
			findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2399
												finfo[i].dobj.catId.oid);
2400
		finfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2401
		finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
2402
		finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
2403
		finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
2404
		finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
2405 2406
		finfo[i].isProlangFunc = 
			strcmp(PQgetvalue(res, i, i_is_pl_handler), "t") == 0;
2407 2408
		if (finfo[i].nargs == 0)
			finfo[i].argtypes = NULL;
2409
		else
2410
		{
2411 2412 2413
			finfo[i].argtypes = (Oid *) malloc(finfo[i].nargs * sizeof(Oid));
			parseOidArray(PQgetvalue(res, i, i_proargtypes),
						  finfo[i].argtypes, finfo[i].nargs);
2414
		}
2415

2416
		if (strlen(finfo[i].rolname) == 0)
2417 2418
			write_msg(NULL, 
					  "WARNING: owner of function \"%s\" appears to be invalid\n",
2419
					  finfo[i].dobj.name);
2420
	}
2421

2422
	PQclear(res);
2423

2424
	destroyPQExpBuffer(query);
2425

2426 2427
	return finfo;
}
2428

2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445
/*
 * getTables
 *	  read all the user-defined tables (no indexes, no catalogs)
 * in the system catalogs return them in the TableInfo* structure
 *
 * numTables is set to the number of tables read in
 */
TableInfo *
getTables(int *numTables)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
	PQExpBuffer delqry = createPQExpBuffer();
	PQExpBuffer lockquery = createPQExpBuffer();
	TableInfo  *tblinfo;
2446
	int			i_reltableoid;
2447 2448 2449 2450 2451
	int			i_reloid;
	int			i_relname;
	int			i_relnamespace;
	int			i_relkind;
	int			i_relacl;
2452
	int			i_rolname;
2453 2454 2455 2456 2457
	int			i_relchecks;
	int			i_reltriggers;
	int			i_relhasindex;
	int			i_relhasrules;
	int			i_relhasoids;
2458 2459
	int			i_owning_tab;
	int			i_owning_col;
2460
	int			i_reltablespace;
2461

2462 2463
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
2464

2465 2466 2467
	/*
	 * Find all the tables (including views and sequences).
	 *
Bruce Momjian's avatar
Bruce Momjian committed
2468 2469
	 * We include system catalogs, so that we can work if a user table is
	 * defined to inherit from a system catalog (pretty weird, but...)
2470
	 *
2471 2472 2473 2474 2475 2476 2477
	 * We ignore tables that are not type 'r' (ordinary relation), 'S'
	 * (sequence), 'v' (view), or 'c' (composite type).
	 *
	 * Composite-type table entries won't be dumped as such, but we have
	 * to make a DumpableObject for them so that we can track dependencies
	 * of the composite type (pg_depend entries for columns of the composite
	 * type link to the pg_class entry not the pg_type entry).
2478 2479
	 *
	 * Note: in this phase we should collect only a minimal amount of
Bruce Momjian's avatar
Bruce Momjian committed
2480 2481
	 * information about each table, basically just enough to decide if it
	 * is interesting.	We must fetch all tables in this phase because
2482 2483
	 * otherwise we cannot correctly identify inherited columns, serial
	 * columns, etc.
2484
	 */
2485

2486
	if (g_fout->remoteVersion >= 80000)
2487
	{
2488 2489 2490 2491
		/*
		 * Left join to pick up dependency info linking sequences to their
		 * serial column, if any
		 */
2492
		appendPQExpBuffer(query,
2493 2494
						  "SELECT c.tableoid, c.oid, relname, "
						  "relacl, relkind, relnamespace, "
2495
						  "(%s relowner) as rolname, "
2496
						  "relchecks, reltriggers, "
2497 2498
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
2499 2500 2501 2502 2503 2504 2505 2506
						  "d.refobjsubid as owning_col, "
						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace "
						  "from pg_class c "
						  "left join pg_depend d on "
						  "(c.relkind = '%c' and "
						"d.classid = c.tableoid and d.objid = c.oid and "
						  "d.objsubid = 0 and "
						"d.refclassid = c.tableoid and d.deptype = 'i') "
2507
						  "where relkind in ('%c', '%c', '%c', '%c') "
2508
						  "order by c.oid",
2509
						  username_subquery,
2510
						  RELKIND_SEQUENCE,
2511 2512
						  RELKIND_RELATION, RELKIND_SEQUENCE,
						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
2513 2514 2515 2516 2517 2518 2519 2520 2521 2522
	}
	else if (g_fout->remoteVersion >= 70300)
	{
		/*
		 * Left join to pick up dependency info linking sequences to their
		 * serial column, if any
		 */
		appendPQExpBuffer(query,
						  "SELECT c.tableoid, c.oid, relname, "
						  "relacl, relkind, relnamespace, "
2523
						  "(%s relowner) as rolname, "
2524 2525 2526 2527 2528
						  "relchecks, reltriggers, "
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
						  "d.refobjsubid as owning_col, "
						  "NULL as reltablespace "
2529 2530 2531
						  "from pg_class c "
						  "left join pg_depend d on "
						  "(c.relkind = '%c' and "
Bruce Momjian's avatar
Bruce Momjian committed
2532
						"d.classid = c.tableoid and d.objid = c.oid and "
2533
						  "d.objsubid = 0 and "
Bruce Momjian's avatar
Bruce Momjian committed
2534
						"d.refclassid = c.tableoid and d.deptype = 'i') "
2535
						  "where relkind in ('%c', '%c', '%c', '%c') "
2536
						  "order by c.oid",
2537
						  username_subquery,
2538
						  RELKIND_SEQUENCE,
2539 2540
						  RELKIND_RELATION, RELKIND_SEQUENCE,
						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
2541 2542 2543 2544
	}
	else if (g_fout->remoteVersion >= 70200)
	{
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
2545
					   "SELECT tableoid, oid, relname, relacl, relkind, "
2546
						  "0::oid as relnamespace, "
2547
						  "(%s relowner) as rolname, "
2548
						  "relchecks, reltriggers, "
2549 2550
						  "relhasindex, relhasrules, relhasoids, "
						  "NULL::oid as owning_tab, "
2551 2552
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2553 2554 2555
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
2556 2557
						  username_subquery,
						  RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2558 2559 2560 2561 2562
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* all tables have oids in 7.1 */
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
2563
					   "SELECT tableoid, oid, relname, relacl, relkind, "
2564
						  "0::oid as relnamespace, "
2565
						  "(%s relowner) as rolname, "
2566
						  "relchecks, reltriggers, "
2567 2568 2569
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2570 2571
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2572 2573 2574
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
2575 2576
						  username_subquery,
						  RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2577 2578 2579 2580 2581 2582 2583 2584
	}
	else
	{
		/*
		 * Before 7.1, view relkind was not set to 'v', so we must check
		 * if we have a view by looking for a rule in pg_rewrite.
		 */
		appendPQExpBuffer(query,
2585 2586 2587
						  "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
						  "oid, relname, relacl, "
2588 2589 2590 2591 2592 2593
						  "CASE WHEN relhasrules and relkind = 'r' "
				  "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
				  "             r.ev_class = c.oid AND r.ev_type = '1') "
						  "THEN '%c'::\"char\" "
						  "ELSE relkind END AS relkind,"
						  "0::oid as relnamespace, "
2594
						  "(%s relowner) as rolname, "
2595
						  "relchecks, reltriggers, "
2596 2597 2598
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2599 2600
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2601 2602 2603 2604
						  "from pg_class c "
						  "where relkind in ('%c', '%c') "
						  "order by oid",
						  RELKIND_VIEW,
2605
						  username_subquery,
2606 2607
						  RELKIND_RELATION, RELKIND_SEQUENCE);
	}
2608

2609
	res = PQexec(g_conn, query->data);
2610
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2611

2612
	ntups = PQntuples(res);
2613

2614
	*numTables = ntups;
2615

2616 2617 2618 2619 2620 2621 2622 2623 2624
	/*
	 * Extract data from result and lock dumpable tables.  We do the
	 * locking before anything else, to minimize the window wherein a
	 * table could disappear under us.
	 *
	 * Note that we have to save info about all tables here, even when
	 * dumping only one, because we don't yet know which tables might be
	 * inheritance ancestors of the target table.
	 */
2625
	tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
2626

2627
	i_reltableoid = PQfnumber(res, "tableoid");
2628 2629 2630 2631 2632
	i_reloid = PQfnumber(res, "oid");
	i_relname = PQfnumber(res, "relname");
	i_relnamespace = PQfnumber(res, "relnamespace");
	i_relacl = PQfnumber(res, "relacl");
	i_relkind = PQfnumber(res, "relkind");
2633
	i_rolname = PQfnumber(res, "rolname");
2634 2635 2636 2637 2638
	i_relchecks = PQfnumber(res, "relchecks");
	i_reltriggers = PQfnumber(res, "reltriggers");
	i_relhasindex = PQfnumber(res, "relhasindex");
	i_relhasrules = PQfnumber(res, "relhasrules");
	i_relhasoids = PQfnumber(res, "relhasoids");
2639 2640
	i_owning_tab = PQfnumber(res, "owning_tab");
	i_owning_col = PQfnumber(res, "owning_col");
2641
	i_reltablespace = PQfnumber(res, "reltablespace");
2642

2643 2644
	for (i = 0; i < ntups; i++)
	{
2645 2646 2647 2648
		tblinfo[i].dobj.objType = DO_TABLE;
		tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
		tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
		AssignDumpId(&tblinfo[i].dobj);
2649 2650
		tblinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_relname));
		tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2651
											  tblinfo[i].dobj.catId.oid);
2652
		tblinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2653 2654 2655 2656 2657 2658 2659
		tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
		tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
		tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
		tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
		tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
		tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
		tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
2660 2661
		if (PQgetisnull(res, i, i_owning_tab))
		{
2662
			tblinfo[i].owning_tab = InvalidOid;
2663 2664 2665 2666
			tblinfo[i].owning_col = 0;
		}
		else
		{
2667
			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
2668 2669
			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
		}
2670
		tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
2671

2672
		/* other fields were zeroed above */
Bruce Momjian's avatar
Bruce Momjian committed
2673

2674
		/*
Bruce Momjian's avatar
Bruce Momjian committed
2675 2676
		 * Decide whether we want to dump this table.  Sequences owned by
		 * serial columns are never dumpable on their own; we will
2677
		 * transpose their owning table's dump flag to them below.
2678
		 */
2679 2680 2681
		if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
			tblinfo[i].dump = false;
		else if (OidIsValid(tblinfo[i].owning_tab))
2682
			tblinfo[i].dump = false;
2683 2684
		else
			selectDumpableTable(&tblinfo[i]);
2685
		tblinfo[i].interesting = tblinfo[i].dump;
2686

2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
		/*
		 * Read-lock target tables to make sure they aren't DROPPED or
		 * altered in schema before we get around to dumping them.
		 *
		 * Note that we don't explicitly lock parents of the target tables;
		 * we assume our lock on the child is enough to prevent schema
		 * alterations to parent tables.
		 *
		 * NOTE: it'd be kinda nice to lock views and sequences too, not only
		 * plain tables, but the backend doesn't presently allow that.
		 */
		if (tblinfo[i].dump && tblinfo[i].relkind == RELKIND_RELATION)
		{
			resetPQExpBuffer(lockquery);
			appendPQExpBuffer(lockquery,
							  "LOCK TABLE %s IN ACCESS SHARE MODE",
Bruce Momjian's avatar
Bruce Momjian committed
2703 2704
					 fmtQualifiedId(tblinfo[i].dobj.namespace->dobj.name,
									tblinfo[i].dobj.name));
2705
			do_sql_command(g_conn, lockquery->data);
2706
		}
2707

2708
		/* Emit notice if join for owner failed */
2709
		if (strlen(tblinfo[i].rolname) == 0)
2710
			write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
2711
					  tblinfo[i].dobj.name);
2712 2713
	}

Bruce Momjian's avatar
Bruce Momjian committed
2714
	/*
Bruce Momjian's avatar
Bruce Momjian committed
2715
	 * If the user is attempting to dump a specific table, check to ensure
Bruce Momjian's avatar
Bruce Momjian committed
2716 2717 2718
	 * that the specified table actually exists.  (This is a bit
	 * simplistic since we don't fully check the combination of -n and -t
	 * switches.)
Bruce Momjian's avatar
Bruce Momjian committed
2719 2720 2721 2722
	 */
	if (selectTableName)
	{
		for (i = 0; i < ntups; i++)
2723
			if (strcmp(tblinfo[i].dobj.name, selectTableName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
2724 2725 2726 2727 2728
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
2729
			write_msg(NULL, "specified table \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
2730 2731 2732 2733 2734
					  selectTableName);
			exit_nicely();
		}
	}

2735
	PQclear(res);
2736 2737
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
2738
	destroyPQExpBuffer(lockquery);
2739

2740
	return tblinfo;
2741 2742 2743 2744
}

/*
 * getInherits
2745
 *	  read all the inheritance information
2746 2747
 * from the system catalogs return them in the InhInfo* structure
 *
2748
 * numInherits is set to the number of pairs read in
2749
 */
2750
InhInfo *
2751 2752
getInherits(int *numInherits)
{
2753
	PGresult   *res;
2754 2755
	int			ntups;
	int			i;
2756 2757
	PQExpBuffer query = createPQExpBuffer();
	InhInfo    *inhinfo;
2758

2759
	int			i_inhrelid;
2760
	int			i_inhparent;
2761

2762 2763 2764
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

2765 2766
	/* find all the inheritance information */

Bruce Momjian's avatar
Bruce Momjian committed
2767
	appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
2768

Bruce Momjian's avatar
Bruce Momjian committed
2769
	res = PQexec(g_conn, query->data);
2770
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2771 2772 2773 2774 2775 2776 2777

	ntups = PQntuples(res);

	*numInherits = ntups;

	inhinfo = (InhInfo *) malloc(ntups * sizeof(InhInfo));

2778
	i_inhrelid = PQfnumber(res, "inhrelid");
2779 2780 2781 2782
	i_inhparent = PQfnumber(res, "inhparent");

	for (i = 0; i < ntups; i++)
	{
2783 2784
		inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
		inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
2785 2786 2787
	}

	PQclear(res);
2788 2789 2790

	destroyPQExpBuffer(query);

2791
	return inhinfo;
2792 2793 2794
}

/*
2795 2796
 * getIndexes
 *	  get information about every index on a dumpable table
2797
 *
2798 2799
 * Note: index data is not returned directly to the caller, but it
 * does get entered into the DumpableObject tables.
2800 2801
 */
void
2802
getIndexes(TableInfo tblinfo[], int numTables)
2803
{
2804
	int			i,
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819
				j;
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	IndxInfo   *indxinfo;
	ConstraintInfo *constrinfo;
	int			i_tableoid,
				i_oid,
				i_indexname,
				i_indexdef,
				i_indnkeys,
				i_indkey,
				i_indisclustered,
				i_contype,
				i_conname,
				i_contableoid,
2820 2821
				i_conoid,
				i_tablespace;
2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836
	int			ntups;

	for (i = 0; i < numTables; i++)
	{
		TableInfo  *tbinfo = &tblinfo[i];

		/* Only plain tables have indexes */
		if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex)
			continue;

		if (!tbinfo->dump)
			continue;

		if (g_verbose)
			write_msg(NULL, "reading indexes for table \"%s\"\n",
2837
					  tbinfo->dobj.name);
2838 2839

		/* Make sure we are in proper schema so indexdef is right */
2840
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
2841 2842 2843 2844

		/*
		 * The point of the messy-looking outer join is to find a
		 * constraint that is related by an internal dependency link to
Bruce Momjian's avatar
Bruce Momjian committed
2845 2846 2847
		 * the index. If we find one, create a CONSTRAINT entry linked to
		 * the INDEX entry.  We assume an index won't have more than one
		 * internal dependency.
2848 2849
		 */
		resetPQExpBuffer(query);
2850
		if (g_fout->remoteVersion >= 80000)
2851 2852 2853 2854 2855 2856 2857 2858 2859
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
				 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
2860
							  "c.oid as conoid, "
Bruce Momjian's avatar
Bruce Momjian committed
2861
							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace "
2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886
							  "FROM pg_catalog.pg_index i "
				  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
							  "LEFT JOIN pg_catalog.pg_depend d "
							  "ON (d.classid = t.tableoid "
							  "AND d.objid = t.oid "
							  "AND d.deptype = 'i') "
							  "LEFT JOIN pg_catalog.pg_constraint c "
							  "ON (d.refclassid = c.tableoid "
							  "AND d.refobjid = c.oid) "
							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70300)
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
				 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
							  "c.oid as conoid, "
							  "NULL as tablespace "
2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904
							  "FROM pg_catalog.pg_index i "
				  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
							  "LEFT JOIN pg_catalog.pg_depend d "
							  "ON (d.classid = t.tableoid "
							  "AND d.objid = t.oid "
							  "AND d.deptype = 'i') "
							  "LEFT JOIN pg_catalog.pg_constraint c "
							  "ON (d.refclassid = c.tableoid "
							  "AND d.refobjid = c.oid) "
							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70100)
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
Bruce Momjian's avatar
Bruce Momjian committed
2905
							"pg_get_indexdef(i.indexrelid) as indexdef, "
2906 2907 2908 2909 2910 2911
							  "t.relnatts as indnkeys, "
							  "i.indkey, false as indisclustered, "
							  "CASE WHEN i.indisprimary THEN 'p'::char "
							  "ELSE '0'::char END as contype, "
							  "t.relname as conname, "
							  "0::oid as contableoid, "
2912 2913
							  "t.oid as conoid, "
							  "NULL as tablespace "
2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926
							  "FROM pg_index i, pg_class t "
							  "WHERE t.oid = i.indexrelid "
							  "AND i.indrelid = '%u'::oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else
		{
			appendPQExpBuffer(query,
							  "SELECT "
							  "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
							  "t.oid, "
							  "t.relname as indexname, "
Bruce Momjian's avatar
Bruce Momjian committed
2927
							"pg_get_indexdef(i.indexrelid) as indexdef, "
2928 2929 2930 2931 2932 2933
							  "t.relnatts as indnkeys, "
							  "i.indkey, false as indisclustered, "
							  "CASE WHEN i.indisprimary THEN 'p'::char "
							  "ELSE '0'::char END as contype, "
							  "t.relname as conname, "
							  "0::oid as contableoid, "
2934 2935
							  "t.oid as conoid, "
							  "NULL as tablespace "
2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958
							  "FROM pg_index i, pg_class t "
							  "WHERE t.oid = i.indexrelid "
							  "AND i.indrelid = '%u'::oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}

		res = PQexec(g_conn, query->data);
		check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

		ntups = PQntuples(res);

		i_tableoid = PQfnumber(res, "tableoid");
		i_oid = PQfnumber(res, "oid");
		i_indexname = PQfnumber(res, "indexname");
		i_indexdef = PQfnumber(res, "indexdef");
		i_indnkeys = PQfnumber(res, "indnkeys");
		i_indkey = PQfnumber(res, "indkey");
		i_indisclustered = PQfnumber(res, "indisclustered");
		i_contype = PQfnumber(res, "contype");
		i_conname = PQfnumber(res, "conname");
		i_contableoid = PQfnumber(res, "contableoid");
		i_conoid = PQfnumber(res, "conoid");
2959
		i_tablespace = PQfnumber(res, "tablespace");
2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971

		indxinfo = (IndxInfo *) malloc(ntups * sizeof(IndxInfo));
		constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));

		for (j = 0; j < ntups; j++)
		{
			char		contype;

			indxinfo[j].dobj.objType = DO_INDEX;
			indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
			indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
			AssignDumpId(&indxinfo[j].dobj);
2972 2973
			indxinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_indexname));
			indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
2974 2975 2976
			indxinfo[j].indextable = tbinfo;
			indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
			indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
2977
			indxinfo[j].tablespace = strdup(PQgetvalue(res, j, i_tablespace));
2978

2979 2980 2981
			/*
			 * In pre-7.4 releases, indkeys may contain more entries than
			 * indnkeys says (since indnkeys will be 1 for a functional
Bruce Momjian's avatar
Bruce Momjian committed
2982 2983 2984 2985 2986
			 * index).	We don't actually care about this case since we
			 * don't examine indkeys except for indexes associated with
			 * PRIMARY and UNIQUE constraints, which are never functional
			 * indexes. But we have to allocate enough space to keep
			 * parseOidArray from complaining.
2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006
			 */
			indxinfo[j].indkeys = (Oid *) malloc(INDEX_MAX_KEYS * sizeof(Oid));
			parseOidArray(PQgetvalue(res, j, i_indkey),
						  indxinfo[j].indkeys, INDEX_MAX_KEYS);
			indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
			contype = *(PQgetvalue(res, j, i_contype));

			if (contype == 'p' || contype == 'u')
			{
				/*
				 * If we found a constraint matching the index, create an
				 * entry for it.
				 *
				 * In a pre-7.3 database, we take this path iff the index was
				 * marked indisprimary.
				 */
				constrinfo[j].dobj.objType = DO_CONSTRAINT;
				constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
				constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
				AssignDumpId(&constrinfo[j].dobj);
3007 3008
				constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
				constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3009 3010 3011 3012 3013 3014 3015
				constrinfo[j].contable = tbinfo;
				constrinfo[j].condomain = NULL;
				constrinfo[j].contype = contype;
				constrinfo[j].condef = NULL;
				constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
				constrinfo[j].coninherited = false;
				constrinfo[j].separate = true;
3016

3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073
				indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;

				/* If pre-7.3 DB, better make sure table comes first */
				addObjectDependency(&constrinfo[j].dobj,
									tbinfo->dobj.dumpId);
			}
			else
			{
				/* Plain secondary index */
				indxinfo[j].indexconstraint = 0;
			}
		}

		PQclear(res);
	}

	destroyPQExpBuffer(query);
}

/*
 * getConstraints
 *
 * Get info about constraints on dumpable tables.
 *
 * Currently handles foreign keys only.
 * Unique and primary key constraints are handled with indexes,
 * while check constraints are processed in getTableAttrs().
 */
void
getConstraints(TableInfo tblinfo[], int numTables)
{
	int			i,
				j;
	ConstraintInfo *constrinfo;
	PQExpBuffer query;
	PGresult   *res;
	int			i_condef,
				i_contableoid,
				i_conoid,
				i_conname;
	int			ntups;

	/* pg_constraint was created in 7.3, so nothing to do if older */
	if (g_fout->remoteVersion < 70300)
		return;

	query = createPQExpBuffer();

	for (i = 0; i < numTables; i++)
	{
		TableInfo  *tbinfo = &tblinfo[i];

		if (tbinfo->ntrig == 0 || !tbinfo->dump)
			continue;

		if (g_verbose)
			write_msg(NULL, "reading foreign key constraints for table \"%s\"\n",
3074
					  tbinfo->dobj.name);
3075 3076 3077 3078 3079

		/*
		 * select table schema to ensure constraint expr is qualified if
		 * needed
		 */
3080
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3081 3082 3083 3084

		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
3085
						"pg_catalog.pg_get_constraintdef(oid) as condef "
3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107
						  "FROM pg_catalog.pg_constraint "
						  "WHERE conrelid = '%u'::pg_catalog.oid "
						  "AND contype = 'f'",
						  tbinfo->dobj.catId.oid);
		res = PQexec(g_conn, query->data);
		check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

		ntups = PQntuples(res);

		i_contableoid = PQfnumber(res, "tableoid");
		i_conoid = PQfnumber(res, "oid");
		i_conname = PQfnumber(res, "conname");
		i_condef = PQfnumber(res, "condef");

		constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));

		for (j = 0; j < ntups; j++)
		{
			constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
			constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
			constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
			AssignDumpId(&constrinfo[j].dobj);
3108 3109
			constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
			constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147
			constrinfo[j].contable = tbinfo;
			constrinfo[j].condomain = NULL;
			constrinfo[j].contype = 'f';
			constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
			constrinfo[j].conindex = 0;
			constrinfo[j].coninherited = false;
			constrinfo[j].separate = true;
		}

		PQclear(res);
	}

	destroyPQExpBuffer(query);
}

/*
 * getDomainConstraints
 *
 * Get info about constraints on a domain.
 */
static void
getDomainConstraints(TypeInfo *tinfo)
{
	int			i;
	ConstraintInfo *constrinfo;
	PQExpBuffer query;
	PGresult   *res;
	int			i_tableoid,
				i_oid,
				i_conname,
				i_consrc;
	int			ntups;

	/* pg_constraint was created in 7.3, so nothing to do if older */
	if (g_fout->remoteVersion < 70300)
		return;

	/*
Bruce Momjian's avatar
Bruce Momjian committed
3148 3149
	 * select appropriate schema to ensure names in constraint are
	 * properly qualified
3150
	 */
3151
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
3152 3153 3154 3155 3156

	query = createPQExpBuffer();

	if (g_fout->remoteVersion >= 70400)
		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
3157
						"pg_catalog.pg_get_constraintdef(oid) AS consrc "
3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190
						  "FROM pg_catalog.pg_constraint "
						  "WHERE contypid = '%u'::pg_catalog.oid "
						  "ORDER BY conname",
						  tinfo->dobj.catId.oid);
	else
		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
						  "'CHECK (' || consrc || ')' AS consrc "
						  "FROM pg_catalog.pg_constraint "
						  "WHERE contypid = '%u'::pg_catalog.oid "
						  "ORDER BY conname",
						  tinfo->dobj.catId.oid);

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	ntups = PQntuples(res);

	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
	i_conname = PQfnumber(res, "conname");
	i_consrc = PQfnumber(res, "consrc");

	constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));

	tinfo->nDomChecks = ntups;
	tinfo->domChecks = constrinfo;

	for (i = 0; i < ntups; i++)
	{
		constrinfo[i].dobj.objType = DO_CONSTRAINT;
		constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&constrinfo[i].dobj);
3191 3192
		constrinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		constrinfo[i].dobj.namespace = tinfo->dobj.namespace;
3193 3194 3195 3196 3197 3198 3199
		constrinfo[i].contable = NULL;
		constrinfo[i].condomain = tinfo;
		constrinfo[i].contype = 'c';
		constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
		constrinfo[i].conindex = 0;
		constrinfo[i].coninherited = false;
		constrinfo[i].separate = false;
Bruce Momjian's avatar
Bruce Momjian committed
3200

3201
		/*
Bruce Momjian's avatar
Bruce Momjian committed
3202 3203
		 * Make the domain depend on the constraint, ensuring it won't be
		 * output till any constraint dependencies are OK.
3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273
		 */
		addObjectDependency(&tinfo->dobj,
							constrinfo[i].dobj.dumpId);
	}

	PQclear(res);

	destroyPQExpBuffer(query);
}

/*
 * getRules
 *	  get basic information about every rule in the system
 *
 * numRules is set to the number of rules read in
 */
RuleInfo *
getRules(int *numRules)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
	RuleInfo   *ruleinfo;
	int			i_tableoid;
	int			i_oid;
	int			i_rulename;
	int			i_ruletable;
	int			i_ev_type;
	int			i_is_instead;

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT "
						  "tableoid, oid, rulename, "
						  "ev_class as ruletable, ev_type, is_instead "
						  "FROM pg_rewrite "
						  "ORDER BY oid");
	}
	else
	{
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
						  "oid, rulename, "
						  "ev_class as ruletable, ev_type, is_instead "
						  "FROM pg_rewrite "
						  "ORDER BY oid");
	}

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	ntups = PQntuples(res);

	*numRules = ntups;

	ruleinfo = (RuleInfo *) malloc(ntups * sizeof(RuleInfo));

	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
	i_rulename = PQfnumber(res, "rulename");
	i_ruletable = PQfnumber(res, "ruletable");
	i_ev_type = PQfnumber(res, "ev_type");
	i_is_instead = PQfnumber(res, "is_instead");

	for (i = 0; i < ntups; i++)
	{
Bruce Momjian's avatar
Bruce Momjian committed
3274
		Oid			ruletableoid;
3275 3276 3277 3278 3279

		ruleinfo[i].dobj.objType = DO_RULE;
		ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&ruleinfo[i].dobj);
3280
		ruleinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_rulename));
3281 3282
		ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
		ruleinfo[i].ruletable = findTableByOid(ruletableoid);
3283 3284 3285 3286 3287 3288 3289
		if (ruleinfo[i].ruletable == NULL)
		{
			write_msg(NULL, "failed sanity check, parent table OID %u of pg_rewrite entry OID %u not found\n",
					  ruletableoid,
					  ruleinfo[i].dobj.catId.oid);
			exit_nicely();
		}
3290
		ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
3291 3292 3293 3294 3295
		ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
		ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
		if (ruleinfo[i].ruletable)
		{
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3296 3297 3298 3299
			 * If the table is a view, force its ON SELECT rule to be
			 * sorted before the view itself --- this ensures that any
			 * dependencies for the rule affect the table's positioning.
			 * Other rules are forced to appear after their table.
3300 3301 3302
			 */
			if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
				ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
3303
			{
3304 3305
				addObjectDependency(&ruleinfo[i].ruletable->dobj,
									ruleinfo[i].dobj.dumpId);
3306 3307 3308
				/* We'll merge the rule into CREATE VIEW, if possible */
				ruleinfo[i].separate = false;
			}
3309
			else
3310
			{
3311 3312
				addObjectDependency(&ruleinfo[i].dobj,
									ruleinfo[i].ruletable->dobj.dumpId);
3313 3314
				ruleinfo[i].separate = true;
			}
3315
		}
3316 3317
		else
			ruleinfo[i].separate = true;
3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return ruleinfo;
}

/*
 * getTriggers
 *	  get information about every trigger on a dumpable table
 *
 * Note: trigger data is not returned directly to the caller, but it
 * does get entered into the DumpableObject tables.
 */
void
getTriggers(TableInfo tblinfo[], int numTables)
{
	int			i,
				j;
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	TriggerInfo *tginfo;
	int			i_tableoid,
				i_oid,
				i_tgname,
				i_tgfname,
				i_tgtype,
				i_tgnargs,
				i_tgargs,
				i_tgisconstraint,
				i_tgconstrname,
				i_tgconstrrelid,
				i_tgconstrrelname,
				i_tgdeferrable,
				i_tginitdeferred;
	int			ntups;

	for (i = 0; i < numTables; i++)
	{
		TableInfo  *tbinfo = &tblinfo[i];

		if (tbinfo->ntrig == 0 || !tbinfo->dump)
			continue;

		if (g_verbose)
			write_msg(NULL, "reading triggers for table \"%s\"\n",
3366
					  tbinfo->dobj.name);
3367 3368 3369 3370 3371

		/*
		 * select table schema to ensure regproc name is qualified if
		 * needed
		 */
3372
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385

		resetPQExpBuffer(query);
		if (g_fout->remoteVersion >= 70300)
		{
			/*
			 * We ignore triggers that are tied to a foreign-key
			 * constraint
			 */
			appendPQExpBuffer(query,
							  "SELECT tgname, "
							  "tgfoid::pg_catalog.regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
Bruce Momjian's avatar
Bruce Momjian committed
3386
						 "tgconstrrelid, tginitdeferred, tableoid, oid, "
3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402
				 "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
							  "from pg_catalog.pg_trigger t "
							  "where tgrelid = '%u'::pg_catalog.oid "
							  "and (not tgisconstraint "
							  " OR NOT EXISTS"
							  "  (SELECT 1 FROM pg_catalog.pg_depend d "
							  "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
							  "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70100)
		{
			appendPQExpBuffer(query,
							"SELECT tgname, tgfoid::regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
Bruce Momjian's avatar
Bruce Momjian committed
3403
						 "tgconstrrelid, tginitdeferred, tableoid, oid, "
3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437
			  "(select relname from pg_class where oid = tgconstrrelid) "
							  "		as tgconstrrelname "
							  "from pg_trigger "
							  "where tgrelid = '%u'::oid",
							  tbinfo->dobj.catId.oid);
		}
		else
		{
			appendPQExpBuffer(query,
							"SELECT tgname, tgfoid::regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
							  "tgconstrrelid, tginitdeferred, "
							  "(SELECT oid FROM pg_class WHERE relname = 'pg_trigger') AS tableoid, "

							  "oid, "
			  "(select relname from pg_class where oid = tgconstrrelid) "
							  "		as tgconstrrelname "
							  "from pg_trigger "
							  "where tgrelid = '%u'::oid",
							  tbinfo->dobj.catId.oid);
		}
		res = PQexec(g_conn, query->data);
		check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

		ntups = PQntuples(res);

		/*
		 * We may have less triggers than recorded due to having ignored
		 * foreign-key triggers
		 */
		if (ntups > tbinfo->ntrig)
		{
			write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
3438
					  tbinfo->ntrig, tbinfo->dobj.name, ntups);
3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462
			exit_nicely();
		}
		i_tableoid = PQfnumber(res, "tableoid");
		i_oid = PQfnumber(res, "oid");
		i_tgname = PQfnumber(res, "tgname");
		i_tgfname = PQfnumber(res, "tgfname");
		i_tgtype = PQfnumber(res, "tgtype");
		i_tgnargs = PQfnumber(res, "tgnargs");
		i_tgargs = PQfnumber(res, "tgargs");
		i_tgisconstraint = PQfnumber(res, "tgisconstraint");
		i_tgconstrname = PQfnumber(res, "tgconstrname");
		i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
		i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
		i_tgdeferrable = PQfnumber(res, "tgdeferrable");
		i_tginitdeferred = PQfnumber(res, "tginitdeferred");

		tginfo = (TriggerInfo *) malloc(ntups * sizeof(TriggerInfo));

		for (j = 0; j < ntups; j++)
		{
			tginfo[j].dobj.objType = DO_TRIGGER;
			tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
			tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
			AssignDumpId(&tginfo[j].dobj);
3463 3464
			tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
			tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482
			tginfo[j].tgtable = tbinfo;
			tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
			tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
			tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
			tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
			tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
			tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
			tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';

			if (tginfo[j].tgisconstraint)
			{
				tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
				tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
				if (OidIsValid(tginfo[j].tgconstrrelid))
				{
					if (PQgetisnull(res, j, i_tgconstrrelname))
					{
						write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
3483
								  tginfo[j].dobj.name, tbinfo->dobj.name,
3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575
								  tginfo[j].tgconstrrelid);
						exit_nicely();
					}
					tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
				}
				else
					tginfo[j].tgconstrrelname = NULL;
			}
			else
			{
				tginfo[j].tgconstrname = NULL;
				tginfo[j].tgconstrrelid = InvalidOid;
				tginfo[j].tgconstrrelname = NULL;
			}
		}

		PQclear(res);
	}

	destroyPQExpBuffer(query);
}

/*
 * getProcLangs
 *	  get basic information about every procedural language in the system
 *
 * numProcLangs is set to the number of langs read in
 *
 * NB: this must run after getFuncs() because we assume we can do
 * findFuncByOid().
 */
ProcLangInfo *
getProcLangs(int *numProcLangs)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
	ProcLangInfo *planginfo;
	int			i_tableoid;
	int			i_oid;
	int			i_lanname;
	int			i_lanpltrusted;
	int			i_lanplcallfoid;
	int			i_lanvalidator = -1;
	int			i_lanacl = -1;

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, * FROM pg_language "
						  "WHERE lanispl "
						  "ORDER BY oid");
	}
	else
	{
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_language') AS tableoid, "
						  "oid, * FROM pg_language "
						  "WHERE lanispl "
						  "ORDER BY oid");
	}

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	ntups = PQntuples(res);

	*numProcLangs = ntups;

	planginfo = (ProcLangInfo *) malloc(ntups * sizeof(ProcLangInfo));

	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
	i_lanname = PQfnumber(res, "lanname");
	i_lanpltrusted = PQfnumber(res, "lanpltrusted");
	i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
	if (g_fout->remoteVersion >= 70300)
	{
		i_lanvalidator = PQfnumber(res, "lanvalidator");
		i_lanacl = PQfnumber(res, "lanacl");
	}

	for (i = 0; i < ntups; i++)
	{
		planginfo[i].dobj.objType = DO_PROCLANG;
		planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&planginfo[i].dobj);

3576
		planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589
		planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
		planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
		if (g_fout->remoteVersion >= 70300)
		{
			planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
			planginfo[i].lanacl = strdup(PQgetvalue(res, i, i_lanacl));
		}
		else
		{
			FuncInfo   *funcInfo;

			planginfo[i].lanvalidator = InvalidOid;
			planginfo[i].lanacl = strdup("{=U}");
Bruce Momjian's avatar
Bruce Momjian committed
3590

3591
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3592 3593
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647
			 * mechanism will handle this for us.)
			 */
			funcInfo = findFuncByOid(planginfo[i].lanplcallfoid);
			if (funcInfo)
				addObjectDependency(&planginfo[i].dobj,
									funcInfo->dobj.dumpId);
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return planginfo;
}

/*
 * getCasts
 *	  get basic information about every cast in the system
 *
 * numCasts is set to the number of casts read in
 */
CastInfo *
getCasts(int *numCasts)
{
	PGresult   *res;
	int			ntups;
	int			i;
	PQExpBuffer query = createPQExpBuffer();
	CastInfo   *castinfo;
	int			i_tableoid;
	int			i_oid;
	int			i_castsource;
	int			i_casttarget;
	int			i_castfunc;
	int			i_castcontext;

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, "
						  "castsource, casttarget, castfunc, castcontext "
						  "FROM pg_cast ORDER BY 3,4");
	}
	else
	{
		appendPQExpBuffer(query, "SELECT 0 as tableoid, p.oid, "
						  "t1.oid as castsource, t2.oid as casttarget, "
						  "p.oid as castfunc, 'e' as castcontext "
						  "FROM pg_type t1, pg_type t2, pg_proc p "
						  "WHERE p.pronargs = 1 AND "
						  "p.proargtypes[0] = t1.oid AND "
Bruce Momjian's avatar
Bruce Momjian committed
3648
					  "p.prorettype = t2.oid AND p.proname = t2.typname "
3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669
						  "ORDER BY 3,4");
	}

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	ntups = PQntuples(res);

	*numCasts = ntups;

	castinfo = (CastInfo *) malloc(ntups * sizeof(CastInfo));

	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
	i_castsource = PQfnumber(res, "castsource");
	i_casttarget = PQfnumber(res, "casttarget");
	i_castfunc = PQfnumber(res, "castfunc");
	i_castcontext = PQfnumber(res, "castcontext");

	for (i = 0; i < ntups; i++)
	{
Bruce Momjian's avatar
Bruce Momjian committed
3670
		PQExpBufferData namebuf;
3671 3672 3673
		TypeInfo   *sTypeInfo;
		TypeInfo   *tTypeInfo;

3674 3675 3676 3677 3678 3679 3680 3681 3682
		castinfo[i].dobj.objType = DO_CAST;
		castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
		castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
		AssignDumpId(&castinfo[i].dobj);
		castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
		castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
		castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
		castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));

3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695
		/*
		 * Try to name cast as concatenation of typnames.  This is only
		 * used for purposes of sorting.  If we fail to find either type,
		 * the name will be an empty string.
		 */
		initPQExpBuffer(&namebuf);
		sTypeInfo = findTypeByOid(castinfo[i].castsource);
		tTypeInfo = findTypeByOid(castinfo[i].casttarget);
		if (sTypeInfo && tTypeInfo)
			appendPQExpBuffer(&namebuf, "%s %s",
							  sTypeInfo->dobj.name, tTypeInfo->dobj.name);
		castinfo[i].dobj.name = namebuf.data;

3696 3697 3698
		if (OidIsValid(castinfo[i].castfunc))
		{
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3699 3700
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745
			 * mechanism will handle this for us.)
			 */
			FuncInfo   *funcInfo;

			funcInfo = findFuncByOid(castinfo[i].castfunc);
			if (funcInfo)
				addObjectDependency(&castinfo[i].dobj,
									funcInfo->dobj.dumpId);
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return castinfo;
}

/*
 * getTableAttrs -
 *	  for each interesting table, read info about its attributes
 *	  (names, types, default values, CHECK constraints, etc)
 *
 * This is implemented in a very inefficient way right now, looping
 * through the tblinfo and doing a join per table to find the attrs and their
 * types.  However, because we want type names and so forth to be named
 * relative to the schema of each table, we couldn't do it in just one
 * query.  (Maybe one query per schema?)
 *
 *	modifies tblinfo
 */
void
getTableAttrs(TableInfo *tblinfo, int numTables)
{
	int			i,
				j,
				k;
	PQExpBuffer q = createPQExpBuffer();
	int			i_attnum;
	int			i_attname;
	int			i_atttypname;
	int			i_atttypmod;
	int			i_attstattarget;
	int			i_attstorage;
	int			i_typstorage;
3746
	int			i_attnotnull;
3747
	int			i_atthasdef;
3748
	int			i_attisdropped;
3749
	int			i_attislocal;
3750
	PGresult   *res;
3751
	int			ntups;
3752
	bool		hasdefaults;
3753 3754 3755

	for (i = 0; i < numTables; i++)
	{
Bruce Momjian's avatar
Bruce Momjian committed
3756
		TableInfo  *tbinfo = &tblinfo[i];
3757

3758
		/* Don't bother to collect info for sequences */
3759
		if (tbinfo->relkind == RELKIND_SEQUENCE)
3760 3761
			continue;

3762
		/* Don't bother with uninteresting tables, either */
3763
		if (!tbinfo->interesting)
3764 3765 3766 3767 3768 3769
			continue;

		/*
		 * Make sure we are in proper schema for this table; this allows
		 * correct retrieval of formatted type names and default exprs
		 */
3770
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3771

3772 3773 3774
		/* find all the user attributes and their types */

		/*
3775
		 * we must read the attribute names in attribute number order!
3776
		 * because we will use the attnum to index into the attnames array
3777 3778 3779 3780
		 * later.  We actually ask to order by "attrelid, attnum" because
		 * (at least up to 7.3) the planner is not smart enough to realize
		 * it needn't re-sort the output of an indexscan on
		 * pg_attribute_relid_attnum_index.
3781 3782
		 */
		if (g_verbose)
3783
			write_msg(NULL, "finding the columns and types of table \"%s\"\n",
3784
					  tbinfo->dobj.name);
3785

Bruce Momjian's avatar
Bruce Momjian committed
3786
		resetPQExpBuffer(q);
3787

3788 3789
		if (g_fout->remoteVersion >= 70300)
		{
3790
			/* need left join here to not fail on dropped columns ... */
3791
			appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
Bruce Momjian's avatar
Bruce Momjian committed
3792 3793
			  "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
			   "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname "
3794 3795
							  "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
							  "on a.atttypid = t.oid "
3796
							  "where a.attrelid = '%u'::pg_catalog.oid "
3797 3798
							  "and a.attnum > 0::pg_catalog.int2 "
							  "order by a.attrelid, a.attnum",
3799
							  tbinfo->dobj.catId.oid);
3800 3801
		}
		else if (g_fout->remoteVersion >= 70100)
3802
		{
3803 3804
			/*
			 * attstattarget doesn't exist in 7.1.  It does exist in 7.2,
Bruce Momjian's avatar
Bruce Momjian committed
3805 3806
			 * but we don't dump it because we can't tell whether it's
			 * been explicitly set or was just a default.
3807
			 */
3808
			appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, -1 as attstattarget, a.attstorage, t.typstorage, "
3809
							  "a.attnotnull, a.atthasdef, false as attisdropped, false as attislocal, "
Bruce Momjian's avatar
Bruce Momjian committed
3810
						  "format_type(t.oid,a.atttypmod) as atttypname "
3811 3812
							  "from pg_attribute a left join pg_type t "
							  "on a.atttypid = t.oid "
3813
							  "where a.attrelid = '%u'::oid "
3814 3815
							  "and a.attnum > 0::int2 "
							  "order by a.attrelid, a.attnum",
3816
							  tbinfo->dobj.catId.oid);
3817 3818 3819
		}
		else
		{
3820
			/* format_type not available before 7.1 */
3821
			appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, attstorage, attstorage as typstorage, "
3822
							  "attnotnull, atthasdef, false as attisdropped, false as attislocal, "
3823 3824
							  "(select typname from pg_type where oid = atttypid) as atttypname "
							  "from pg_attribute a "
3825
							  "where attrelid = '%u'::oid "
3826 3827
							  "and attnum > 0::int2 "
							  "order by attrelid, attnum",
3828
							  tbinfo->dobj.catId.oid);
3829 3830
		}

Bruce Momjian's avatar
Bruce Momjian committed
3831
		res = PQexec(g_conn, q->data);
3832
		check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
3833 3834 3835

		ntups = PQntuples(res);

3836
		i_attnum = PQfnumber(res, "attnum");
3837
		i_attname = PQfnumber(res, "attname");
3838
		i_atttypname = PQfnumber(res, "atttypname");
3839
		i_atttypmod = PQfnumber(res, "atttypmod");
3840
		i_attstattarget = PQfnumber(res, "attstattarget");
3841 3842
		i_attstorage = PQfnumber(res, "attstorage");
		i_typstorage = PQfnumber(res, "typstorage");
3843
		i_attnotnull = PQfnumber(res, "attnotnull");
3844
		i_atthasdef = PQfnumber(res, "atthasdef");
3845
		i_attisdropped = PQfnumber(res, "attisdropped");
3846
		i_attislocal = PQfnumber(res, "attislocal");
3847

3848 3849 3850 3851 3852
		tbinfo->numatts = ntups;
		tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
		tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
		tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
		tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
3853 3854
		tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
		tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
3855
		tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
3856
		tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
3857 3858
		tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
3859
		tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
3860 3861 3862
		tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
3863 3864
		hasdefaults = false;

3865 3866
		for (j = 0; j < ntups; j++)
		{
Bruce Momjian's avatar
Bruce Momjian committed
3867
			if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
3868
			{
3869
				write_msg(NULL, "invalid column numbering in table \"%s\"\n",
3870
						  tbinfo->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
3871
				exit_nicely();
3872
			}
3873 3874 3875 3876
			tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
			tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
			tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
			tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
3877 3878
			tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
			tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
3879
			tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
3880
			tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3881
			tbinfo->attisserial[j] = false;		/* fix below */
3882
			tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3883
			tbinfo->attrdefs[j] = NULL; /* fix below */
3884
			if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
3885 3886
				hasdefaults = true;
			/* these flags will be set in flagInhAttrs() */
3887 3888 3889
			tbinfo->inhAttrs[j] = false;
			tbinfo->inhAttrDef[j] = false;
			tbinfo->inhNotNull[j] = false;
3890
		}
3891

3892
		PQclear(res);
3893

3894 3895 3896
		/*
		 * Get info about column defaults
		 */
3897 3898
		if (hasdefaults)
		{
3899
			AttrDefInfo *attrdefs;
3900 3901 3902
			int			numDefaults;

			if (g_verbose)
3903
				write_msg(NULL, "finding default expressions of table \"%s\"\n",
3904
						  tbinfo->dobj.name);
3905 3906

			resetPQExpBuffer(q);
3907 3908
			if (g_fout->remoteVersion >= 70300)
			{
3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949
				appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
					   "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
								  "FROM pg_catalog.pg_attrdef "
								  "WHERE adrelid = '%u'::pg_catalog.oid",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70200)
			{
				/* 7.2 did not have OIDs in pg_attrdef */
				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, adnum, "
								  "pg_get_expr(adbin, adrelid) AS adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70100)
			{
				/* no pg_get_expr, so must rely on adsrc */
				appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			else
			{
				/* no pg_get_expr, no tableoid either */
				appendPQExpBuffer(q, "SELECT "
								  "(SELECT oid FROM pg_class WHERE relname = 'pg_attrdef') AS tableoid, "
								  "oid, adnum, adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			res = PQexec(g_conn, q->data);
			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);

			numDefaults = PQntuples(res);
			attrdefs = (AttrDefInfo *) malloc(numDefaults * sizeof(AttrDefInfo));

			for (j = 0; j < numDefaults; j++)
			{
Bruce Momjian's avatar
Bruce Momjian committed
3950
				int			adnum;
3951 3952 3953 3954 3955 3956 3957 3958 3959

				attrdefs[j].dobj.objType = DO_ATTRDEF;
				attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
				attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
				AssignDumpId(&attrdefs[j].dobj);
				attrdefs[j].adtable = tbinfo;
				attrdefs[j].adnum = adnum = atoi(PQgetvalue(res, j, 2));
				attrdefs[j].adef_expr = strdup(PQgetvalue(res, j, 3));

3960 3961 3962
				attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
				attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;

3963 3964 3965
				/*
				 * Defaults on a VIEW must always be dumped as separate
				 * ALTER TABLE commands.  Defaults on regular tables are
Bruce Momjian's avatar
Bruce Momjian committed
3966 3967 3968
				 * dumped as part of the CREATE TABLE if possible.	To
				 * check if it's safe, we mark the default as needing to
				 * appear before the CREATE.
3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986
				 */
				if (tbinfo->relkind == RELKIND_VIEW)
				{
					attrdefs[j].separate = true;
					/* needed in case pre-7.3 DB: */
					addObjectDependency(&attrdefs[j].dobj,
										tbinfo->dobj.dumpId);
				}
				else
				{
					attrdefs[j].separate = false;
					addObjectDependency(&tbinfo->dobj,
										attrdefs[j].dobj.dumpId);
				}

				if (adnum <= 0 || adnum > ntups)
				{
					write_msg(NULL, "invalid adnum value %d for table \"%s\"\n",
3987
							  adnum, tbinfo->dobj.name);
3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004
					exit_nicely();
				}
				tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
			}
			PQclear(res);
		}

		/*
		 * Get info about table CHECK constraints
		 */
		if (tbinfo->ncheck > 0)
		{
			ConstraintInfo *constrs;
			int			numConstrs;

			if (g_verbose)
				write_msg(NULL, "finding check constraints for table \"%s\"\n",
4005
						  tbinfo->dobj.name);
4006 4007 4008 4009 4010

			resetPQExpBuffer(q);
			if (g_fout->remoteVersion >= 70400)
			{
				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
4011
						"pg_catalog.pg_get_constraintdef(oid) AS consrc "
4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027
								  "FROM pg_catalog.pg_constraint "
								  "WHERE conrelid = '%u'::pg_catalog.oid "
								  "   AND contype = 'c' "
								  "ORDER BY conname",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70300)
			{
				/* no pg_get_constraintdef, must use consrc */
				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
								  "'CHECK (' || consrc || ')' AS consrc "
								  "FROM pg_catalog.pg_constraint "
								  "WHERE conrelid = '%u'::pg_catalog.oid "
								  "   AND contype = 'c' "
								  "ORDER BY conname",
								  tbinfo->dobj.catId.oid);
4028 4029
			}
			else if (g_fout->remoteVersion >= 70200)
4030
			{
4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048
				/* 7.2 did not have OIDs in pg_relcheck */
				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
								  "rcname AS conname, "
								  "'CHECK (' || rcsrc || ')' AS consrc "
								  "FROM pg_relcheck "
								  "WHERE rcrelid = '%u'::oid "
								  "ORDER BY rcname",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70100)
			{
				appendPQExpBuffer(q, "SELECT tableoid, oid, "
								  "rcname AS conname, "
								  "'CHECK (' || rcsrc || ')' AS consrc "
								  "FROM pg_relcheck "
								  "WHERE rcrelid = '%u'::oid "
								  "ORDER BY rcname",
								  tbinfo->dobj.catId.oid);
4049 4050 4051
			}
			else
			{
4052 4053 4054 4055 4056 4057 4058 4059 4060
				/* no tableoid in 7.0 */
				appendPQExpBuffer(q, "SELECT "
								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
								  "oid, rcname AS conname, "
								  "'CHECK (' || rcsrc || ')' AS consrc "
								  "FROM pg_relcheck "
								  "WHERE rcrelid = '%u'::oid "
								  "ORDER BY rcname",
								  tbinfo->dobj.catId.oid);
4061 4062
			}
			res = PQexec(g_conn, q->data);
4063 4064 4065 4066
			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);

			numConstrs = PQntuples(res);
			if (numConstrs != tbinfo->ncheck)
4067
			{
4068
				write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
4069
						  tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
4070
				write_msg(NULL, "(The system catalogs might be corrupted.)\n");
4071 4072
				exit_nicely();
			}
4073

4074 4075
			constrs = (ConstraintInfo *) malloc(numConstrs * sizeof(ConstraintInfo));
			tbinfo->checkexprs = constrs;
4076

4077 4078 4079 4080 4081 4082
			for (j = 0; j < numConstrs; j++)
			{
				constrs[j].dobj.objType = DO_CONSTRAINT;
				constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
				constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
				AssignDumpId(&constrs[j].dobj);
4083 4084
				constrs[j].dobj.name = strdup(PQgetvalue(res, j, 2));
				constrs[j].dobj.namespace = tbinfo->dobj.namespace;
4085 4086 4087 4088 4089 4090 4091
				constrs[j].contable = tbinfo;
				constrs[j].condomain = NULL;
				constrs[j].contype = 'c';
				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
				constrs[j].conindex = 0;
				constrs[j].coninherited = false;
				constrs[j].separate = false;
4092 4093 4094 4095 4096 4097
				/*
				 * Mark the constraint as needing to appear before the
				 * table --- this is so that any other dependencies of
				 * the constraint will be emitted before we try to create
				 * the table.
				 */
4098 4099
				addObjectDependency(&tbinfo->dobj,
									constrs[j].dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
4100

4101 4102 4103 4104 4105
				/*
				 * If the constraint is inherited, this will be detected
				 * later.  We also detect later if the constraint must be
				 * split out from the table definition.
				 */
4106
			}
4107
			PQclear(res);
4108
		}
4109 4110

		/*
Bruce Momjian's avatar
Bruce Momjian committed
4111 4112 4113 4114 4115
		 * Check to see if any columns are serial columns.	Our first
		 * quick filter is that it must be integer or bigint with a
		 * default.  If so, we scan to see if we found a sequence linked
		 * to this column. If we did, mark the column and sequence
		 * appropriately.
4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126
		 */
		for (j = 0; j < ntups; j++)
		{
			/*
			 * Note assumption that format_type will show these types as
			 * exactly "integer" and "bigint" regardless of schema path.
			 * This is correct in 7.3 but needs to be watched.
			 */
			if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
				strcmp(tbinfo->atttypnames[j], "bigint") != 0)
				continue;
4127
			if (tbinfo->attrdefs[j] == NULL)
4128 4129 4130
				continue;
			for (k = 0; k < numTables; k++)
			{
Bruce Momjian's avatar
Bruce Momjian committed
4131
				TableInfo  *seqinfo = &tblinfo[k];
4132

4133 4134
				if (OidIsValid(seqinfo->owning_tab) &&
					seqinfo->owning_tab == tbinfo->dobj.catId.oid &&
Bruce Momjian's avatar
Bruce Momjian committed
4135
					seqinfo->owning_col == j + 1)
4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147
				{
					/*
					 * Found a match.  Copy the table's interesting and
					 * dumpable flags to the sequence.
					 */
					tbinfo->attisserial[j] = true;
					seqinfo->interesting = tbinfo->interesting;
					seqinfo->dump = tbinfo->dump;
					break;
				}
			}
		}
4148
	}
4149 4150

	destroyPQExpBuffer(q);
4151 4152 4153 4154
}


/*
4155
 * dumpComment --
Bruce Momjian's avatar
Bruce Momjian committed
4156
 *
4157
 * This routine is used to dump any comments associated with the
4158
 * object handed to this routine. The routine takes a constant character
4159
 * string for the target part of the comment-creation command, plus
4160
 * the namespace and owner of the object (for labeling the ArchiveEntry),
4161 4162
 * plus catalog ID and subid which are the lookup key for pg_description,
 * plus the dump ID for the object (for setting a dependency).
4163
 * If a matching pg_description entry is found, it is dumped.
4164 4165 4166 4167 4168 4169 4170
 *
 * Note: although this routine takes a dumpId for dependency purposes,
 * that purpose is just to mark the dependency in the emitted dump file
 * for possible future use by pg_restore.  We do NOT use it for determining
 * ordering of the comment in the dump file, because this routine is called
 * after dependency sorting occurs.  This routine should be called just after
 * calling ArchiveEntry() for the specified object.
4171
 */
4172
static void
4173 4174
dumpComment(Archive *fout, const char *target,
			const char *namespace, const char *owner,
4175
			CatalogId catalogId, int subid, DumpId dumpId)
4176
{
4177 4178
	CommentItem *comments;
	int			ncomments;
Bruce Momjian's avatar
Bruce Momjian committed
4179

4180 4181 4182 4183
	/* Comments are SCHEMA not data */
	if (dataOnly)
		return;

4184 4185 4186
	/* Search for comments associated with catalogId, using table */
	ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
							 &comments);
4187

4188 4189
	/* Is there one matching the subid? */
	while (ncomments > 0)
4190
	{
4191 4192 4193 4194
		if (comments->objsubid == subid)
			break;
		comments++;
		ncomments--;
4195
	}
Bruce Momjian's avatar
Bruce Momjian committed
4196

Bruce Momjian's avatar
Bruce Momjian committed
4197
	/* If a comment exists, build COMMENT ON statement */
4198
	if (ncomments > 0)
4199
	{
4200 4201
		PQExpBuffer query = createPQExpBuffer();

4202
		appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
4203
		appendStringLiteral(query, comments->descr, false);
4204
		appendPQExpBuffer(query, ";\n");
4205

4206
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
4207
					 target, namespace, NULL, owner, false,
4208 4209 4210
					 "COMMENT", query->data, "", NULL,
					 &(dumpId), 1,
					 NULL, NULL);
4211

4212 4213
		destroyPQExpBuffer(query);
	}
4214 4215 4216 4217 4218
}

/*
 * dumpTableComment --
 *
4219
 * As above, but dump comments for both the specified table (or view)
4220
 * and its columns.
4221 4222 4223
 */
static void
dumpTableComment(Archive *fout, TableInfo *tbinfo,
4224
				 const char *reltypename)
4225
{
4226 4227
	CommentItem *comments;
	int			ncomments;
4228 4229 4230 4231 4232 4233 4234
	PQExpBuffer query;
	PQExpBuffer target;

	/* Comments are SCHEMA not data */
	if (dataOnly)
		return;

4235 4236 4237 4238 4239
	/* Search for comments associated with relation, using table */
	ncomments = findComments(fout,
							 tbinfo->dobj.catId.tableoid,
							 tbinfo->dobj.catId.oid,
							 &comments);
4240

4241 4242 4243
	/* If comments exist, build COMMENT ON statements */
	if (ncomments <= 0)
		return;
4244 4245 4246 4247

	query = createPQExpBuffer();
	target = createPQExpBuffer();

4248
	while (ncomments > 0)
4249
	{
4250 4251
		const char *descr = comments->descr;
		int			objsubid = comments->objsubid;
4252 4253 4254 4255 4256

		if (objsubid == 0)
		{
			resetPQExpBuffer(target);
			appendPQExpBuffer(target, "%s %s", reltypename,
4257
							  fmtId(tbinfo->dobj.name));
4258 4259 4260

			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4261
			appendStringLiteral(query, descr, false);
4262 4263
			appendPQExpBuffer(query, ";\n");

4264 4265
			ArchiveEntry(fout, nilCatalogId, createDumpId(),
						 target->data,
4266 4267
						 tbinfo->dobj.namespace->dobj.name,
						 NULL,
4268
						 tbinfo->rolname,
4269
						 false, "COMMENT", query->data, "", NULL,
4270 4271
						 &(tbinfo->dobj.dumpId), 1,
						 NULL, NULL);
4272 4273 4274 4275 4276
		}
		else if (objsubid > 0 && objsubid <= tbinfo->numatts)
		{
			resetPQExpBuffer(target);
			appendPQExpBuffer(target, "COLUMN %s.",
4277
							  fmtId(tbinfo->dobj.name));
4278
			appendPQExpBuffer(target, "%s",
Bruce Momjian's avatar
Bruce Momjian committed
4279
							  fmtId(tbinfo->attnames[objsubid - 1]));
Bruce Momjian's avatar
Bruce Momjian committed
4280

4281 4282
			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4283
			appendStringLiteral(query, descr, false);
4284 4285
			appendPQExpBuffer(query, ";\n");

4286 4287
			ArchiveEntry(fout, nilCatalogId, createDumpId(),
						 target->data,
4288 4289
						 tbinfo->dobj.namespace->dobj.name,
						 NULL,
4290
						 tbinfo->rolname,
4291
						 false, "COMMENT", query->data, "", NULL,
4292 4293
						 &(tbinfo->dobj.dumpId), 1,
						 NULL, NULL);
4294
		}
4295 4296 4297

		comments++;
		ncomments--;
4298
	}
Bruce Momjian's avatar
Bruce Momjian committed
4299

4300
	destroyPQExpBuffer(query);
4301
	destroyPQExpBuffer(target);
Bruce Momjian's avatar
Bruce Momjian committed
4302 4303
}

4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328
/*
 * findComments --
 *
 * Find the comment(s), if any, associated with the given object.  All the
 * objsubid values associated with the given classoid/objoid are found with
 * one search.
 */
static int
findComments(Archive *fout, Oid classoid, Oid objoid,
			 CommentItem **items)
{
	/* static storage for table of comments */
	static CommentItem *comments = NULL;
	static int	ncomments = -1;

	CommentItem *middle = NULL;
	CommentItem *low;
	CommentItem *high;
	int			nmatch;

	/* Get comments if we didn't already */
	if (ncomments < 0)
		ncomments = collectComments(fout, &comments);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
4329 4330 4331
	 * Pre-7.2, pg_description does not contain classoid, so
	 * collectComments just stores a zero.	If there's a collision on
	 * object OID, well, you get duplicate comments.
4332 4333 4334 4335 4336 4337 4338 4339
	 */
	if (fout->remoteVersion < 70200)
		classoid = 0;

	/*
	 * Do binary search to find some item matching the object.
	 */
	low = &comments[0];
Bruce Momjian's avatar
Bruce Momjian committed
4340
	high = &comments[ncomments - 1];
4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471
	while (low <= high)
	{
		middle = low + (high - low) / 2;

		if (classoid < middle->classoid)
			high = middle - 1;
		else if (classoid > middle->classoid)
			low = middle + 1;
		else if (objoid < middle->objoid)
			high = middle - 1;
		else if (objoid > middle->objoid)
			low = middle + 1;
		else
			break;				/* found a match */
	}

	if (low > high)				/* no matches */
	{
		*items = NULL;
		return 0;
	}

	/*
	 * Now determine how many items match the object.  The search loop
	 * invariant still holds: only items between low and high inclusive
	 * could match.
	 */
	nmatch = 1;
	while (middle > low)
	{
		if (classoid != middle[-1].classoid ||
			objoid != middle[-1].objoid)
			break;
		middle--;
		nmatch++;
	}

	*items = middle;

	middle += nmatch;
	while (middle <= high)
	{
		if (classoid != middle->classoid ||
			objoid != middle->objoid)
			break;
		middle++;
		nmatch++;
	}

	return nmatch;
}

/*
 * collectComments --
 *
 * Construct a table of all comments available for database objects.
 * We used to do per-object queries for the comments, but it's much faster
 * to pull them all over at once, and on most databases the memory cost
 * isn't high.
 *
 * The table is sorted by classoid/objid/objsubid for speed in lookup.
 */
static int
collectComments(Archive *fout, CommentItem **items)
{
	PGresult   *res;
	PQExpBuffer query;
	int			i_description;
	int			i_classoid;
	int			i_objoid;
	int			i_objsubid;
	int			ntups;
	int			i;
	CommentItem *comments;

	/*
	 * Note we do NOT change source schema here; preserve the caller's
	 * setting, instead.
	 */

	query = createPQExpBuffer();

	if (fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
						  "FROM pg_catalog.pg_description "
						  "ORDER BY classoid, objoid, objsubid");
	}
	else if (fout->remoteVersion >= 70200)
	{
		appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
						  "FROM pg_description "
						  "ORDER BY classoid, objoid, objsubid");
	}
	else
	{
		/* Note: this will fail to find attribute comments in pre-7.2... */
		appendPQExpBuffer(query, "SELECT description, 0 as classoid, objoid, 0 as objsubid "
						  "FROM pg_description "
						  "ORDER BY objoid");
	}

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	/* Construct lookup table containing OIDs in numeric form */

	i_description = PQfnumber(res, "description");
	i_classoid = PQfnumber(res, "classoid");
	i_objoid = PQfnumber(res, "objoid");
	i_objsubid = PQfnumber(res, "objsubid");

	ntups = PQntuples(res);

	comments = (CommentItem *) malloc(ntups * sizeof(CommentItem));

	for (i = 0; i < ntups; i++)
	{
		comments[i].descr = PQgetvalue(res, i, i_description);
		comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
		comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
		comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
	}

	/* Do NOT free the PGresult since we are keeping pointers into it */
	destroyPQExpBuffer(query);

	*items = comments;
	return ntups;
}

4472
/*
4473
 * dumpDumpableObject
Bruce Momjian's avatar
Bruce Momjian committed
4474
 *
4475 4476
 * This routine and its subsidiaries are responsible for creating
 * ArchiveEntries (TOC objects) for each object to be dumped.
4477
 */
4478 4479
static void
dumpDumpableObject(Archive *fout, DumpableObject *dobj)
4480
{
4481
	switch (dobj->objType)
4482
	{
4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533
		case DO_NAMESPACE:
			dumpNamespace(fout, (NamespaceInfo *) dobj);
			break;
		case DO_TYPE:
			dumpType(fout, (TypeInfo *) dobj);
			break;
		case DO_FUNC:
			dumpFunc(fout, (FuncInfo *) dobj);
			break;
		case DO_AGG:
			dumpAgg(fout, (AggInfo *) dobj);
			break;
		case DO_OPERATOR:
			dumpOpr(fout, (OprInfo *) dobj);
			break;
		case DO_OPCLASS:
			dumpOpclass(fout, (OpclassInfo *) dobj);
			break;
		case DO_CONVERSION:
			dumpConversion(fout, (ConvInfo *) dobj);
			break;
		case DO_TABLE:
			dumpTable(fout, (TableInfo *) dobj);
			break;
		case DO_ATTRDEF:
			dumpAttrDef(fout, (AttrDefInfo *) dobj);
			break;
		case DO_INDEX:
			dumpIndex(fout, (IndxInfo *) dobj);
			break;
		case DO_RULE:
			dumpRule(fout, (RuleInfo *) dobj);
			break;
		case DO_TRIGGER:
			dumpTrigger(fout, (TriggerInfo *) dobj);
			break;
		case DO_CONSTRAINT:
			dumpConstraint(fout, (ConstraintInfo *) dobj);
			break;
		case DO_FK_CONSTRAINT:
			dumpConstraint(fout, (ConstraintInfo *) dobj);
			break;
		case DO_PROCLANG:
			dumpProcLang(fout, (ProcLangInfo *) dobj);
			break;
		case DO_CAST:
			dumpCast(fout, (CastInfo *) dobj);
			break;
		case DO_TABLE_DATA:
			dumpTableData(fout, (TableDataInfo *) dobj);
			break;
4534 4535 4536 4537 4538
		case DO_TABLE_TYPE:
			/* table rowtypes are never dumped separately */
			break;
		case DO_BLOBS:
			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
4539
						 dobj->name, NULL, NULL, "",
4540
						 false, "BLOBS", "", "", NULL,
4541 4542 4543
						 NULL, 0,
						 dumpBlobs, NULL);
			break;
Tom Lane's avatar
Tom Lane committed
4544 4545 4546 4547 4548 4549 4550
		case DO_BLOB_COMMENTS:
			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
						 dobj->name, NULL, NULL, "",
						 false, "BLOB COMMENTS", "", "", NULL,
						 NULL, 0,
						 dumpBlobComments, NULL);
			break;
4551 4552 4553 4554
	}
}

/*
4555 4556
 * dumpNamespace
 *	  writes out to fout the queries to recreate a user-defined namespace
4557
 */
4558 4559
static void
dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
4560
{
4561 4562
	PQExpBuffer q;
	PQExpBuffer delq;
4563
	char	   *qnspname;
4564

4565 4566 4567
	/* skip if not to be dumped */
	if (!nspinfo->dump || dataOnly)
		return;
4568

4569
	/* don't dump dummy namespace from pre-7.3 source */
4570
	if (strlen(nspinfo->dobj.name) == 0)
4571
		return;
4572

4573 4574
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
4575

4576
	qnspname = strdup(fmtId(nspinfo->dobj.name));
4577

4578
	appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
4579

4580
	appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
4581

4582 4583
	ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
				 nspinfo->dobj.name,
4584
				 NULL, NULL, 
4585
				 nspinfo->rolname,
4586 4587 4588
				 false, "SCHEMA", q->data, delq->data, NULL,
				 nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
				 NULL, NULL);
4589

4590 4591 4592 4593
	/* Dump Schema Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "SCHEMA %s", qnspname);
	dumpComment(fout, q->data,
4594
				NULL, nspinfo->rolname,
4595
				nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
4596

4597
	dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
4598
			qnspname, nspinfo->dobj.name, NULL,
4599
			nspinfo->rolname, nspinfo->nspacl);
4600

4601
	free(qnspname);
4602 4603 4604 4605 4606 4607

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

/*
4608 4609 4610 4611 4612 4613 4614
 * dumpType
 *	  writes out to fout the queries to recreate a user-defined type
 */
static void
dumpType(Archive *fout, TypeInfo *tinfo)
{
	/* Dump only types in dumpable namespaces */
4615
	if (!tinfo->dobj.namespace->dump || dataOnly)
4616 4617 4618
		return;

	/* skip complex types, except for standalone composite types */
4619
	/* (note: this test should now be unnecessary) */
4620 4621 4622 4623 4624 4625 4626 4627
	if (OidIsValid(tinfo->typrelid) && tinfo->typrelkind != 'c')
		return;

	/* skip undefined placeholder types */
	if (!tinfo->isDefined)
		return;

	/* skip all array types that start w/ underscore */
4628
	if ((tinfo->dobj.name[0] == '_') &&
4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642
		OidIsValid(tinfo->typelem))
		return;

	/* Dump out in proper style */
	if (tinfo->typtype == 'b')
		dumpBaseType(fout, tinfo);
	else if (tinfo->typtype == 'd')
		dumpDomain(fout, tinfo);
	else if (tinfo->typtype == 'c')
		dumpCompositeType(fout, tinfo);
}

/*
 * dumpBaseType
Bruce Momjian's avatar
Bruce Momjian committed
4643
 *	  writes out to fout the queries to recreate a user-defined base type
4644 4645
 */
static void
4646
dumpBaseType(Archive *fout, TypeInfo *tinfo)
4647 4648 4649 4650 4651 4652 4653 4654 4655
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
	char	   *typlen;
	char	   *typinput;
	char	   *typoutput;
4656 4657
	char	   *typreceive;
	char	   *typsend;
4658
	char	   *typanalyze;
4659 4660 4661 4662
	Oid			typinputoid;
	Oid			typoutputoid;
	Oid			typreceiveoid;
	Oid			typsendoid;
4663
	Oid			typanalyzeoid;
4664 4665 4666 4667 4668 4669 4670
	char	   *typdelim;
	char	   *typdefault;
	char	   *typbyval;
	char	   *typalign;
	char	   *typstorage;

	/* Set proper schema search path so regproc references list correctly */
4671
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4672 4673

	/* Fetch type-specific details */
4674
	if (fout->remoteVersion >= 80000)
4675 4676 4677
	{
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, typreceive, typsend, "
4678
						  "typanalyze, "
4679 4680 4681 4682
						  "typinput::pg_catalog.oid as typinputoid, "
						  "typoutput::pg_catalog.oid as typoutputoid, "
						  "typreceive::pg_catalog.oid as typreceiveoid, "
						  "typsend::pg_catalog.oid as typsendoid, "
4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699
						  "typanalyze::pg_catalog.oid as typanalyzeoid, "
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_catalog.pg_type "
						  "WHERE oid = '%u'::pg_catalog.oid",
						  tinfo->dobj.catId.oid);
	}
	else if (fout->remoteVersion >= 70400)
	{
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, typreceive, typsend, "
						  "'-' as typanalyze, "
						  "typinput::pg_catalog.oid as typinputoid, "
						  "typoutput::pg_catalog.oid as typoutputoid, "
						  "typreceive::pg_catalog.oid as typreceiveoid, "
						  "typsend::pg_catalog.oid as typsendoid, "
						  "0 as typanalyzeoid, "
4700 4701 4702
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_catalog.pg_type "
4703 4704
						  "WHERE oid = '%u'::pg_catalog.oid",
						  tinfo->dobj.catId.oid);
4705 4706
	}
	else if (fout->remoteVersion >= 70300)
4707
	{
4708 4709
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4710
						  "'-' as typreceive, '-' as typsend, "
4711
						  "'-' as typanalyze, "
4712 4713
						  "typinput::pg_catalog.oid as typinputoid, "
						  "typoutput::pg_catalog.oid as typoutputoid, "
4714
						  "0 as typreceiveoid, 0 as typsendoid, "
4715
						  "0 as typanalyzeoid, "
4716 4717 4718
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_catalog.pg_type "
4719 4720
						  "WHERE oid = '%u'::pg_catalog.oid",
						  tinfo->dobj.catId.oid);
4721 4722
	}
	else if (fout->remoteVersion >= 70100)
4723
	{
4724 4725 4726 4727
		/*
		 * Note: although pre-7.3 catalogs contain typreceive and typsend,
		 * ignore them because they are not right.
		 */
4728 4729
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4730
						  "'-' as typreceive, '-' as typsend, "
4731
						  "'-' as typanalyze, "
4732 4733
						  "typinput::oid as typinputoid, "
						  "typoutput::oid as typoutputoid, "
4734
						  "0 as typreceiveoid, 0 as typsendoid, "
4735
						  "0 as typanalyzeoid, "
4736 4737 4738
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_type "
4739 4740
						  "WHERE oid = '%u'::oid",
						  tinfo->dobj.catId.oid);
4741 4742 4743
	}
	else
	{
4744 4745
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4746
						  "'-' as typreceive, '-' as typsend, "
4747
						  "'-' as typanalyze, "
4748 4749
						  "typinput::oid as typinputoid, "
						  "typoutput::oid as typoutputoid, "
4750
						  "0 as typreceiveoid, 0 as typsendoid, "
4751
						  "0 as typanalyzeoid, "
4752 4753 4754
						  "typdelim, typdefault, typbyval, typalign, "
						  "'p'::char as typstorage "
						  "FROM pg_type "
4755 4756
						  "WHERE oid = '%u'::oid",
						  tinfo->dobj.catId.oid);
4757 4758 4759
	}

	res = PQexec(g_conn, query->data);
4760
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}

	typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
	typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
	typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
4774 4775
	typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
	typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
4776
	typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
4777 4778 4779 4780
	typinputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typinputoid")));
	typoutputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typoutputoid")));
	typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
	typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
4781
	typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
4782 4783 4784 4785
	typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4786
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4787 4788 4789 4790
	typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
	typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
	typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));

Bruce Momjian's avatar
Bruce Momjian committed
4791 4792 4793 4794
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
4795
	appendPQExpBuffer(delq, "DROP TYPE %s.",
4796
					  fmtId(tinfo->dobj.namespace->dobj.name));
4797
	appendPQExpBuffer(delq, "%s CASCADE;\n",
4798
					  fmtId(tinfo->dobj.name));
4799 4800

	appendPQExpBuffer(q,
4801
					  "CREATE TYPE %s (\n"
4802
					  "    INTERNALLENGTH = %s",
4803
					  fmtId(tinfo->dobj.name),
4804
					  (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
4805 4806 4807

	if (fout->remoteVersion >= 70300)
	{
4808
		/* regproc result is correctly quoted as of 7.3 */
4809 4810
		appendPQExpBuffer(q, ",\n    INPUT = %s", typinput);
		appendPQExpBuffer(q, ",\n    OUTPUT = %s", typoutput);
4811
		if (OidIsValid(typreceiveoid))
4812
			appendPQExpBuffer(q, ",\n    RECEIVE = %s", typreceive);
4813
		if (OidIsValid(typsendoid))
4814
			appendPQExpBuffer(q, ",\n    SEND = %s", typsend);
4815 4816
		if (OidIsValid(typanalyzeoid))
			appendPQExpBuffer(q, ",\n    ANALYZE = %s", typanalyze);
4817 4818 4819 4820 4821
	}
	else
	{
		/* regproc delivers an unquoted name before 7.3 */
		/* cannot combine these because fmtId uses static result area */
4822 4823
		appendPQExpBuffer(q, ",\n    INPUT = %s", fmtId(typinput));
		appendPQExpBuffer(q, ",\n    OUTPUT = %s", fmtId(typoutput));
4824
		/* no chance that receive/send/analyze need be printed */
Bruce Momjian's avatar
Bruce Momjian committed
4825 4826
	}

4827 4828
	if (typdefault != NULL)
	{
4829 4830
		appendPQExpBuffer(q, ",\n    DEFAULT = ");
		appendStringLiteral(q, typdefault, true);
4831 4832 4833 4834 4835 4836 4837
	}

	if (tinfo->isArray)
	{
		char	   *elemType;

		/* reselect schema in case changed by function dump */
4838
		selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4839
		elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque);
4840
		appendPQExpBuffer(q, ",\n    ELEMENT = %s", elemType);
4841 4842 4843
		free(elemType);
	}

4844 4845 4846 4847 4848 4849
	if (typdelim && strcmp(typdelim, ",") != 0)
	{
		appendPQExpBuffer(q, ",\n    DELIMITER = ");
		appendStringLiteral(q, typdelim, true);
	}

4850
	if (strcmp(typalign, "c") == 0)
4851
		appendPQExpBuffer(q, ",\n    ALIGNMENT = char");
4852
	else if (strcmp(typalign, "s") == 0)
4853
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int2");
4854
	else if (strcmp(typalign, "i") == 0)
4855
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int4");
4856
	else if (strcmp(typalign, "d") == 0)
4857
		appendPQExpBuffer(q, ",\n    ALIGNMENT = double");
4858 4859

	if (strcmp(typstorage, "p") == 0)
4860
		appendPQExpBuffer(q, ",\n    STORAGE = plain");
4861
	else if (strcmp(typstorage, "e") == 0)
4862
		appendPQExpBuffer(q, ",\n    STORAGE = external");
4863
	else if (strcmp(typstorage, "x") == 0)
4864
		appendPQExpBuffer(q, ",\n    STORAGE = extended");
4865
	else if (strcmp(typstorage, "m") == 0)
4866
		appendPQExpBuffer(q, ",\n    STORAGE = main");
4867 4868

	if (strcmp(typbyval, "t") == 0)
4869 4870 4871
		appendPQExpBuffer(q, ",\n    PASSEDBYVALUE");

	appendPQExpBuffer(q, "\n);\n");
4872

4873
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4874 4875
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4876
				 NULL,
4877
				 tinfo->rolname, false,
4878 4879 4880
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4881

Bruce Momjian's avatar
Bruce Momjian committed
4882
	/* Dump Type Comments */
4883 4884
	resetPQExpBuffer(q);

4885
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
4886
	dumpComment(fout, q->data,
4887
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
4888
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
4889 4890

	PQclear(res);
4891 4892
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
4893
	destroyPQExpBuffer(query);
Bruce Momjian's avatar
Bruce Momjian committed
4894 4895
}

4896
/*
4897
 * dumpDomain
Bruce Momjian's avatar
Bruce Momjian committed
4898
 *	  writes out to fout the queries to recreate a user-defined domain
4899
 */
4900
static void
4901
dumpDomain(Archive *fout, TypeInfo *tinfo)
4902 4903 4904 4905
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
4906
	PGresult   *res;
4907
	int			ntups;
4908
	int			i;
4909 4910 4911
	char	   *typnotnull;
	char	   *typdefn;
	char	   *typdefault;
4912

4913
	/* Set proper schema search path so type references list correctly */
4914
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4915

4916
	/* Fetch domain specific details */
4917
	/* We assume here that remoteVersion must be at least 70300 */
4918
	appendPQExpBuffer(query, "SELECT typnotnull, "
Bruce Momjian's avatar
Bruce Momjian committed
4919
			"pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
4920
					  "typdefault "
4921
					  "FROM pg_catalog.pg_type "
4922 4923
					  "WHERE oid = '%u'::pg_catalog.oid",
					  tinfo->dobj.catId.oid);
4924 4925

	res = PQexec(g_conn, query->data);
4926
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4927 4928 4929 4930

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
4931 4932 4933 4934 4935
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}
4936

4937 4938 4939 4940 4941
	typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
	typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4942
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4943 4944 4945

	appendPQExpBuffer(q,
					  "CREATE DOMAIN %s AS %s",
4946
					  fmtId(tinfo->dobj.name),
4947
					  typdefn);
4948

4949
	if (typnotnull[0] == 't')
4950 4951
		appendPQExpBuffer(q, " NOT NULL");

4952
	if (typdefault)
4953
		appendPQExpBuffer(q, " DEFAULT %s", typdefault);
4954

4955 4956 4957
	PQclear(res);

	/*
4958
	 * Add any CHECK constraints for the domain
4959
	 */
4960
	for (i = 0; i < tinfo->nDomChecks; i++)
4961
	{
4962
		ConstraintInfo *domcheck = &(tinfo->domChecks[i]);
4963

4964 4965
		if (!domcheck->separate)
			appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
Bruce Momjian's avatar
Bruce Momjian committed
4966
						   fmtId(domcheck->dobj.name), domcheck->condef);
4967
	}
Bruce Momjian's avatar
Bruce Momjian committed
4968

4969 4970
	appendPQExpBuffer(q, ";\n");

4971 4972 4973 4974 4975
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delq, "DROP DOMAIN %s.",
4976
					  fmtId(tinfo->dobj.namespace->dobj.name));
4977
	appendPQExpBuffer(delq, "%s;\n",
4978
					  fmtId(tinfo->dobj.name));
4979

4980
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4981 4982
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4983
				 NULL,
4984
				 tinfo->rolname, false,
4985 4986 4987
				 "DOMAIN", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4988

Bruce Momjian's avatar
Bruce Momjian committed
4989
	/* Dump Domain Comments */
4990 4991
	resetPQExpBuffer(q);

4992
	appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->dobj.name));
4993
	dumpComment(fout, q->data,
4994
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
4995
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
4996 4997 4998 4999

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
5000 5001
}

Bruce Momjian's avatar
Bruce Momjian committed
5002
/*
5003
 * dumpCompositeType
Bruce Momjian's avatar
Bruce Momjian committed
5004
 *	  writes out to fout the queries to recreate a user-defined stand-alone
5005
 *	  composite type
Bruce Momjian's avatar
Bruce Momjian committed
5006 5007
 */
static void
5008
dumpCompositeType(Archive *fout, TypeInfo *tinfo)
Bruce Momjian's avatar
Bruce Momjian committed
5009 5010 5011 5012 5013 5014
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
Bruce Momjian's avatar
Bruce Momjian committed
5015 5016
	int			i_attname;
	int			i_atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
5017 5018 5019
	int			i;

	/* Set proper schema search path so type references list correctly */
5020
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
5021 5022 5023 5024 5025

	/* Fetch type specific details */
	/* We assume here that remoteVersion must be at least 70300 */

	appendPQExpBuffer(query, "SELECT a.attname, "
Bruce Momjian's avatar
Bruce Momjian committed
5026 5027
		 "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
				  "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
5028
					  "WHERE t.oid = '%u'::pg_catalog.oid "
5029 5030 5031
					  "AND a.attrelid = t.typrelid "
					  "AND NOT a.attisdropped "
					  "ORDER BY a.attnum ",
5032
					  tinfo->dobj.catId.oid);
Bruce Momjian's avatar
Bruce Momjian committed
5033 5034

	res = PQexec(g_conn, query->data);
5035
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
Bruce Momjian's avatar
Bruce Momjian committed
5036 5037 5038 5039 5040

	/* Expecting at least a single result */
	ntups = PQntuples(res);
	if (ntups < 1)
	{
5041
		write_msg(NULL, "query yielded no rows: %s\n", query->data);
Bruce Momjian's avatar
Bruce Momjian committed
5042 5043 5044
		exit_nicely();
	}

5045 5046
	i_attname = PQfnumber(res, "attname");
	i_atttypdefn = PQfnumber(res, "atttypdefn");
Bruce Momjian's avatar
Bruce Momjian committed
5047

5048
	appendPQExpBuffer(q, "CREATE TYPE %s AS (",
5049
					  fmtId(tinfo->dobj.name));
Bruce Momjian's avatar
Bruce Momjian committed
5050 5051 5052

	for (i = 0; i < ntups; i++)
	{
5053 5054
		char	   *attname;
		char	   *atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
5055

5056 5057
		attname = PQgetvalue(res, i, i_attname);
		atttypdefn = PQgetvalue(res, i, i_atttypdefn);
Bruce Momjian's avatar
Bruce Momjian committed
5058

5059 5060 5061
		appendPQExpBuffer(q, "\n\t%s %s", fmtId(attname), atttypdefn);
		if (i < ntups - 1)
			appendPQExpBuffer(q, ",");
Bruce Momjian's avatar
Bruce Momjian committed
5062
	}
5063
	appendPQExpBuffer(q, "\n);\n");
Bruce Momjian's avatar
Bruce Momjian committed
5064

Bruce Momjian's avatar
Bruce Momjian committed
5065 5066 5067 5068
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5069
	appendPQExpBuffer(delq, "DROP TYPE %s.",
5070
					  fmtId(tinfo->dobj.namespace->dobj.name));
5071
	appendPQExpBuffer(delq, "%s;\n",
5072
					  fmtId(tinfo->dobj.name));
5073

5074
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
5075 5076
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
5077
				 NULL,
5078
				 tinfo->rolname, false,
5079 5080 5081
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
5082

5083

5084 5085
	/* Dump Type Comments */
	resetPQExpBuffer(q);
5086

5087
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
5088
	dumpComment(fout, q->data,
5089
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
5090
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
5091

5092 5093 5094 5095 5096
	PQclear(res);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
}
5097

5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110
/*
 * dumpProcLang
 *		  writes out to fout the queries to recreate a user-defined
 *		  procedural language
 */
static void
dumpProcLang(Archive *fout, ProcLangInfo *plang)
{
	PQExpBuffer defqry;
	PQExpBuffer delqry;
	char	   *qlanname;
	FuncInfo   *funcInfo;
	FuncInfo   *validatorInfo = NULL;
5111

5112 5113
	if (dataOnly)
		return;
5114

5115
	/*
5116 5117 5118
	 * We dump PLs iff their underlying call handler functions have been
	 * marked as language functions (or have a non-system OID in
	 * pre-7.3 databases).	We treat the PL itself as being in
Bruce Momjian's avatar
Bruce Momjian committed
5119 5120
	 * the underlying function's namespace, though it isn't really.  This
	 * avoids searchpath problems for the HANDLER clause.
5121 5122
	 *
	 */
5123

5124 5125 5126
	funcInfo = findFuncByOid(plang->lanplcallfoid);
	if (funcInfo == NULL)
		return;
5127

5128
	if (!funcInfo->isProlangFunc && !funcInfo->dobj.namespace->dump)
5129
		return;
5130

5131 5132 5133 5134 5135 5136
	if (OidIsValid(plang->lanvalidator))
	{
		validatorInfo = findFuncByOid(plang->lanvalidator);
		if (validatorInfo == NULL)
			return;
	}
5137

5138 5139
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
5140

5141
	qlanname = strdup(fmtId(plang->dobj.name));
5142

5143 5144
	appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
					  qlanname);
5145

5146 5147 5148 5149
	appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
					  plang->lanpltrusted ? "TRUSTED " : "",
					  qlanname);
	appendPQExpBuffer(defqry, " HANDLER %s",
5150
					  fmtId(funcInfo->dobj.name));
5151 5152 5153 5154
	if (OidIsValid(plang->lanvalidator))
	{
		appendPQExpBuffer(defqry, " VALIDATOR ");
		/* Cope with possibility that validator is in different schema */
5155
		if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
5156
			appendPQExpBuffer(defqry, "%s.",
Bruce Momjian's avatar
Bruce Momjian committed
5157
						fmtId(validatorInfo->dobj.namespace->dobj.name));
5158
		appendPQExpBuffer(defqry, "%s",
5159
						  fmtId(validatorInfo->dobj.name));
5160
	}
5161
	appendPQExpBuffer(defqry, ";\n");
5162

5163
	ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
5164
				 plang->dobj.name,
5165
				 funcInfo->dobj.namespace->dobj.name, NULL, "",
5166
				 false, "PROCEDURAL LANGUAGE",
5167 5168 5169
				 defqry->data, delqry->data, NULL,
				 plang->dobj.dependencies, plang->dobj.nDeps,
				 NULL, NULL);
5170

5171 5172
	/* Dump Proc Lang Comments */
	resetPQExpBuffer(defqry);
5173

5174 5175 5176 5177
	appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
	dumpComment(fout, defqry->data,
				NULL, "",
				plang->dobj.catId, 0, plang->dobj.dumpId);
5178

5179 5180
	if (plang->lanpltrusted)
		dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
5181 5182
				qlanname, plang->dobj.name,
				funcInfo->dobj.namespace->dobj.name,
5183
				NULL, plang->lanacl);
5184

5185 5186 5187 5188
	free(qlanname);

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5189 5190
}

5191
/*
5192
 * format_function_arguments: generate function name and argument list
5193 5194 5195
 *
 * The argument type names are qualified if needed.  The function name
 * is never qualified.
5196
 *
5197
 * Any or all of allargtypes, argmodes, argnames may be NULL.
5198
 */
5199
static char *
5200 5201 5202 5203
format_function_arguments(FuncInfo *finfo, int nallargs,
						  char **allargtypes,
						  char **argmodes,
						  char **argnames)
5204 5205 5206 5207 5208
{
	PQExpBufferData fn;
	int			j;

	initPQExpBuffer(&fn);
5209 5210
	appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
	for (j = 0; j < nallargs; j++)
5211
	{
5212
		Oid			typid;
5213
		char	   *typname;
5214 5215
		const char *argmode;
		const char *argname;
5216

5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240
		typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
		typname = getFormattedTypeName(typid, zeroAsOpaque);

		if (argmodes)
		{
			switch (argmodes[j][0])
			{
				case 'i':
					argmode = "";
					break;
				case 'o':
					argmode = "OUT ";
					break;
				case 'b':
					argmode = "INOUT ";
					break;
				default:
					write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
					argmode = "";
					break;
			}
		}
		else
			argmode = "";
5241 5242 5243 5244 5245

		argname = argnames ? argnames[j] : (char *) NULL;
		if (argname && argname[0] == '\0')
			argname = NULL;

5246
		appendPQExpBuffer(&fn, "%s%s%s%s%s",
5247
						  (j > 0) ? ", " : "",
5248
						  argmode,
5249 5250
						  argname ? fmtId(argname) : "",
						  argname ? " " : "",
5251 5252 5253 5254 5255 5256 5257
						  typname);
		free(typname);
	}
	appendPQExpBuffer(&fn, ")");
	return fn.data;
}

5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293
/*
 * format_function_signature: generate function name and argument list
 *
 * This is like format_function_arguments except that only a minimal
 * list of input argument types is generated; this is sufficient to
 * reference the function, but not to define it.
 *
 * If honor_quotes is false then the function name is never quoted.
 * This is appropriate for use in TOC tags, but not in SQL commands.
 */
static char *
format_function_signature(FuncInfo *finfo, bool honor_quotes)
{
	PQExpBufferData fn;
	int			j;

	initPQExpBuffer(&fn);
	if (honor_quotes)
		appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
	else
		appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
	for (j = 0; j < finfo->nargs; j++)
	{
		char	   *typname;

		typname = getFormattedTypeName(finfo->argtypes[j], zeroAsOpaque);

		appendPQExpBuffer(&fn, "%s%s",
						  (j > 0) ? ", " : "",
						  typname);
		free(typname);
	}
	appendPQExpBuffer(&fn, ")");
	return fn.data;
}

5294

5295
/*
5296 5297
 * dumpFunc:
 *	  dump out one function
5298
 */
5299
static void
5300
dumpFunc(Archive *fout, FuncInfo *finfo)
5301
{
5302 5303 5304 5305 5306 5307 5308
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delqry;
	PQExpBuffer asPart;
	PGresult   *res;
	char	   *funcsig;
	char	   *funcsig_tag;
5309 5310 5311 5312
	int			ntups;
	char	   *proretset;
	char	   *prosrc;
	char	   *probin;
5313 5314
	char	   *proallargtypes;
	char	   *proargmodes;
5315
	char	   *proargnames;
5316 5317
	char	   *provolatile;
	char	   *proisstrict;
5318
	char	   *prosecdef;
5319
	char	   *lanname;
5320
	char	   *rettypename;
5321 5322 5323 5324
	int			nallargs;
	char	  **allargtypes = NULL;
	char	  **argmodes = NULL;
	char	  **argnames = NULL;
5325

5326 5327
	/* Dump only funcs in dumpable namespaces, or needed language handlers */
	if ((!finfo->isProlangFunc && !finfo->dobj.namespace->dump) || dataOnly)
5328
		return;
5329

5330

5331 5332 5333 5334
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delqry = createPQExpBuffer();
	asPart = createPQExpBuffer();
5335

5336
	/* Set proper schema search path so type references list correctly */
5337
	selectSourceSchema(finfo->dobj.namespace->dobj.name);
5338 5339

	/* Fetch function-specific details */
5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351
	if (g_fout->remoteVersion >= 80100)
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
						  "proallargtypes, proargmodes, proargnames, "
						  "provolatile, proisstrict, prosecdef, "
						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
						  "FROM pg_catalog.pg_proc "
						  "WHERE oid = '%u'::pg_catalog.oid",
						  finfo->dobj.catId.oid);
	}
	else if (g_fout->remoteVersion >= 80000)
5352 5353 5354
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5355 5356
						  "null as proallargtypes, "
						  "null as proargmodes, "
5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367
						  "proargnames, "
						  "provolatile, proisstrict, prosecdef, "
						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
						  "FROM pg_catalog.pg_proc "
						  "WHERE oid = '%u'::pg_catalog.oid",
						  finfo->dobj.catId.oid);
	}
	else if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5368 5369 5370
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
5371
						  "provolatile, proisstrict, prosecdef, "
5372
						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
5373
						  "FROM pg_catalog.pg_proc "
5374 5375
						  "WHERE oid = '%u'::pg_catalog.oid",
						  finfo->dobj.catId.oid);
5376 5377 5378 5379 5380
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5381 5382 5383
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
Bruce Momjian's avatar
Bruce Momjian committed
5384
		 "case when proiscachable then 'i' else 'v' end as provolatile, "
5385
						  "proisstrict, "
5386
						  "'f'::boolean as prosecdef, "
5387
						  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5388
						  "FROM pg_proc "
5389 5390
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5391 5392 5393 5394 5395
	}
	else
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5396 5397 5398
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
Bruce Momjian's avatar
Bruce Momjian committed
5399
		 "case when proiscachable then 'i' else 'v' end as provolatile, "
5400
						  "'f'::boolean as proisstrict, "
5401
						  "'f'::boolean as prosecdef, "
5402
						  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5403
						  "FROM pg_proc "
5404 5405
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5406
	}
5407

5408
	res = PQexec(g_conn, query->data);
5409
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
5410

5411 5412 5413
	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
5414
	{
5415 5416
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
5417
		exit_nicely();
5418 5419
	}

5420 5421 5422
	proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
	prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
	probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
5423 5424
	proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
	proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
5425
	proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
5426 5427
	provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
	proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
5428
	prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
5429
	lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
5430

5431
	/*
5432 5433
	 * See backend/commands/define.c for details of how the 'AS' clause is
	 * used.
5434
	 */
5435
	if (strcmp(probin, "-") != 0)
5436
	{
5437
		appendPQExpBuffer(asPart, "AS ");
5438
		appendStringLiteral(asPart, probin, true);
5439
		if (strcmp(prosrc, "-") != 0)
5440 5441
		{
			appendPQExpBuffer(asPart, ", ");
Bruce Momjian's avatar
Bruce Momjian committed
5442

5443
			/*
5444 5445 5446 5447 5448 5449 5450
			 * where we have bin, use dollar quoting if allowed and src
			 * contains quote or backslash; else use regular quoting.
			 */
			if (disable_dollar_quoting)
				appendStringLiteral(asPart, prosrc, false);
			else
				appendStringLiteralDQOpt(asPart, prosrc, false, NULL);
5451
		}
5452 5453 5454
	}
	else
	{
5455
		if (strcmp(prosrc, "-") != 0)
5456 5457
		{
			appendPQExpBuffer(asPart, "AS ");
5458 5459 5460 5461 5462
			/* with no bin, dollar quote src unconditionally if allowed */
			if (disable_dollar_quoting)
				appendStringLiteral(asPart, prosrc, false);
			else
				appendStringLiteralDQ(asPart, prosrc, NULL);
5463
		}
5464 5465
	}

5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497
	nallargs = finfo->nargs;	/* unless we learn different from allargs */

	if (proallargtypes && *proallargtypes)
	{
		int			nitems = 0;

		if (!parsePGArray(proallargtypes, &allargtypes, &nitems) ||
			nitems < finfo->nargs)
		{
			write_msg(NULL, "WARNING: could not parse proallargtypes array\n");
			if (allargtypes)
				free(allargtypes);
			allargtypes = NULL;
		}
		else
			nallargs = nitems;
	}

	if (proargmodes && *proargmodes)
	{
		int			nitems = 0;

		if (!parsePGArray(proargmodes, &argmodes, &nitems) ||
			nitems != nallargs)
		{
			write_msg(NULL, "WARNING: could not parse proargmodes array\n");
			if (argmodes)
				free(argmodes);
			argmodes = NULL;
		}
	}

5498 5499
	if (proargnames && *proargnames)
	{
Bruce Momjian's avatar
Bruce Momjian committed
5500
		int			nitems = 0;
5501

5502 5503
		if (!parsePGArray(proargnames, &argnames, &nitems) ||
			nitems != nallargs)
5504 5505
		{
			write_msg(NULL, "WARNING: could not parse proargnames array\n");
5506 5507 5508
			if (argnames)
				free(argnames);
			argnames = NULL;
5509 5510 5511
		}
	}

5512 5513 5514
	funcsig = format_function_arguments(finfo, nallargs, allargtypes,
										argmodes, argnames);
	funcsig_tag = format_function_signature(finfo, false);
5515

Bruce Momjian's avatar
Bruce Momjian committed
5516 5517 5518 5519
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5520
	appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
5521
					  fmtId(finfo->dobj.namespace->dobj.name),
5522
					  funcsig);
5523

5524
	rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
5525

5526
	appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
5527
	appendPQExpBuffer(q, "RETURNS %s%s\n    %s\n    LANGUAGE %s",
5528
					  (proretset[0] == 't') ? "SETOF " : "",
5529
					  rettypename,
5530
					  asPart->data,
5531
					  fmtId(lanname));
5532 5533

	free(rettypename);
5534

5535
	if (provolatile[0] != PROVOLATILE_VOLATILE)
5536
	{
5537
		if (provolatile[0] == PROVOLATILE_IMMUTABLE)
5538
			appendPQExpBuffer(q, " IMMUTABLE");
5539
		else if (provolatile[0] == PROVOLATILE_STABLE)
5540
			appendPQExpBuffer(q, " STABLE");
5541
		else if (provolatile[0] != PROVOLATILE_VOLATILE)
5542
		{
5543
			write_msg(NULL, "unrecognized provolatile value for function \"%s\"\n",
5544
					  finfo->dobj.name);
5545 5546
			exit_nicely();
		}
5547
	}
5548

5549 5550
	if (proisstrict[0] == 't')
		appendPQExpBuffer(q, " STRICT");
5551

5552 5553 5554
	if (prosecdef[0] == 't')
		appendPQExpBuffer(q, " SECURITY DEFINER");

5555 5556
	appendPQExpBuffer(q, ";\n");

5557 5558
	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
				 funcsig_tag,
5559
				 finfo->dobj.namespace->dobj.name,
5560
				 NULL,
5561
				 finfo->rolname, false,
5562 5563 5564
				 "FUNCTION", q->data, delqry->data, NULL,
				 finfo->dobj.dependencies, finfo->dobj.nDeps,
				 NULL, NULL);
5565

Bruce Momjian's avatar
Bruce Momjian committed
5566
	/* Dump Function Comments */
Bruce Momjian's avatar
Bruce Momjian committed
5567
	resetPQExpBuffer(q);
5568
	appendPQExpBuffer(q, "FUNCTION %s", funcsig);
5569
	dumpComment(fout, q->data,
5570
				finfo->dobj.namespace->dobj.name, finfo->rolname,
5571 5572 5573 5574
				finfo->dobj.catId, 0, finfo->dobj.dumpId);

	dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
			funcsig, funcsig_tag,
5575
			finfo->dobj.namespace->dobj.name,
5576
			finfo->rolname, finfo->proacl);
5577

5578 5579 5580
	PQclear(res);

	destroyPQExpBuffer(query);
5581 5582 5583
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delqry);
	destroyPQExpBuffer(asPart);
5584
	free(funcsig);
5585
	free(funcsig_tag);
5586 5587 5588 5589 5590 5591
	if (allargtypes)
		free(allargtypes);
	if (argmodes)
		free(argmodes);
	if (argnames)
		free(argnames);
5592 5593
}

5594 5595

/*
5596
 * Dump a user-defined cast
5597
 */
5598 5599
static void
dumpCast(Archive *fout, CastInfo *cast)
5600
{
5601 5602 5603 5604 5605 5606
	PQExpBuffer defqry;
	PQExpBuffer delqry;
	PQExpBuffer castsig;
	FuncInfo   *funcInfo = NULL;
	TypeInfo   *sourceInfo;
	TypeInfo   *targetInfo;
5607

5608 5609
	if (dataOnly)
		return;
5610

5611
	if (OidIsValid(cast->castfunc))
5612
	{
5613 5614 5615
		funcInfo = findFuncByOid(cast->castfunc);
		if (funcInfo == NULL)
			return;
5616 5617
	}

5618 5619 5620 5621
	/*
	 * As per discussion we dump casts if one or more of the underlying
	 * objects (the conversion function and the two data types) are not
	 * builtin AND if all of the non-builtin objects namespaces are
Bruce Momjian's avatar
Bruce Momjian committed
5622 5623
	 * included in the dump. Builtin meaning, the namespace name does not
	 * start with "pg_".
5624 5625 5626
	 */
	sourceInfo = findTypeByOid(cast->castsource);
	targetInfo = findTypeByOid(cast->casttarget);
5627

5628 5629
	if (sourceInfo == NULL || targetInfo == NULL)
		return;
5630

5631 5632 5633
	/*
	 * Skip this cast if all objects are from pg_
	 */
5634 5635 5636 5637
	if ((funcInfo == NULL ||
		 strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) &&
		strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) == 0 &&
		strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) == 0)
5638
		return;
5639

5640
	/*
Bruce Momjian's avatar
Bruce Momjian committed
5641 5642
	 * Skip cast if function isn't from pg_ and that namespace is not
	 * dumped.
5643
	 */
5644
	if (funcInfo &&
5645 5646
		strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!funcInfo->dobj.namespace->dump)
5647
		return;
5648

5649 5650 5651
	/*
	 * Same for the Source type
	 */
5652 5653
	if (strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!sourceInfo->dobj.namespace->dump)
5654
		return;
5655

5656 5657 5658
	/*
	 * and the target type.
	 */
5659 5660
	if (strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!targetInfo->dobj.namespace->dump)
5661
		return;
5662

5663 5664
	/* Make sure we are in proper schema (needed for getFormattedTypeName) */
	selectSourceSchema("pg_catalog");
5665

5666 5667 5668
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
	castsig = createPQExpBuffer();
5669

5670 5671 5672
	appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5673

5674 5675 5676
	appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5677

5678 5679 5680
	if (!OidIsValid(cast->castfunc))
		appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
	else
5681 5682
	{
		/*
Bruce Momjian's avatar
Bruce Momjian committed
5683 5684
		 * Always qualify the function name, in case it is not in
		 * pg_catalog schema (format_function_signature won't qualify it).
5685 5686
		 */
		appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
5687
						  fmtId(funcInfo->dobj.namespace->dobj.name));
5688
		appendPQExpBuffer(defqry, "%s",
5689
						  format_function_signature(funcInfo, true));
5690
	}
5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703

	if (cast->castcontext == 'a')
		appendPQExpBuffer(defqry, " AS ASSIGNMENT");
	else if (cast->castcontext == 'i')
		appendPQExpBuffer(defqry, " AS IMPLICIT");
	appendPQExpBuffer(defqry, ";\n");

	appendPQExpBuffer(castsig, "CAST (%s AS %s)",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));

	ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
				 castsig->data,
5704
				 "pg_catalog", NULL, "",
5705
				 false, "CAST", defqry->data, delqry->data, NULL,
5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716
				 cast->dobj.dependencies, cast->dobj.nDeps,
				 NULL, NULL);

	/* Dump Cast Comments */
	resetPQExpBuffer(defqry);
	appendPQExpBuffer(defqry, "CAST (%s AS %s)",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
	dumpComment(fout, defqry->data,
				NULL, "",
				cast->dobj.catId, 0, cast->dobj.dumpId);
5717 5718 5719

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5720
	destroyPQExpBuffer(castsig);
5721 5722
}

5723
/*
5724
 * dumpOpr
5725 5726 5727
 *	  write out a single operator definition
 */
static void
5728
dumpOpr(Archive *fout, OprInfo *oprinfo)
5729
{
5730 5731 5732 5733 5734
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer oprid;
	PQExpBuffer details;
5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764
	const char *name;
	PGresult   *res;
	int			ntups;
	int			i_oprkind;
	int			i_oprcode;
	int			i_oprleft;
	int			i_oprright;
	int			i_oprcom;
	int			i_oprnegate;
	int			i_oprrest;
	int			i_oprjoin;
	int			i_oprcanhash;
	int			i_oprlsortop;
	int			i_oprrsortop;
	int			i_oprltcmpop;
	int			i_oprgtcmpop;
	char	   *oprkind;
	char	   *oprcode;
	char	   *oprleft;
	char	   *oprright;
	char	   *oprcom;
	char	   *oprnegate;
	char	   *oprrest;
	char	   *oprjoin;
	char	   *oprcanhash;
	char	   *oprlsortop;
	char	   *oprrsortop;
	char	   *oprltcmpop;
	char	   *oprgtcmpop;

5765
	/* Dump only operators in dumpable namespaces */
5766
	if (!oprinfo->dobj.namespace->dump || dataOnly)
5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781
		return;

	/*
	 * some operators are invalid because they were the result of user
	 * defining operators before commutators exist
	 */
	if (!OidIsValid(oprinfo->oprcode))
		return;

	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
	oprid = createPQExpBuffer();
	details = createPQExpBuffer();

5782
	/* Make sure we are in proper schema so regoperator works correctly */
5783
	selectSourceSchema(oprinfo->dobj.namespace->dobj.name);
5784 5785 5786

	if (g_fout->remoteVersion >= 70300)
	{
5787 5788 5789 5790 5791 5792 5793 5794
		appendPQExpBuffer(query, "SELECT oprkind, "
						  "oprcode::pg_catalog.regprocedure, "
						  "oprleft::pg_catalog.regtype, "
						  "oprright::pg_catalog.regtype, "
						  "oprcom::pg_catalog.regoperator, "
						  "oprnegate::pg_catalog.regoperator, "
						  "oprrest::pg_catalog.regprocedure, "
						  "oprjoin::pg_catalog.regprocedure, "
5795
						  "oprcanhash, "
5796 5797 5798 5799 5800
						  "oprlsortop::pg_catalog.regoperator, "
						  "oprrsortop::pg_catalog.regoperator, "
						  "oprltcmpop::pg_catalog.regoperator, "
						  "oprgtcmpop::pg_catalog.regoperator "
						  "from pg_catalog.pg_operator "
5801 5802
						  "where oid = '%u'::pg_catalog.oid",
						  oprinfo->dobj.catId.oid);
5803 5804 5805 5806 5807
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
						  "CASE WHEN oprleft = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
5808
					   "ELSE format_type(oprleft, NULL) END as oprleft, "
5809
						  "CASE WHEN oprright = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
5810
					 "ELSE format_type(oprright, NULL) END as oprright, "
5811 5812 5813 5814
						  "oprcom, oprnegate, oprrest, oprjoin, "
						  "oprcanhash, oprlsortop, oprrsortop, "
						  "0 as oprltcmpop, 0 as oprgtcmpop "
						  "from pg_operator "
5815 5816
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828
	}
	else
	{
		appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
						  "CASE WHEN oprleft = 0 THEN '-'::name "
						  "ELSE (select typname from pg_type where oid = oprleft) END as oprleft, "
						  "CASE WHEN oprright = 0 THEN '-'::name "
						  "ELSE (select typname from pg_type where oid = oprright) END as oprright, "
						  "oprcom, oprnegate, oprrest, oprjoin, "
						  "oprcanhash, oprlsortop, oprrsortop, "
						  "0 as oprltcmpop, 0 as oprgtcmpop "
						  "from pg_operator "
5829 5830
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5831
	}
5832

5833
	res = PQexec(g_conn, query->data);
5834
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
5835

5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872
	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}

	i_oprkind = PQfnumber(res, "oprkind");
	i_oprcode = PQfnumber(res, "oprcode");
	i_oprleft = PQfnumber(res, "oprleft");
	i_oprright = PQfnumber(res, "oprright");
	i_oprcom = PQfnumber(res, "oprcom");
	i_oprnegate = PQfnumber(res, "oprnegate");
	i_oprrest = PQfnumber(res, "oprrest");
	i_oprjoin = PQfnumber(res, "oprjoin");
	i_oprcanhash = PQfnumber(res, "oprcanhash");
	i_oprlsortop = PQfnumber(res, "oprlsortop");
	i_oprrsortop = PQfnumber(res, "oprrsortop");
	i_oprltcmpop = PQfnumber(res, "oprltcmpop");
	i_oprgtcmpop = PQfnumber(res, "oprgtcmpop");

	oprkind = PQgetvalue(res, 0, i_oprkind);
	oprcode = PQgetvalue(res, 0, i_oprcode);
	oprleft = PQgetvalue(res, 0, i_oprleft);
	oprright = PQgetvalue(res, 0, i_oprright);
	oprcom = PQgetvalue(res, 0, i_oprcom);
	oprnegate = PQgetvalue(res, 0, i_oprnegate);
	oprrest = PQgetvalue(res, 0, i_oprrest);
	oprjoin = PQgetvalue(res, 0, i_oprjoin);
	oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
	oprlsortop = PQgetvalue(res, 0, i_oprlsortop);
	oprrsortop = PQgetvalue(res, 0, i_oprrsortop);
	oprltcmpop = PQgetvalue(res, 0, i_oprltcmpop);
	oprgtcmpop = PQgetvalue(res, 0, i_oprgtcmpop);

5873
	appendPQExpBuffer(details, "    PROCEDURE = %s",
5874 5875 5876
					  convertRegProcReference(oprcode));

	appendPQExpBuffer(oprid, "%s (",
5877
					  oprinfo->dobj.name);
5878 5879

	/*
Bruce Momjian's avatar
Bruce Momjian committed
5880 5881
	 * right unary means there's a left arg and left unary means there's a
	 * right arg
5882 5883 5884 5885 5886 5887 5888
	 */
	if (strcmp(oprkind, "r") == 0 ||
		strcmp(oprkind, "b") == 0)
	{
		if (g_fout->remoteVersion >= 70100)
			name = oprleft;
		else
5889 5890
			name = fmtId(oprleft);
		appendPQExpBuffer(details, ",\n    LEFTARG = %s", name);
5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901
		appendPQExpBuffer(oprid, "%s", name);
	}
	else
		appendPQExpBuffer(oprid, "NONE");

	if (strcmp(oprkind, "l") == 0 ||
		strcmp(oprkind, "b") == 0)
	{
		if (g_fout->remoteVersion >= 70100)
			name = oprright;
		else
5902 5903
			name = fmtId(oprright);
		appendPQExpBuffer(details, ",\n    RIGHTARG = %s", name);
5904 5905 5906 5907 5908
		appendPQExpBuffer(oprid, ", %s)", name);
	}
	else
		appendPQExpBuffer(oprid, ", NONE)");

5909
	name = convertOperatorReference(oprcom);
5910
	if (name)
5911
		appendPQExpBuffer(details, ",\n    COMMUTATOR = %s", name);
5912

5913
	name = convertOperatorReference(oprnegate);
5914
	if (name)
5915
		appendPQExpBuffer(details, ",\n    NEGATOR = %s", name);
5916 5917

	if (strcmp(oprcanhash, "t") == 0)
5918
		appendPQExpBuffer(details, ",\n    HASHES");
5919 5920 5921

	name = convertRegProcReference(oprrest);
	if (name)
5922
		appendPQExpBuffer(details, ",\n    RESTRICT = %s", name);
5923 5924 5925

	name = convertRegProcReference(oprjoin);
	if (name)
5926
		appendPQExpBuffer(details, ",\n    JOIN = %s", name);
5927

5928
	name = convertOperatorReference(oprlsortop);
5929
	if (name)
5930
		appendPQExpBuffer(details, ",\n    SORT1 = %s", name);
5931

5932
	name = convertOperatorReference(oprrsortop);
5933
	if (name)
5934
		appendPQExpBuffer(details, ",\n    SORT2 = %s", name);
5935

5936
	name = convertOperatorReference(oprltcmpop);
5937
	if (name)
5938
		appendPQExpBuffer(details, ",\n    LTCMP = %s", name);
5939

5940
	name = convertOperatorReference(oprgtcmpop);
5941
	if (name)
5942
		appendPQExpBuffer(details, ",\n    GTCMP = %s", name);
5943

Bruce Momjian's avatar
Bruce Momjian committed
5944 5945 5946 5947
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5948
	appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
5949
					  fmtId(oprinfo->dobj.namespace->dobj.name),
5950 5951
					  oprid->data);

5952
	appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n",
5953
					  oprinfo->dobj.name, details->data);
5954

5955
	ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
5956
				 oprinfo->dobj.name,
5957 5958
				 oprinfo->dobj.namespace->dobj.name, 
				 NULL,
5959
				 oprinfo->rolname,
5960
				 false, "OPERATOR", q->data, delq->data, NULL,
5961 5962
				 oprinfo->dobj.dependencies, oprinfo->dobj.nDeps,
				 NULL, NULL);
5963

Bruce Momjian's avatar
Bruce Momjian committed
5964
	/* Dump Operator Comments */
5965 5966 5967
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
	dumpComment(fout, q->data,
5968
				oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
5969
				oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996

	PQclear(res);

	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(oprid);
	destroyPQExpBuffer(details);
}

/*
 * Convert a function reference obtained from pg_operator
 *
 * Returns what to print, or NULL if function references is InvalidOid
 *
 * In 7.3 the input is a REGPROCEDURE display; we have to strip the
 * argument-types part.  In prior versions, the input is a REGPROC display.
 */
static const char *
convertRegProcReference(const char *proc)
{
	/* In all cases "-" means a null reference */
	if (strcmp(proc, "-") == 0)
		return NULL;

	if (g_fout->remoteVersion >= 70300)
	{
Bruce Momjian's avatar
Bruce Momjian committed
5997 5998 5999
		char	   *name;
		char	   *paren;
		bool		inquote;
6000 6001 6002 6003 6004

		name = strdup(proc);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
6005
		{
6006
			if (*paren == '(' && !inquote)
6007
			{
6008 6009
				*paren = '\0';
				break;
6010
			}
6011 6012
			if (*paren == '"')
				inquote = !inquote;
6013
		}
6014 6015 6016 6017
		return name;
	}

	/* REGPROC before 7.3 does not quote its result */
6018
	return fmtId(proc);
6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030
}

/*
 * Convert an operator cross-reference obtained from pg_operator
 *
 * Returns what to print, or NULL to print nothing
 *
 * In 7.3 the input is a REGOPERATOR display; we have to strip the
 * argument-types part.  In prior versions, the input is just a
 * numeric OID, which we search our operator list for.
 */
static const char *
6031
convertOperatorReference(const char *opr)
6032
{
Bruce Momjian's avatar
Bruce Momjian committed
6033
	OprInfo    *oprInfo;
6034 6035 6036 6037 6038 6039 6040

	/* In all cases "0" means a null reference */
	if (strcmp(opr, "0") == 0)
		return NULL;

	if (g_fout->remoteVersion >= 70300)
	{
6041
		char	   *name;
Bruce Momjian's avatar
Bruce Momjian committed
6042 6043
		char	   *paren;
		bool		inquote;
6044

6045 6046 6047 6048
		name = strdup(opr);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
6049
		{
6050
			if (*paren == '(' && !inquote)
6051
			{
6052 6053
				*paren = '\0';
				break;
6054
			}
6055 6056
			if (*paren == '"')
				inquote = !inquote;
6057
		}
6058
		return name;
6059
	}
6060

6061 6062 6063
	oprInfo = findOprByOid(atooid(opr));
	if (oprInfo == NULL)
	{
6064
		write_msg(NULL, "WARNING: could not find operator with OID %s\n",
6065
				  opr);
6066
		return NULL;
6067
	}
6068
	return oprInfo->dobj.name;
6069 6070 6071
}

/*
6072
 * dumpOpclass
6073 6074 6075
 *	  write out a single operator class definition
 */
static void
6076
dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
6077
{
6078 6079 6080
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103
	PGresult   *res;
	int			ntups;
	int			i_opcintype;
	int			i_opckeytype;
	int			i_opcdefault;
	int			i_amname;
	int			i_amopstrategy;
	int			i_amopreqcheck;
	int			i_amopopr;
	int			i_amprocnum;
	int			i_amproc;
	char	   *opcintype;
	char	   *opckeytype;
	char	   *opcdefault;
	char	   *amname;
	char	   *amopstrategy;
	char	   *amopreqcheck;
	char	   *amopopr;
	char	   *amprocnum;
	char	   *amproc;
	bool		needComma;
	int			i;

6104
	/* Dump only opclasses in dumpable namespaces */
6105
	if (!opcinfo->dobj.namespace->dump || dataOnly)
6106 6107
		return;

6108 6109 6110 6111 6112 6113 6114 6115
	/*
	 * XXX currently we do not implement dumping of operator classes from
	 * pre-7.3 databases.  This could be done but it seems not worth the
	 * trouble.
	 */
	if (g_fout->remoteVersion < 70300)
		return;

6116 6117 6118 6119
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();

6120
	/* Make sure we are in proper schema so regoperator works correctly */
6121
	selectSourceSchema(opcinfo->dobj.namespace->dobj.name);
6122 6123 6124 6125 6126

	/* Get additional fields from the pg_opclass row */
	appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
					  "opckeytype::pg_catalog.regtype, "
					  "opcdefault, "
Bruce Momjian's avatar
Bruce Momjian committed
6127
	"(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
6128
					  "FROM pg_catalog.pg_opclass "
6129 6130
					  "WHERE oid = '%u'::pg_catalog.oid",
					  opcinfo->dobj.catId.oid);
6131 6132

	res = PQexec(g_conn, query->data);
6133
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}

	i_opcintype = PQfnumber(res, "opcintype");
	i_opckeytype = PQfnumber(res, "opckeytype");
	i_opcdefault = PQfnumber(res, "opcdefault");
	i_amname = PQfnumber(res, "amname");

	opcintype = PQgetvalue(res, 0, i_opcintype);
	opckeytype = PQgetvalue(res, 0, i_opckeytype);
	opcdefault = PQgetvalue(res, 0, i_opcdefault);
6152 6153
	/* amname will still be needed after we PQclear res */
	amname = strdup(PQgetvalue(res, 0, i_amname));
6154

Bruce Momjian's avatar
Bruce Momjian committed
6155 6156 6157 6158
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
6159
	appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
6160
					  fmtId(opcinfo->dobj.namespace->dobj.name));
6161
	appendPQExpBuffer(delq, ".%s",
6162
					  fmtId(opcinfo->dobj.name));
6163
	appendPQExpBuffer(delq, " USING %s;\n",
6164
					  fmtId(amname));
6165 6166

	/* Build the fixed portion of the CREATE command */
6167
	appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n    ",
6168
					  fmtId(opcinfo->dobj.name));
6169 6170
	if (strcmp(opcdefault, "t") == 0)
		appendPQExpBuffer(q, "DEFAULT ");
6171
	appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n    ",
6172
					  opcintype,
6173
					  fmtId(amname));
6174 6175 6176 6177 6178

	needComma = false;

	if (strcmp(opckeytype, "-") != 0)
	{
6179
		appendPQExpBuffer(q, "STORAGE %s",
6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193
						  opckeytype);
		needComma = true;
	}

	PQclear(res);

	/*
	 * Now fetch and print the OPERATOR entries (pg_amop rows).
	 */
	resetPQExpBuffer(query);

	appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
					  "amopopr::pg_catalog.regoperator "
					  "FROM pg_catalog.pg_amop "
6194
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
6195
					  "ORDER BY amopstrategy",
6196
					  opcinfo->dobj.catId.oid);
6197 6198

	res = PQexec(g_conn, query->data);
6199
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213

	ntups = PQntuples(res);

	i_amopstrategy = PQfnumber(res, "amopstrategy");
	i_amopreqcheck = PQfnumber(res, "amopreqcheck");
	i_amopopr = PQfnumber(res, "amopopr");

	for (i = 0; i < ntups; i++)
	{
		amopstrategy = PQgetvalue(res, i, i_amopstrategy);
		amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
		amopopr = PQgetvalue(res, i, i_amopopr);

		if (needComma)
6214
			appendPQExpBuffer(q, " ,\n    ");
6215

6216
		appendPQExpBuffer(q, "OPERATOR %s %s",
6217 6218
						  amopstrategy, amopopr);
		if (strcmp(amopreqcheck, "t") == 0)
6219
			appendPQExpBuffer(q, " RECHECK");
6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233

		needComma = true;
	}

	PQclear(res);

	/*
	 * Now fetch and print the FUNCTION entries (pg_amproc rows).
	 */
	resetPQExpBuffer(query);

	appendPQExpBuffer(query, "SELECT amprocnum, "
					  "amproc::pg_catalog.regprocedure "
					  "FROM pg_catalog.pg_amproc "
6234
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
6235
					  "ORDER BY amprocnum",
6236
					  opcinfo->dobj.catId.oid);
6237 6238

	res = PQexec(g_conn, query->data);
6239
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251

	ntups = PQntuples(res);

	i_amprocnum = PQfnumber(res, "amprocnum");
	i_amproc = PQfnumber(res, "amproc");

	for (i = 0; i < ntups; i++)
	{
		amprocnum = PQgetvalue(res, i, i_amprocnum);
		amproc = PQgetvalue(res, i, i_amproc);

		if (needComma)
6252
			appendPQExpBuffer(q, " ,\n    ");
6253

6254
		appendPQExpBuffer(q, "FUNCTION %s %s",
6255 6256 6257 6258 6259 6260 6261
						  amprocnum, amproc);

		needComma = true;
	}

	PQclear(res);

6262
	appendPQExpBuffer(q, ";\n");
6263

6264
	ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
6265
				 opcinfo->dobj.name,
6266 6267
				 opcinfo->dobj.namespace->dobj.name, 
				 NULL,
6268
				 opcinfo->rolname,
6269
				 false, "OPERATOR CLASS", q->data, delq->data, NULL,
6270 6271
				 opcinfo->dobj.dependencies, opcinfo->dobj.nDeps,
				 NULL, NULL);
6272

6273 6274 6275
	/* Dump Operator Class Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR CLASS %s",
6276
					  fmtId(opcinfo->dobj.name));
6277 6278 6279
	appendPQExpBuffer(q, " USING %s",
					  fmtId(amname));
	dumpComment(fout, q->data,
6280
				NULL, opcinfo->rolname,
6281
				opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
6282 6283

	free(amname);
6284 6285 6286 6287 6288
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

6289
/*
6290
 * dumpConversion
6291 6292 6293
 *	  write out a single conversion definition
 */
static void
6294
dumpConversion(Archive *fout, ConvInfo *convinfo)
6295
{
6296 6297 6298 6299
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312
	PGresult   *res;
	int			ntups;
	int			i_conname;
	int			i_conforencoding;
	int			i_contoencoding;
	int			i_conproc;
	int			i_condefault;
	const char *conname;
	const char *conforencoding;
	const char *contoencoding;
	const char *conproc;
	bool		condefault;

6313
	/* Dump only conversions in dumpable namespaces */
6314
	if (!convinfo->dobj.namespace->dump || dataOnly)
6315 6316 6317 6318 6319 6320 6321
		return;

	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
	details = createPQExpBuffer();

6322
	/* Make sure we are in proper schema */
6323
	selectSourceSchema(convinfo->dobj.namespace->dobj.name);
6324 6325

	/* Get conversion-specific details */
6326
	appendPQExpBuffer(query, "SELECT conname, "
Bruce Momjian's avatar
Bruce Momjian committed
6327 6328
	 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
	   "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
6329 6330
					  "conproc, condefault "
					  "FROM pg_catalog.pg_conversion c "
6331 6332
					  "WHERE c.oid = '%u'::pg_catalog.oid",
					  convinfo->dobj.catId.oid);
6333 6334

	res = PQexec(g_conn, query->data);
6335
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}

	i_conname = PQfnumber(res, "conname");
	i_conforencoding = PQfnumber(res, "conforencoding");
	i_contoencoding = PQfnumber(res, "contoencoding");
	i_conproc = PQfnumber(res, "conproc");
	i_condefault = PQfnumber(res, "condefault");

	conname = PQgetvalue(res, 0, i_conname);
	conforencoding = PQgetvalue(res, 0, i_conforencoding);
	contoencoding = PQgetvalue(res, 0, i_contoencoding);
	conproc = PQgetvalue(res, 0, i_conproc);
	condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');

	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delq, "DROP CONVERSION %s",
6363
					  fmtId(convinfo->dobj.namespace->dobj.name));
6364
	appendPQExpBuffer(delq, ".%s;\n",
6365
					  fmtId(convinfo->dobj.name));
6366 6367

	appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
Bruce Momjian's avatar
Bruce Momjian committed
6368 6369
					  (condefault) ? "DEFAULT " : "",
					  fmtId(convinfo->dobj.name));
6370 6371 6372 6373 6374
	appendStringLiteral(q, conforencoding, true);
	appendPQExpBuffer(q, " TO ");
	appendStringLiteral(q, contoencoding, true);
	/* regproc is automatically quoted in 7.3 and above */
	appendPQExpBuffer(q, " FROM %s;\n", conproc);
6375

6376
	ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
6377
				 convinfo->dobj.name,
6378 6379
				 convinfo->dobj.namespace->dobj.name, 
				 NULL, 
6380
				 convinfo->rolname,
6381
				 false, "CONVERSION", q->data, delq->data, NULL,
6382 6383
				 convinfo->dobj.dependencies, convinfo->dobj.nDeps,
				 NULL, NULL);
6384 6385 6386

	/* Dump Conversion Comments */
	resetPQExpBuffer(q);
6387
	appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->dobj.name));
6388
	dumpComment(fout, q->data,
6389
				convinfo->dobj.namespace->dobj.name, convinfo->rolname,
6390
				convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
6391

6392
	PQclear(res);
6393

6394 6395 6396 6397
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6398 6399
}

6400 6401 6402 6403 6404 6405
/*
 * format_aggregate_signature: generate aggregate name and argument list
 *
 * The argument type names are qualified if needed.  The aggregate name
 * is never qualified.
 */
6406
static char *
6407
format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
6408 6409 6410 6411
{
	PQExpBufferData buf;

	initPQExpBuffer(&buf);
6412 6413
	if (honor_quotes)
		appendPQExpBuffer(&buf, "%s",
6414
						  fmtId(agginfo->aggfn.dobj.name));
6415
	else
6416
		appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name);
6417

6418
	/* If using regtype or format_type, fmtbasetype is already quoted */
6419 6420
	if (fout->remoteVersion >= 70100)
	{
6421
		if (agginfo->anybasetype)
6422 6423
			appendPQExpBuffer(&buf, "(*)");
		else
6424
			appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
6425
	}
6426 6427
	else
	{
6428
		if (agginfo->anybasetype)
6429 6430 6431
			appendPQExpBuffer(&buf, "(*)");
		else
			appendPQExpBuffer(&buf, "(%s)",
6432
							  fmtId(agginfo->fmtbasetype));
6433 6434 6435
	}

	return buf.data;
6436 6437 6438
}

/*
6439
 * dumpAgg
6440 6441 6442
 *	  write out a single aggregate definition
 */
static void
6443
dumpAgg(Archive *fout, AggInfo *agginfo)
6444
{
6445 6446 6447 6448
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6449
	char	   *aggsig;
6450
	char	   *aggsig_tag;
6451 6452 6453 6454
	PGresult   *res;
	int			ntups;
	int			i_aggtransfn;
	int			i_aggfinalfn;
6455
	int			i_aggsortop;
6456 6457
	int			i_aggtranstype;
	int			i_agginitval;
6458
	int			i_anybasetype;
6459
	int			i_fmtbasetype;
6460 6461 6462
	int			i_convertok;
	const char *aggtransfn;
	const char *aggfinalfn;
6463
	const char *aggsortop;
6464 6465 6466 6467
	const char *aggtranstype;
	const char *agginitval;
	bool		convertok;

6468
	/* Dump only aggs in dumpable namespaces */
6469
	if (!agginfo->aggfn.dobj.namespace->dump || dataOnly)
6470 6471 6472 6473 6474 6475 6476
		return;

	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
	details = createPQExpBuffer();

6477
	/* Make sure we are in proper schema */
6478
	selectSourceSchema(agginfo->aggfn.dobj.namespace->dobj.name);
6479 6480

	/* Get aggregate-specific details */
6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495
	if (g_fout->remoteVersion >= 80100)
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, "
						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
						  "aggsortop::pg_catalog.regoperator, "
						  "agginitval, "
						  "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
					"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
						  "'t'::boolean as convertok "
				  "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
						  "where a.aggfnoid = p.oid "
						  "and p.oid = '%u'::pg_catalog.oid",
						  agginfo->aggfn.dobj.catId.oid);
	}
	else if (g_fout->remoteVersion >= 70300)
6496 6497
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, "
6498
						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
6499
						  "0 as aggsortop, "
6500
						  "agginitval, "
6501
						  "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
Bruce Momjian's avatar
Bruce Momjian committed
6502
					"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
6503
						  "'t'::boolean as convertok "
Bruce Momjian's avatar
Bruce Momjian committed
6504
				  "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
6505
						  "where a.aggfnoid = p.oid "
6506 6507
						  "and p.oid = '%u'::pg_catalog.oid",
						  agginfo->aggfn.dobj.catId.oid);
6508 6509 6510 6511
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
Bruce Momjian's avatar
Bruce Momjian committed
6512
					  "format_type(aggtranstype, NULL) as aggtranstype, "
6513
						  "0 as aggsortop, "
6514
						  "agginitval, "
6515
						  "aggbasetype = 0 as anybasetype, "
6516
						  "CASE WHEN aggbasetype = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
6517
			   "ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
6518
						  "'t'::boolean as convertok "
6519
						  "from pg_aggregate "
6520 6521
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6522 6523 6524 6525 6526 6527
	}
	else
	{
		appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
						  "aggfinalfn, "
						  "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
6528
						  "0 as aggsortop, "
6529
						  "agginitval1 as agginitval, "
6530
						  "aggbasetype = 0 as anybasetype, "
6531
						  "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
6532 6533
						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
						  "from pg_aggregate "
6534 6535
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6536
	}
6537

6538
	res = PQexec(g_conn, query->data);
6539
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6540

6541 6542 6543 6544 6545 6546 6547 6548
	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
		write_msg(NULL, "Got %d rows instead of one from: %s",
				  ntups, query->data);
		exit_nicely();
	}
Bruce Momjian's avatar
Bruce Momjian committed
6549

6550 6551
	i_aggtransfn = PQfnumber(res, "aggtransfn");
	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
6552
	i_aggsortop = PQfnumber(res, "aggsortop");
6553 6554
	i_aggtranstype = PQfnumber(res, "aggtranstype");
	i_agginitval = PQfnumber(res, "agginitval");
6555
	i_anybasetype = PQfnumber(res, "anybasetype");
6556
	i_fmtbasetype = PQfnumber(res, "fmtbasetype");
6557
	i_convertok = PQfnumber(res, "convertok");
6558

6559 6560
	aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
6561
	aggsortop = PQgetvalue(res, 0, i_aggsortop);
6562 6563
	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
	agginitval = PQgetvalue(res, 0, i_agginitval);
6564
	/* we save anybasetype for format_aggregate_signature */
6565
	agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
6566
	/* we save fmtbasetype for format_aggregate_signature */
6567
	agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
6568
	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
6569

6570 6571
	aggsig = format_aggregate_signature(agginfo, fout, true);
	aggsig_tag = format_aggregate_signature(agginfo, fout, false);
6572

6573 6574 6575
	if (!convertok)
	{
		write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
6576
				  aggsig);
6577 6578
		return;
	}
6579

6580 6581 6582
	if (g_fout->remoteVersion >= 70300)
	{
		/* If using 7.3's regproc or regtype, data is already quoted */
6583
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6584 6585
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6586 6587 6588 6589 6590 6591
						  aggtransfn,
						  aggtranstype);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* format_type quotes, regproc does not */
6592
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6593 6594
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6595
						  fmtId(aggtransfn),
6596 6597 6598 6599 6600
						  aggtranstype);
	}
	else
	{
		/* need quotes all around */
6601
		appendPQExpBuffer(details, "    BASETYPE = %s,\n",
6602
						  agginfo->anybasetype ? "'any'" :
6603 6604 6605 6606 6607
						  fmtId(agginfo->fmtbasetype));
		appendPQExpBuffer(details, "    SFUNC = %s,\n",
						  fmtId(aggtransfn));
		appendPQExpBuffer(details, "    STYPE = %s",
						  fmtId(aggtranstype));
6608
	}
6609

6610 6611
	if (!PQgetisnull(res, 0, i_agginitval))
	{
6612 6613
		appendPQExpBuffer(details, ",\n    INITCOND = ");
		appendStringLiteral(details, agginitval, true);
6614
	}
6615

6616 6617
	if (strcmp(aggfinalfn, "-") != 0)
	{
6618
		appendPQExpBuffer(details, ",\n    FINALFUNC = %s",
6619 6620
						  aggfinalfn);
	}
6621

6622 6623 6624 6625 6626 6627 6628
	aggsortop = convertOperatorReference(aggsortop);
	if (aggsortop)
	{
		appendPQExpBuffer(details, ",\n    SORTOP = %s",
						  aggsortop);
	}

Bruce Momjian's avatar
Bruce Momjian committed
6629 6630 6631 6632
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
6633
	appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
6634
					  fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
6635
					  aggsig);
6636

6637
	appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
6638
					  fmtId(agginfo->aggfn.dobj.name),
6639
					  details->data);
6640

6641 6642
	ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
				 aggsig_tag,
6643 6644
				 agginfo->aggfn.dobj.namespace->dobj.name,
				 NULL,
6645
				 agginfo->aggfn.rolname,
6646
				 false, "AGGREGATE", q->data, delq->data, NULL,
6647
				 agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps,
6648
				 NULL, NULL);
6649

Bruce Momjian's avatar
Bruce Momjian committed
6650
	/* Dump Aggregate Comments */
6651
	resetPQExpBuffer(q);
6652
	appendPQExpBuffer(q, "AGGREGATE %s", aggsig);
6653
	dumpComment(fout, q->data,
6654
		agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
6655 6656 6657 6658
				agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);

	/*
	 * Since there is no GRANT ON AGGREGATE syntax, we have to make the
Bruce Momjian's avatar
Bruce Momjian committed
6659 6660
	 * ACL command look like a function's GRANT; in particular this
	 * affects the syntax for aggregates on ANY.
6661 6662 6663 6664
	 */
	free(aggsig);
	free(aggsig_tag);

6665 6666
	aggsig = format_function_signature(&agginfo->aggfn, true);
	aggsig_tag = format_function_signature(&agginfo->aggfn, false);
6667 6668 6669 6670

	dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
			"FUNCTION",
			aggsig, aggsig_tag,
6671
			agginfo->aggfn.dobj.namespace->dobj.name,
6672
			agginfo->aggfn.rolname, agginfo->aggfn.proacl);
6673 6674 6675

	free(aggsig);
	free(aggsig_tag);
Bruce Momjian's avatar
Bruce Momjian committed
6676

6677
	PQclear(res);
6678

6679
	destroyPQExpBuffer(query);
6680 6681 6682
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6683 6684
}

6685

6686
/*----------
6687 6688
 * Write out grant/revoke information
 *
6689 6690
 * 'objCatId' is the catalog ID of the underlying object.
 * 'objDumpId' is the dump ID of the underlying object.
6691
 * 'type' must be TABLE, FUNCTION, LANGUAGE, or SCHEMA.
Bruce Momjian's avatar
Bruce Momjian committed
6692
 * 'name' is the formatted name of the object.	Must be quoted etc. already.
6693
 * 'tag' is the tag for the archive entry (typ. unquoted name of object).
6694
 * 'nspname' is the namespace the object is in (NULL if none).
6695
 * 'owner' is the owner, NULL if there is no owner (for languages).
6696 6697
 * 'acls' is the string read out of the fooacl system catalog field;
 * it will be parsed here.
6698
 *----------
Bruce Momjian's avatar
Bruce Momjian committed
6699
 */
6700
static void
6701 6702
dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
		const char *type, const char *name,
6703
		const char *tag, const char *nspname, const char *owner,
6704
		const char *acls)
Bruce Momjian's avatar
Bruce Momjian committed
6705
{
6706
	PQExpBuffer sql;
6707

6708 6709 6710
	/* Do nothing if ACL dump is not enabled */
	if (dataOnly || aclsSkip)
		return;
6711

6712
	sql = createPQExpBuffer();
6713

6714
	if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql))
6715
	{
6716
		write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
6717 6718
				  acls, name, type);
		exit_nicely();
Bruce Momjian's avatar
Bruce Momjian committed
6719
	}
6720

6721
	if (sql->len > 0)
6722 6723
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
					 tag, nspname,
6724
					 NULL, 
6725
					 owner ? owner : "",
6726
					 false, "ACL", sql->data, "", NULL,
6727 6728
					 &(objDumpId), 1,
					 NULL, NULL);
6729 6730

	destroyPQExpBuffer(sql);
Bruce Momjian's avatar
Bruce Momjian committed
6731 6732
}

6733
/*
6734 6735
 * dumpTable
 *	  write out to fout the declarations (not data) of a user-defined table
6736
 */
6737 6738
static void
dumpTable(Archive *fout, TableInfo *tbinfo)
6739
{
6740
	char	   *namecopy;
6741

6742
	if (tbinfo->dump)
6743
	{
6744 6745
		if (tbinfo->relkind == RELKIND_SEQUENCE)
			dumpSequence(fout, tbinfo);
6746
		else if (!dataOnly)
6747 6748 6749
			dumpTableSchema(fout, tbinfo);

		/* Handle the ACL here */
6750
		namecopy = strdup(fmtId(tbinfo->dobj.name));
6751
		dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
6752
				namecopy, tbinfo->dobj.name,
6753
				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
6754 6755
				tbinfo->relacl);
		free(namecopy);
6756
	}
6757
}
6758

6759
/*
6760
 * dumpTableSchema
6761
 *	  write the declaration (not data) of one user-defined table or view
6762
 */
6763
static void
6764
dumpTableSchema(Archive *fout, TableInfo *tbinfo)
6765
{
6766
	PQExpBuffer query = createPQExpBuffer();
6767
	PQExpBuffer q = createPQExpBuffer();
6768
	PQExpBuffer delq = createPQExpBuffer();
6769
	PGresult   *res;
6770
	int			numParents;
6771
	TableInfo **parents;
Bruce Momjian's avatar
Bruce Momjian committed
6772
	int			actual_atts;	/* number of attrs in this CREATE statment */
6773
	char	   *reltypename;
6774
	char	   *storage;
6775 6776
	int			j,
				k;
6777

6778
	/* Make sure we are in proper schema */
6779
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
6780

6781 6782
	/* Is it a table or a view? */
	if (tbinfo->relkind == RELKIND_VIEW)
6783
	{
6784
		char	   *viewdef;
6785

6786
		reltypename = "VIEW";
6787

6788 6789 6790
		/* Fetch the view definition */
		if (g_fout->remoteVersion >= 70300)
		{
6791 6792 6793 6794
			/* Beginning in 7.3, viewname is not unique; rely on OID */
			appendPQExpBuffer(query,
							  "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) as viewdef",
							  tbinfo->dobj.catId.oid);
6795 6796 6797
		}
		else
		{
6798
			appendPQExpBuffer(query, "SELECT definition as viewdef "
6799
							  " from pg_views where viewname = ");
6800
			appendStringLiteral(query, tbinfo->dobj.name, true);
6801 6802
			appendPQExpBuffer(query, ";");
		}
6803

6804
		res = PQexec(g_conn, query->data);
6805
		check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6806

6807 6808 6809 6810
		if (PQntuples(res) != 1)
		{
			if (PQntuples(res) < 1)
				write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
6811
						  tbinfo->dobj.name);
6812
			else
6813
				write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
6814
						  tbinfo->dobj.name);
6815 6816
			exit_nicely();
		}
Bruce Momjian's avatar
Bruce Momjian committed
6817

6818
		viewdef = PQgetvalue(res, 0, 0);
6819

6820 6821 6822
		if (strlen(viewdef) == 0)
		{
			write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
6823
					  tbinfo->dobj.name);
6824 6825
			exit_nicely();
		}
6826

Bruce Momjian's avatar
Bruce Momjian committed
6827 6828 6829 6830
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6831
		appendPQExpBuffer(delq, "DROP VIEW %s.",
6832
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6833
		appendPQExpBuffer(delq, "%s;\n",
6834
						  fmtId(tbinfo->dobj.name));
6835

6836
		appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
6837
						  fmtId(tbinfo->dobj.name), viewdef);
6838

6839 6840 6841 6842 6843 6844
		PQclear(res);
	}
	else
	{
		reltypename = "TABLE";
		numParents = tbinfo->numParents;
6845
		parents = tbinfo->parents;
6846

Bruce Momjian's avatar
Bruce Momjian committed
6847 6848 6849 6850
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6851
		appendPQExpBuffer(delq, "DROP TABLE %s.",
6852
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6853
		appendPQExpBuffer(delq, "%s;\n",
6854
						  fmtId(tbinfo->dobj.name));
6855

6856
		appendPQExpBuffer(q, "CREATE TABLE %s (",
6857
						  fmtId(tbinfo->dobj.name));
6858 6859 6860
		actual_atts = 0;
		for (j = 0; j < tbinfo->numatts; j++)
		{
6861 6862
			/* Is this one of the table's own attrs, and not dropped ? */
			if (!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j])
6863 6864 6865
			{
				/* Format properly if not first attr */
				if (actual_atts > 0)
6866 6867
					appendPQExpBuffer(q, ",");
				appendPQExpBuffer(q, "\n    ");
6868

6869
				/* Attribute name */
6870
				appendPQExpBuffer(q, "%s ",
6871
								  fmtId(tbinfo->attnames[j]));
6872

6873
				/* Attribute type */
6874
				if (g_fout->remoteVersion >= 70100)
6875
				{
Bruce Momjian's avatar
Bruce Momjian committed
6876
					char	   *typname = tbinfo->atttypnames[j];
6877 6878 6879 6880 6881 6882 6883 6884 6885 6886

					if (tbinfo->attisserial[j])
					{
						if (strcmp(typname, "integer") == 0)
							typname = "serial";
						else if (strcmp(typname, "bigint") == 0)
							typname = "bigserial";
					}
					appendPQExpBuffer(q, "%s", typname);
				}
6887
				else
6888 6889
				{
					/* If no format_type, fake it */
6890 6891 6892
					appendPQExpBuffer(q, "%s",
									  myFormatType(tbinfo->atttypnames[j],
												   tbinfo->atttypmod[j]));
6893
				}
6894

6895
				/*
Bruce Momjian's avatar
Bruce Momjian committed
6896 6897
				 * Default value --- suppress if inherited, serial, or to
				 * be printed separately.
6898 6899
				 */
				if (tbinfo->attrdefs[j] != NULL &&
6900
					!tbinfo->inhAttrDef[j] &&
6901 6902
					!tbinfo->attisserial[j] &&
					!tbinfo->attrdefs[j]->separate)
6903
					appendPQExpBuffer(q, " DEFAULT %s",
6904
									  tbinfo->attrdefs[j]->adef_expr);
Bruce Momjian's avatar
Bruce Momjian committed
6905

6906 6907 6908 6909 6910 6911 6912 6913 6914
				/*
				 * Not Null constraint --- suppress if inherited
				 *
				 * Note: we could suppress this for serial columns since
				 * SERIAL implies NOT NULL.  We choose not to for forward
				 * compatibility, since there has been some talk of making
				 * SERIAL not imply NOT NULL, in which case the explicit
				 * specification would be needed.
				 */
6915 6916
				if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
					appendPQExpBuffer(q, " NOT NULL");
6917

6918
				actual_atts++;
6919
			}
6920
		}
6921

6922
		/*
6923
		 * Add non-inherited CHECK constraints, if any.
6924
		 */
6925
		for (j = 0; j < tbinfo->ncheck; j++)
6926
		{
6927
			ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
6928

6929 6930
			if (constr->coninherited || constr->separate)
				continue;
6931

6932 6933
			if (actual_atts > 0)
				appendPQExpBuffer(q, ",\n    ");
6934

6935
			appendPQExpBuffer(q, "CONSTRAINT %s ",
6936
							  fmtId(constr->dobj.name));
6937
			appendPQExpBuffer(q, "%s", constr->condef);
6938

6939
			actual_atts++;
6940
		}
6941

6942
		appendPQExpBuffer(q, "\n)");
6943

6944 6945 6946 6947 6948
		if (numParents > 0)
		{
			appendPQExpBuffer(q, "\nINHERITS (");
			for (k = 0; k < numParents; k++)
			{
6949
				TableInfo  *parentRel = parents[k];
6950

6951 6952
				if (k > 0)
					appendPQExpBuffer(q, ", ");
6953
				if (parentRel->dobj.namespace != tbinfo->dobj.namespace)
6954
					appendPQExpBuffer(q, "%s.",
Bruce Momjian's avatar
Bruce Momjian committed
6955
							fmtId(parentRel->dobj.namespace->dobj.name));
6956
				appendPQExpBuffer(q, "%s",
6957
								  fmtId(parentRel->dobj.name));
6958 6959 6960
			}
			appendPQExpBuffer(q, ")");
		}
6961

6962
		appendPQExpBuffer(q, ";\n");
6963

6964
		/* Loop dumping statistics and storage statements */
Bruce Momjian's avatar
Bruce Momjian committed
6965
		for (j = 0; j < tbinfo->numatts; j++)
6966
		{
6967
			/*
Bruce Momjian's avatar
Bruce Momjian committed
6968 6969 6970
			 * Dump per-column statistics information. We only issue an
			 * ALTER TABLE statement if the attstattarget entry for this
			 * column is non-negative (i.e. it's not the default value)
6971
			 */
6972 6973
			if (tbinfo->attstattarget[j] >= 0 &&
				!tbinfo->attisdropped[j])
6974
			{
6975
				appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
6976
								  fmtId(tbinfo->dobj.name));
6977
				appendPQExpBuffer(q, "ALTER COLUMN %s ",
6978
								  fmtId(tbinfo->attnames[j]));
6979 6980 6981
				appendPQExpBuffer(q, "SET STATISTICS %d;\n",
								  tbinfo->attstattarget[j]);
			}
6982 6983

			/*
Bruce Momjian's avatar
Bruce Momjian committed
6984 6985 6986
			 * Dump per-column storage information.  The statement is only
			 * dumped if the storage has been changed from the type's
			 * default.
6987
			 */
Bruce Momjian's avatar
Bruce Momjian committed
6988
			if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
6989
			{
Bruce Momjian's avatar
Bruce Momjian committed
6990 6991
				switch (tbinfo->attstorage[j])
				{
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006
					case 'p':
						storage = "PLAIN";
						break;
					case 'e':
						storage = "EXTERNAL";
						break;
					case 'm':
						storage = "MAIN";
						break;
					case 'x':
						storage = "EXTENDED";
						break;
					default:
						storage = NULL;
				}
Bruce Momjian's avatar
Bruce Momjian committed
7007 7008 7009 7010 7011 7012 7013

				/*
				 * Only dump the statement if it's a storage type we
				 * recognize
				 */
				if (storage != NULL)
				{
7014
					appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
7015
									  fmtId(tbinfo->dobj.name));
7016 7017 7018 7019 7020 7021
					appendPQExpBuffer(q, "ALTER COLUMN %s ",
									  fmtId(tbinfo->attnames[j]));
					appendPQExpBuffer(q, "SET STORAGE %s;\n",
									  storage);
				}
			}
7022
		}
7023 7024
	}

7025
	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
7026
				 tbinfo->dobj.name,
7027 7028
				 tbinfo->dobj.namespace->dobj.name,
				 (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
7029
				 tbinfo->rolname,
7030
				 (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
7031 7032 7033
				 reltypename, q->data, delq->data, NULL,
				 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
				 NULL, NULL);
7034

7035
	/* Dump Table Comments */
7036
	dumpTableComment(fout, tbinfo, reltypename);
7037

7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048
	/* Dump comments on inlined table constraints */
	for (j = 0; j < tbinfo->ncheck; j++)
	{
		ConstraintInfo *constr = &(tbinfo->checkexprs[j]);

		if (constr->coninherited || constr->separate)
			continue;

		dumpTableConstraintComment(fout, constr);
	}

7049 7050 7051
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
7052 7053
}

7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069
/*
 * dumpAttrDef --- dump an attribute's default-value declaration
 */
static void
dumpAttrDef(Archive *fout, AttrDefInfo *adinfo)
{
	TableInfo  *tbinfo = adinfo->adtable;
	int			adnum = adinfo->adnum;
	PQExpBuffer q;
	PQExpBuffer delq;

	/* Only print it if "separate" mode is selected */
	if (!tbinfo->dump || !adinfo->separate || dataOnly)
		return;

	/* Don't print inherited or serial defaults, either */
Bruce Momjian's avatar
Bruce Momjian committed
7070
	if (tbinfo->inhAttrDef[adnum - 1] || tbinfo->attisserial[adnum - 1])
7071 7072 7073 7074 7075 7076
		return;

	q = createPQExpBuffer();
	delq = createPQExpBuffer();

	appendPQExpBuffer(q, "ALTER TABLE %s ",
7077
					  fmtId(tbinfo->dobj.name));
7078 7079 7080 7081 7082
	appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
					  fmtId(tbinfo->attnames[adnum - 1]),
					  adinfo->adef_expr);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
7083 7084
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
7085 7086
	 */
	appendPQExpBuffer(delq, "ALTER TABLE %s.",
7087
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7088
	appendPQExpBuffer(delq, "%s ",
7089
					  fmtId(tbinfo->dobj.name));
7090 7091 7092 7093 7094
	appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
					  fmtId(tbinfo->attnames[adnum - 1]));

	ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
				 tbinfo->attnames[adnum - 1],
7095 7096
				 tbinfo->dobj.namespace->dobj.name, 
				 NULL,
7097
				 tbinfo->rolname,
7098
				 false, "DEFAULT", q->data, delq->data, NULL,
7099 7100 7101 7102 7103 7104 7105
				 adinfo->dobj.dependencies, adinfo->dobj.nDeps,
				 NULL, NULL);

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116
/*
 * getAttrName: extract the correct name for an attribute
 *
 * The array tblInfo->attnames[] only provides names of user attributes;
 * if a system attribute number is supplied, we have to fake it.
 * We also do a little bit of bounds checking for safety's sake.
 */
static const char *
getAttrName(int attrnum, TableInfo *tblInfo)
{
	if (attrnum > 0 && attrnum <= tblInfo->numatts)
7117
		return tblInfo->attnames[attrnum - 1];
7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134
	switch (attrnum)
	{
		case SelfItemPointerAttributeNumber:
			return "ctid";
		case ObjectIdAttributeNumber:
			return "oid";
		case MinTransactionIdAttributeNumber:
			return "xmin";
		case MinCommandIdAttributeNumber:
			return "cmin";
		case MaxTransactionIdAttributeNumber:
			return "xmax";
		case MaxCommandIdAttributeNumber:
			return "cmax";
		case TableOidAttributeNumber:
			return "tableoid";
	}
7135
	write_msg(NULL, "invalid column number %d for table \"%s\"\n",
7136
			  attrnum, tblInfo->dobj.name);
7137
	exit_nicely();
7138 7139 7140
	return NULL;				/* keep compiler quiet */
}

7141
/*
7142 7143
 * dumpIndex
 *	  write out to fout a user-defined index
7144
 */
7145 7146
static void
dumpIndex(Archive *fout, IndxInfo *indxinfo)
7147
{
7148 7149 7150
	TableInfo  *tbinfo = indxinfo->indextable;
	PQExpBuffer q;
	PQExpBuffer delq;
7151

7152 7153
	if (dataOnly)
		return;
7154

7155 7156
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
7157

7158 7159
	/*
	 * If there's an associated constraint, don't dump the index per se,
7160 7161
	 * but do dump any comment for it.  (This is safe because dependency
	 * ordering will have ensured the constraint is emitted first.)
7162 7163 7164 7165 7166
	 */
	if (indxinfo->indexconstraint == 0)
	{
		/* Plain secondary index */
		appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
7167

7168 7169 7170 7171
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
7172
							  fmtId(tbinfo->dobj.name));
7173
			appendPQExpBuffer(q, " ON %s;\n",
7174
							  fmtId(indxinfo->dobj.name));
7175
		}
7176

7177
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7178 7179
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
7180
		 */
7181
		appendPQExpBuffer(delq, "DROP INDEX %s.",
7182
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7183
		appendPQExpBuffer(delq, "%s;\n",
7184
						  fmtId(indxinfo->dobj.name));
7185

7186
		ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
7187 7188
					 indxinfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7189
					 indxinfo->tablespace,
7190
					 tbinfo->rolname, false,
7191 7192 7193 7194
					 "INDEX", q->data, delq->data, NULL,
					 indxinfo->dobj.dependencies, indxinfo->dobj.nDeps,
					 NULL, NULL);
	}
7195

7196 7197 7198
	/* Dump Index Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "INDEX %s",
7199
					  fmtId(indxinfo->dobj.name));
7200
	dumpComment(fout, q->data,
7201
				tbinfo->dobj.namespace->dobj.name,
7202
				tbinfo->rolname,
7203
				indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId);
7204

7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

/*
 * dumpConstraint
 *	  write out to fout a user-defined constraint
 */
static void
dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
{
	TableInfo  *tbinfo = coninfo->contable;
	PQExpBuffer q;
	PQExpBuffer delq;

	if (dataOnly)
		return;
	if (tbinfo && !tbinfo->dump)
		return;

	q = createPQExpBuffer();
	delq = createPQExpBuffer();

	if (coninfo->contype == 'p' || coninfo->contype == 'u')
	{
		/* Index-related constraint */
		IndxInfo   *indxinfo;
		int			k;

		indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);

		if (indxinfo == NULL)
		{
Peter Eisentraut's avatar
Peter Eisentraut committed
7238
			write_msg(NULL, "missing index for constraint \"%s\"\n",
7239
					  coninfo->dobj.name);
7240 7241
			exit_nicely();
		}
7242

7243
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
7244
						  fmtId(tbinfo->dobj.name));
7245
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s (",
7246
						  fmtId(coninfo->dobj.name),
Bruce Momjian's avatar
Bruce Momjian committed
7247
					 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
7248 7249

		for (k = 0; k < indxinfo->indnkeys; k++)
7250
		{
7251 7252
			int			indkey = (int) indxinfo->indkeys[k];
			const char *attname;
7253

7254 7255 7256
			if (indkey == InvalidAttrNumber)
				break;
			attname = getAttrName(indkey, tbinfo);
7257

7258 7259 7260 7261
			appendPQExpBuffer(q, "%s%s",
							  (k == 0) ? "" : ", ",
							  fmtId(attname));
		}
7262

7263
		appendPQExpBuffer(q, ");\n");
7264

7265 7266 7267 7268
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
7269
							  fmtId(tbinfo->dobj.name));
7270
			appendPQExpBuffer(q, " ON %s;\n",
7271
							  fmtId(indxinfo->dobj.name));
7272
		}
7273

7274
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7275 7276
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
7277 7278
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
7279
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7280
		appendPQExpBuffer(delq, "%s ",
7281
						  fmtId(tbinfo->dobj.name));
7282
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7283
						  fmtId(coninfo->dobj.name));
7284

7285
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7286 7287
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7288
					 indxinfo->tablespace,
7289
					 tbinfo->rolname, false,
7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300
					 "CONSTRAINT", q->data, delq->data, NULL,
					 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
					 NULL, NULL);
	}
	else if (coninfo->contype == 'f')
	{
		/*
		 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that
		 * the current table data is not processed
		 */
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
7301
						  fmtId(tbinfo->dobj.name));
7302
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7303
						  fmtId(coninfo->dobj.name),
7304
						  coninfo->condef);
7305

7306 7307 7308 7309 7310
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
7311
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7312
		appendPQExpBuffer(delq, "%s ",
7313
						  fmtId(tbinfo->dobj.name));
7314
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7315
						  fmtId(coninfo->dobj.name));
7316

7317
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7318 7319
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7320
					 NULL,
7321
					 tbinfo->rolname, false,
7322 7323 7324 7325 7326 7327 7328
					 "FK CONSTRAINT", q->data, delq->data, NULL,
					 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
					 NULL, NULL);
	}
	else if (coninfo->contype == 'c' && tbinfo)
	{
		/* CHECK constraint on a table */
7329

7330 7331 7332 7333 7334
		/* Ignore if not to be dumped separately */
		if (coninfo->separate)
		{
			/* not ONLY since we want it to propagate to children */
			appendPQExpBuffer(q, "ALTER TABLE %s\n",
7335
							  fmtId(tbinfo->dobj.name));
7336
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7337
							  fmtId(coninfo->dobj.name),
7338
							  coninfo->condef);
7339

7340 7341 7342 7343 7344
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER TABLE %s.",
7345
							  fmtId(tbinfo->dobj.namespace->dobj.name));
7346
			appendPQExpBuffer(delq, "%s ",
7347
							  fmtId(tbinfo->dobj.name));
7348
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7349
							  fmtId(coninfo->dobj.name));
7350

7351
			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7352 7353
						 coninfo->dobj.name,
						 tbinfo->dobj.namespace->dobj.name,
7354
						 NULL,
7355
						 tbinfo->rolname, false,
7356 7357 7358 7359 7360 7361 7362 7363 7364
						 "CHECK CONSTRAINT", q->data, delq->data, NULL,
						 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
						 NULL, NULL);
		}
	}
	else if (coninfo->contype == 'c' && tbinfo == NULL)
	{
		/* CHECK constraint on a domain */
		TypeInfo   *tinfo = coninfo->condomain;
Bruce Momjian's avatar
Adds  
Bruce Momjian committed
7365

7366
		/* Ignore if not to be dumped separately, or if not dumping domain */
7367
		if (coninfo->separate && tinfo->dobj.namespace->dump)
7368 7369
		{
			appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
7370
							  fmtId(tinfo->dobj.name));
7371
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7372
							  fmtId(coninfo->dobj.name),
7373
							  coninfo->condef);
7374

7375 7376 7377 7378 7379
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER DOMAIN %s.",
7380
							  fmtId(tinfo->dobj.namespace->dobj.name));
7381
			appendPQExpBuffer(delq, "%s ",
7382
							  fmtId(tinfo->dobj.name));
7383
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7384
							  fmtId(coninfo->dobj.name));
7385 7386

			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7387 7388
						 coninfo->dobj.name,
						 tinfo->dobj.namespace->dobj.name,
7389
						 NULL,
7390
						 tinfo->rolname, false,
7391 7392 7393
						 "CHECK CONSTRAINT", q->data, delq->data, NULL,
						 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
						 NULL, NULL);
7394
		}
7395 7396 7397
	}
	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
7398
		write_msg(NULL, "unrecognized constraint type: %c\n", coninfo->contype);
7399 7400
		exit_nicely();
	}
7401

7402
	/* Dump Constraint Comments --- only works for table constraints */
7403 7404
	if (tbinfo && coninfo->separate)
		dumpTableConstraintComment(fout, coninfo);
7405 7406 7407

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
7408 7409
}

7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428
/*
 * dumpTableConstraintComment --- dump a constraint's comment if any
 *
 * This is split out because we need the function in two different places
 * depending on whether the constraint is dumped as part of CREATE TABLE
 * or as a separate ALTER command.
 */
static void
dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo)
{
	TableInfo  *tbinfo = coninfo->contable;
	PQExpBuffer q = createPQExpBuffer();

	appendPQExpBuffer(q, "CONSTRAINT %s ",
					  fmtId(coninfo->dobj.name));
	appendPQExpBuffer(q, "ON %s",
					  fmtId(tbinfo->dobj.name));
	dumpComment(fout, q->data,
				tbinfo->dobj.namespace->dobj.name,
7429
				tbinfo->rolname,
7430 7431 7432 7433 7434 7435
				coninfo->dobj.catId, 0,
				coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);

	destroyPQExpBuffer(q);
}

7436 7437
/*
 * findLastBuiltInOid -
7438
 * find the last built in oid
7439 7440 7441
 *
 * For 7.1 and 7.2, we do this by retrieving datlastsysoid from the
 * pg_database entry for the current database
7442
 */
7443
static Oid
7444
findLastBuiltinOid_V71(const char *dbname)
7445
{
Bruce Momjian's avatar
Bruce Momjian committed
7446
	PGresult   *res;
7447
	int			ntups;
7448
	Oid			last_oid;
7449 7450 7451
	PQExpBuffer query = createPQExpBuffer();

	resetPQExpBuffer(query);
7452
	appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
7453
	appendStringLiteral(query, dbname, true);
7454

7455
	res = PQexec(g_conn, query->data);
7456 7457
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

7458
	ntups = PQntuples(res);
7459
	if (ntups < 1)
7460
	{
7461
		write_msg(NULL, "missing pg_database entry for this database\n");
7462
		exit_nicely();
7463 7464 7465
	}
	if (ntups > 1)
	{
7466
		write_msg(NULL, "found more than one pg_database entry for this database\n");
7467
		exit_nicely();
7468
	}
7469
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
7470
	PQclear(res);
7471
	destroyPQExpBuffer(query);
7472
	return last_oid;
7473 7474
}

7475 7476 7477
/*
 * findLastBuiltInOid -
 * find the last built in oid
7478 7479
 *
 * For 7.0, we do this by assuming that the last thing that initdb does is to
Bruce Momjian's avatar
Bruce Momjian committed
7480
 * create the pg_indexes view.	This sucks in general, but seeing that 7.0.x
7481 7482
 * initdb won't be changing anymore, it'll do.
 */
7483
static Oid
7484 7485 7486 7487 7488 7489 7490
findLastBuiltinOid_V70(void)
{
	PGresult   *res;
	int			ntups;
	int			last_oid;

	res = PQexec(g_conn,
7491
				 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'");
7492
	check_sql_result(res, g_conn,
Bruce Momjian's avatar
Bruce Momjian committed
7493
				 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'",
7494
					 PGRES_TUPLES_OK);
7495 7496 7497
	ntups = PQntuples(res);
	if (ntups < 1)
	{
7498
		write_msg(NULL, "could not find entry for pg_indexes in pg_class\n");
7499
		exit_nicely();
7500 7501 7502
	}
	if (ntups > 1)
	{
7503
		write_msg(NULL, "found more than one entry for pg_indexes in pg_class\n");
7504
		exit_nicely();
7505 7506 7507 7508 7509
	}
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
	PQclear(res);
	return last_oid;
}
7510

7511
static void
7512
dumpSequence(Archive *fout, TableInfo *tbinfo)
7513
{
7514
	PGresult   *res;
7515 7516
	char	   *last,
			   *incby,
7517 7518
			   *maxv = NULL,
			   *minv = NULL,
7519
			   *cache;
7520 7521
	char		bufm[100],
				bufx[100];
7522
	bool		cycled,
Bruce Momjian's avatar
Bruce Momjian committed
7523
				called;
7524
	PQExpBuffer query = createPQExpBuffer();
7525
	PQExpBuffer delqry = createPQExpBuffer();
7526

7527
	/* Make sure we are in proper schema */
7528
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7529

7530 7531 7532
	snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
	snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);

Bruce Momjian's avatar
Bruce Momjian committed
7533
	appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7534 7535 7536 7537 7538 7539 7540 7541 7542 7543
					  "SELECT sequence_name, last_value, increment_by, "
			   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
			   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
					  "     ELSE max_value "
					  "END AS max_value, "
				"CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
			   "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
					  "     ELSE min_value "
					  "END AS min_value, "
					  "cache_value, is_cycled, is_called from %s",
7544
					  bufx, bufm,
7545
					  fmtId(tbinfo->dobj.name));
7546

Bruce Momjian's avatar
Bruce Momjian committed
7547
	res = PQexec(g_conn, query->data);
7548
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
7549 7550 7551

	if (PQntuples(res) != 1)
	{
7552
		write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
7553
				  tbinfo->dobj.name, PQntuples(res));
7554
		exit_nicely();
7555 7556
	}

7557 7558
	/* Disable this check: it fails if sequence has been renamed */
#ifdef NOT_USED
7559
	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
7560
	{
7561
		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
7562
				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
7563
		exit_nicely();
7564
	}
7565
#endif
7566

7567 7568
	last = PQgetvalue(res, 0, 1);
	incby = PQgetvalue(res, 0, 2);
7569 7570 7571 7572
	if (!PQgetisnull(res, 0, 3))
		maxv = PQgetvalue(res, 0, 3);
	if (!PQgetisnull(res, 0, 4))
		minv = PQgetvalue(res, 0, 4);
7573 7574 7575
	cache = PQgetvalue(res, 0, 5);
	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
	called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
7576

7577
	/*
7578 7579 7580 7581 7582
	 * The logic we use for restoring sequences is as follows:
	 *
	 * Add a basic CREATE SEQUENCE statement (use last_val for start if
	 * called is false, else use min_val for start_val).  Skip this if the
	 * sequence came from a SERIAL column.
7583
	 *
7584
	 * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
7585
	 * data.  We do this for serial sequences too.
7586
	 */
7587

7588
	if (!dataOnly && !OidIsValid(tbinfo->owning_tab))
7589 7590
	{
		resetPQExpBuffer(delqry);
7591

Bruce Momjian's avatar
Bruce Momjian committed
7592 7593 7594 7595
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
7596
		appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
7597
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7598
		appendPQExpBuffer(delqry, "%s;\n",
7599
						  fmtId(tbinfo->dobj.name));
7600

7601 7602
		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7603
						  "CREATE SEQUENCE %s\n",
7604
						  fmtId(tbinfo->dobj.name));
7605 7606 7607 7608 7609

		if (!called)
			appendPQExpBuffer(query, "    START WITH %s\n", last);

		appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);
7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621

		if (maxv)
			appendPQExpBuffer(query, "    MAXVALUE %s\n", maxv);
		else
			appendPQExpBuffer(query, "    NO MAXVALUE\n");

		if (minv)
			appendPQExpBuffer(query, "    MINVALUE %s\n", minv);
		else
			appendPQExpBuffer(query, "    NO MINVALUE\n");

		appendPQExpBuffer(query,
7622
						  "    CACHE %s%s",
7623
						  cache, (cycled ? "\n    CYCLE" : ""));
7624

7625 7626
		appendPQExpBuffer(query, ";\n");

7627
		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
7628
					 tbinfo->dobj.name,
7629 7630
					 tbinfo->dobj.namespace->dobj.name, 
					 NULL,
7631
					 tbinfo->rolname,
7632
					 false, "SEQUENCE", query->data, delqry->data, NULL,
7633 7634
					 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
					 NULL, NULL);
7635
	}
7636

7637 7638
	if (!schemaOnly)
	{
Bruce Momjian's avatar
Bruce Momjian committed
7639
		TableInfo  *owning_tab;
7640

7641
		resetPQExpBuffer(query);
7642
		appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
Bruce Momjian's avatar
Bruce Momjian committed
7643

7644
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7645 7646 7647 7648 7649
		 * If this is a SERIAL sequence, then use the
		 * pg_get_serial_sequence function to avoid hard-coding the
		 * sequence name.  Note that this implicitly assumes that the
		 * sequence and its owning table are in the same schema, because
		 * we don't schema-qualify the reference.
7650 7651 7652 7653 7654 7655 7656
		 */
		if (OidIsValid(tbinfo->owning_tab) &&
			(owning_tab = findTableByOid(tbinfo->owning_tab)) != NULL)
		{
			appendPQExpBuffer(query, "pg_catalog.pg_get_serial_sequence(");
			appendStringLiteral(query, fmtId(owning_tab->dobj.name), true);
			appendPQExpBuffer(query, ", ");
Bruce Momjian's avatar
Bruce Momjian committed
7657
			appendStringLiteral(query, owning_tab->attnames[tbinfo->owning_col - 1], true);
7658 7659 7660 7661
			appendPQExpBuffer(query, ")");
		}
		else
			appendStringLiteral(query, fmtId(tbinfo->dobj.name), true);
7662 7663
		appendPQExpBuffer(query, ", %s, %s);\n",
						  last, (called ? "true" : "false"));
7664

7665
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
7666
					 tbinfo->dobj.name,
7667 7668
					 tbinfo->dobj.namespace->dobj.name,
					 NULL,
7669
					 tbinfo->rolname,
7670
					 false, "SEQUENCE SET", query->data, "", NULL,
7671 7672
					 &(tbinfo->dobj.dumpId), 1,
					 NULL, NULL);
7673
	}
Bruce Momjian's avatar
Bruce Momjian committed
7674

7675 7676 7677 7678
	if (!dataOnly)
	{
		/* Dump Sequence Comments */
		resetPQExpBuffer(query);
7679
		appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
7680
		dumpComment(fout, query->data,
7681
					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
7682
					tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
7683
	}
7684

7685 7686
	PQclear(res);

7687 7688
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
7689
}
7690

7691
static void
7692
dumpTrigger(Archive *fout, TriggerInfo *tginfo)
7693
{
7694
	TableInfo  *tbinfo = tginfo->tgtable;
7695 7696
	PQExpBuffer query;
	PQExpBuffer delqry;
7697 7698
	const char *p;
	int			findx;
7699

7700
	if (dataOnly)
7701 7702
		return;

7703 7704
	query = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
7705

7706 7707 7708 7709 7710
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
7711
					  fmtId(tginfo->dobj.name));
7712
	appendPQExpBuffer(delqry, "ON %s.",
7713
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7714
	appendPQExpBuffer(delqry, "%s;\n",
7715
					  fmtId(tbinfo->dobj.name));
7716

7717 7718 7719
	if (tginfo->tgisconstraint)
	{
		appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
7720
		appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
7721 7722 7723 7724
	}
	else
	{
		appendPQExpBuffer(query, "CREATE TRIGGER ");
7725
		appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
7726 7727
	}
	appendPQExpBuffer(query, "\n    ");
7728

7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755
	/* Trigger type */
	findx = 0;
	if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
		appendPQExpBuffer(query, "BEFORE");
	else
		appendPQExpBuffer(query, "AFTER");
	if (TRIGGER_FOR_INSERT(tginfo->tgtype))
	{
		appendPQExpBuffer(query, " INSERT");
		findx++;
	}
	if (TRIGGER_FOR_DELETE(tginfo->tgtype))
	{
		if (findx > 0)
			appendPQExpBuffer(query, " OR DELETE");
		else
			appendPQExpBuffer(query, " DELETE");
		findx++;
	}
	if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
	{
		if (findx > 0)
			appendPQExpBuffer(query, " OR UPDATE");
		else
			appendPQExpBuffer(query, " UPDATE");
	}
	appendPQExpBuffer(query, " ON %s\n",
7756
					  fmtId(tbinfo->dobj.name));
7757

7758 7759 7760 7761 7762 7763 7764 7765
	if (tginfo->tgisconstraint)
	{
		if (OidIsValid(tginfo->tgconstrrelid))
		{
			/* If we are using regclass, name is already quoted */
			if (g_fout->remoteVersion >= 70300)
				appendPQExpBuffer(query, "    FROM %s\n    ",
								  tginfo->tgconstrrelname);
7766
			else
7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777
				appendPQExpBuffer(query, "    FROM %s\n    ",
								  fmtId(tginfo->tgconstrrelname));
		}
		if (!tginfo->tgdeferrable)
			appendPQExpBuffer(query, "NOT ");
		appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
		if (tginfo->tginitdeferred)
			appendPQExpBuffer(query, "DEFERRED\n");
		else
			appendPQExpBuffer(query, "IMMEDIATE\n");
	}
7778

7779 7780 7781 7782
	if (TRIGGER_FOR_ROW(tginfo->tgtype))
		appendPQExpBuffer(query, "    FOR EACH ROW\n    ");
	else
		appendPQExpBuffer(query, "    FOR EACH STATEMENT\n    ");
Bruce Momjian's avatar
Bruce Momjian committed
7783

7784 7785 7786 7787 7788 7789 7790
	/* In 7.3, result of regproc is already quoted */
	if (g_fout->remoteVersion >= 70300)
		appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
						  tginfo->tgfname);
	else
		appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
						  fmtId(tginfo->tgfname));
7791

7792 7793 7794
	p = tginfo->tgargs;
	for (findx = 0; findx < tginfo->tgnargs; findx++)
	{
7795
		const char *s = p, *s2 = p;
7796

7797
		/* Set 'p' to end of arg string. marked by '\000' */
7798 7799 7800 7801
		for (;;)
		{
			p = strchr(p, '\\');
			if (p == NULL)
7802
			{
7803 7804
				write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
						  tginfo->tgargs,
7805 7806
						  tginfo->dobj.name,
						  tbinfo->dobj.name);
7807
				exit_nicely();
7808
			}
7809
			p++;
7810
			if (*p == '\\')		/* is it '\\'? */
7811
			{
7812 7813
				p++;
				continue;
7814
			}
7815
			if (p[0] == '0' && p[1] == '0' && p[2] == '0')  /* is it '\000'? */
7816 7817 7818
				break;
		}
		p--;
7819 7820 7821 7822

		while (s2 < p)
			if (*s2++ == '\\')
			{
7823
				appendPQExpBufferChar(query, ESCAPE_STRING_SYNTAX);
7824 7825 7826
				break;
			}

7827 7828 7829
		appendPQExpBufferChar(query, '\'');
		while (s < p)
		{
7830
			if (*s == '\'')		/* bytea already doubles backslashes */
7831
				appendPQExpBufferChar(query, '\'');
7832 7833 7834 7835 7836 7837 7838 7839
			appendPQExpBufferChar(query, *s++);
		}
		appendPQExpBufferChar(query, '\'');
		appendPQExpBuffer(query,
						  (findx < tginfo->tgnargs - 1) ? ", " : "");
		p = p + 4;
	}
	appendPQExpBuffer(query, ");\n");
7840

7841
	ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
7842 7843
				 tginfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7844
				 NULL,
7845
				 tbinfo->rolname, false,
7846 7847 7848
				 "TRIGGER", query->data, delqry->data, NULL,
				 tginfo->dobj.dependencies, tginfo->dobj.nDeps,
				 NULL, NULL);
7849

7850 7851
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "TRIGGER %s ",
7852
					  fmtId(tginfo->dobj.name));
7853
	appendPQExpBuffer(query, "ON %s",
7854
					  fmtId(tbinfo->dobj.name));
7855

7856
	dumpComment(fout, query->data,
7857
				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
7858
				tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
7859

7860 7861 7862
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
}
7863

7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875
/*
 * dumpRule
 *		Dump a rule
 */
static void
dumpRule(Archive *fout, RuleInfo *rinfo)
{
	TableInfo  *tbinfo = rinfo->ruletable;
	PQExpBuffer query;
	PQExpBuffer cmd;
	PQExpBuffer delcmd;
	PGresult   *res;
7876

7877 7878 7879 7880 7881
	/*
	 * Ignore rules for not-to-be-dumped tables
	 */
	if (tbinfo == NULL || !tbinfo->dump || dataOnly)
		return;
7882

7883
	/*
7884 7885
	 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
	 * we do not want to dump it as a separate object.
7886
	 */
7887
	if (!rinfo->separate)
7888
		return;
7889

7890 7891 7892
	/*
	 * Make sure we are in proper schema.
	 */
7893
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7894

7895 7896 7897
	query = createPQExpBuffer();
	cmd = createPQExpBuffer();
	delcmd = createPQExpBuffer();
7898

7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909
	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query,
						  "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid) AS definition",
						  rinfo->dobj.catId.oid);
	}
	else
	{
		/* Rule name was unique before 7.3 ... */
		appendPQExpBuffer(query,
						  "SELECT pg_get_ruledef('%s') AS definition",
7910
						  rinfo->dobj.name);
7911
	}
7912

7913 7914
	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
7915

7916 7917 7918
	if (PQntuples(res) != 1)
	{
		write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
7919
				  rinfo->dobj.name, tbinfo->dobj.name);
7920
		exit_nicely();
7921
	}
7922

7923 7924 7925 7926 7927 7928 7929
	printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delcmd, "DROP RULE %s ",
7930
					  fmtId(rinfo->dobj.name));
7931
	appendPQExpBuffer(delcmd, "ON %s.",
7932
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7933
	appendPQExpBuffer(delcmd, "%s;\n",
7934
					  fmtId(tbinfo->dobj.name));
7935 7936

	ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
7937 7938
				 rinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7939
				 NULL,
7940
				 tbinfo->rolname, false,
7941 7942 7943 7944 7945 7946 7947
				 "RULE", cmd->data, delcmd->data, NULL,
				 rinfo->dobj.dependencies, rinfo->dobj.nDeps,
				 NULL, NULL);

	/* Dump rule comments */
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "RULE %s",
7948
					  fmtId(rinfo->dobj.name));
7949
	appendPQExpBuffer(query, " ON %s",
7950
					  fmtId(tbinfo->dobj.name));
7951
	dumpComment(fout, query->data,
7952
				tbinfo->dobj.namespace->dobj.name,
7953
				tbinfo->rolname,
7954 7955 7956 7957
				rinfo->dobj.catId, 0, rinfo->dobj.dumpId);

	PQclear(res);

7958
	destroyPQExpBuffer(query);
7959 7960
	destroyPQExpBuffer(cmd);
	destroyPQExpBuffer(delcmd);
7961
}
7962

7963 7964 7965
/*
 * getDependencies --- obtain available dependency data
 */
7966
static void
7967
getDependencies(void)
7968
{
7969
	PQExpBuffer query;
Bruce Momjian's avatar
Bruce Momjian committed
7970
	PGresult   *res;
7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983
	int			ntups,
				i;
	int			i_classid,
				i_objid,
				i_refclassid,
				i_refobjid,
				i_deptype;
	DumpableObject *dobj,
			   *refdobj;

	/* No dependency info available before 7.3 */
	if (g_fout->remoteVersion < 70300)
		return;
7984 7985

	if (g_verbose)
Peter Eisentraut's avatar
Peter Eisentraut committed
7986
		write_msg(NULL, "reading dependency data\n");
7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008

	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	query = createPQExpBuffer();

	appendPQExpBuffer(query, "SELECT "
					  "classid, objid, refclassid, refobjid, deptype "
					  "FROM pg_depend "
					  "WHERE deptype != 'p' "
					  "ORDER BY 1,2");

	res = PQexec(g_conn, query->data);
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);

	ntups = PQntuples(res);

	i_classid = PQfnumber(res, "classid");
	i_objid = PQfnumber(res, "objid");
	i_refclassid = PQfnumber(res, "refclassid");
	i_refobjid = PQfnumber(res, "refobjid");
	i_deptype = PQfnumber(res, "deptype");
8009 8010

	/*
8011 8012 8013
	 * Since we ordered the SELECT by referencing ID, we can expect that
	 * multiple entries for the same object will appear together; this
	 * saves on searches.
8014
	 */
8015 8016 8017
	dobj = NULL;

	for (i = 0; i < ntups; i++)
8018
	{
8019 8020 8021
		CatalogId	objId;
		CatalogId	refobjId;
		char		deptype;
8022

8023 8024 8025 8026 8027
		objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
		objId.oid = atooid(PQgetvalue(res, i, i_objid));
		refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
		refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
		deptype = *(PQgetvalue(res, i, i_deptype));
8028

8029 8030 8031 8032
		if (dobj == NULL ||
			dobj->catId.tableoid != objId.tableoid ||
			dobj->catId.oid != objId.oid)
			dobj = findObjectByCatalogId(objId);
8033

8034
		/*
Bruce Momjian's avatar
Bruce Momjian committed
8035 8036 8037
		 * Failure to find objects mentioned in pg_depend is not
		 * unexpected, since for example we don't collect info about TOAST
		 * tables.
8038
		 */
8039
		if (dobj == NULL)
8040
		{
8041 8042 8043 8044 8045
#ifdef NOT_USED
			fprintf(stderr, "no referencing object %u %u\n",
					objId.tableoid, objId.oid);
#endif
			continue;
8046 8047
		}

8048
		refdobj = findObjectByCatalogId(refobjId);
Bruce Momjian's avatar
Bruce Momjian committed
8049

8050
		if (refdobj == NULL)
8051
		{
8052 8053 8054 8055 8056
#ifdef NOT_USED
			fprintf(stderr, "no referenced object %u %u\n",
					refobjId.tableoid, refobjId.oid);
#endif
			continue;
Bruce Momjian's avatar
Bruce Momjian committed
8057 8058
		}

8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071
		/*
		 * Ordinarily, table rowtypes have implicit dependencies on their
		 * tables.  However, for a composite type the implicit dependency
		 * goes the other way in pg_depend; which is the right thing for
		 * DROP but it doesn't produce the dependency ordering we need.
		 * So in that one case, we reverse the direction of the dependency.
		 */
		if (deptype == 'i' &&
			dobj->objType == DO_TABLE &&
			refdobj->objType == DO_TYPE)
			addObjectDependency(refdobj, dobj->dumpId);
		else					/* normal case */
			addObjectDependency(dobj, refdobj->dumpId);
8072
	}
8073

8074 8075
	PQclear(res);

8076
	destroyPQExpBuffer(query);
8077
}
8078

8079

8080 8081 8082 8083
/*
 * selectSourceSchema - make the specified schema the active search path
 * in the source database.
 *
8084 8085 8086 8087 8088 8089 8090
 * NB: pg_catalog is explicitly searched after the specified schema;
 * so user names are only qualified if they are cross-schema references,
 * and system names are only qualified if they conflict with a user name
 * in the current schema.
 *
 * Whenever the selected schema is not pg_catalog, be careful to qualify
 * references to system catalogs and types in our emitted commands!
8091 8092 8093 8094
 */
static void
selectSourceSchema(const char *schemaName)
{
Bruce Momjian's avatar
Bruce Momjian committed
8095
	static char *curSchemaName = NULL;
8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109
	PQExpBuffer query;

	/* Not relevant if fetching from pre-7.3 DB */
	if (g_fout->remoteVersion < 70300)
		return;
	/* Ignore null schema names */
	if (schemaName == NULL || *schemaName == '\0')
		return;
	/* Optimize away repeated selection of same schema */
	if (curSchemaName && strcmp(curSchemaName, schemaName) == 0)
		return;

	query = createPQExpBuffer();
	appendPQExpBuffer(query, "SET search_path = %s",
8110
					  fmtId(schemaName));
8111 8112
	if (strcmp(schemaName, "pg_catalog") != 0)
		appendPQExpBuffer(query, ", pg_catalog");
8113

8114 8115 8116
	do_sql_command(g_conn, query->data);

	destroyPQExpBuffer(query);
8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129
	if (curSchemaName)
		free(curSchemaName);
	curSchemaName = strdup(schemaName);
}

/*
 * getFormattedTypeName - retrieve a nicely-formatted type name for the
 * given type name.
 *
 * NB: in 7.3 and up the result may depend on the currently-selected
 * schema; this is why we don't try to cache the names.
 */
static char *
8130
getFormattedTypeName(Oid oid, OidOptions opts)
8131 8132 8133 8134 8135 8136
{
	char	   *result;
	PQExpBuffer query;
	PGresult   *res;
	int			ntups;

8137
	if (oid == 0)
8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149
	{
		if ((opts & zeroAsOpaque) != 0)
			return strdup(g_opaque_type);
		else if ((opts & zeroAsAny) != 0)
			return strdup("'any'");
		else if ((opts & zeroAsStar) != 0)
			return strdup("*");
		else if ((opts & zeroAsNone) != 0)
			return strdup("NONE");
	}

	query = createPQExpBuffer();
8150 8151
	if (g_fout->remoteVersion >= 70300)
	{
8152
		appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
8153 8154 8155
						  oid);
	}
	else if (g_fout->remoteVersion >= 70100)
8156
	{
8157
		appendPQExpBuffer(query, "SELECT format_type('%u'::oid, NULL)",
8158 8159 8160 8161 8162 8163
						  oid);
	}
	else
	{
		appendPQExpBuffer(query, "SELECT typname "
						  "FROM pg_type "
8164
						  "WHERE oid = '%u'::oid",
8165 8166 8167 8168
						  oid);
	}

	res = PQexec(g_conn, query->data);
8169
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
8170 8171 8172 8173 8174

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
8175
		write_msg(NULL, "query yielded %d rows instead of one: %s\n",
8176 8177 8178 8179
				  ntups, query->data);
		exit_nicely();
	}

8180 8181 8182 8183 8184 8185 8186 8187
	if (g_fout->remoteVersion >= 70100)
	{
		/* already quoted */
		result = strdup(PQgetvalue(res, 0, 0));
	}
	else
	{
		/* may need to quote it */
8188
		result = strdup(fmtId(PQgetvalue(res, 0, 0)));
8189
	}
8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203

	PQclear(res);
	destroyPQExpBuffer(query);

	return result;
}

/*
 * myFormatType --- local implementation of format_type for use with 7.0.
 */
static char *
myFormatType(const char *typname, int32 typmod)
{
	char	   *result;
Bruce Momjian's avatar
Bruce Momjian committed
8204
	bool		isarray = false;
8205 8206
	PQExpBuffer buf = createPQExpBuffer();

8207 8208 8209 8210 8211 8212 8213
	/* Handle array types */
	if (typname[0] == '_')
	{
		isarray = true;
		typname++;
	}

8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246
	/* Show lengths on bpchar and varchar */
	if (!strcmp(typname, "bpchar"))
	{
		int			len = (typmod - VARHDRSZ);

		appendPQExpBuffer(buf, "character");
		if (len > 1)
			appendPQExpBuffer(buf, "(%d)",
							  typmod - VARHDRSZ);
	}
	else if (!strcmp(typname, "varchar"))
	{
		appendPQExpBuffer(buf, "character varying");
		if (typmod != -1)
			appendPQExpBuffer(buf, "(%d)",
							  typmod - VARHDRSZ);
	}
	else if (!strcmp(typname, "numeric"))
	{
		appendPQExpBuffer(buf, "numeric");
		if (typmod != -1)
		{
			int32		tmp_typmod;
			int			precision;
			int			scale;

			tmp_typmod = typmod - VARHDRSZ;
			precision = (tmp_typmod >> 16) & 0xffff;
			scale = tmp_typmod & 0xffff;
			appendPQExpBuffer(buf, "(%d,%d)",
							  precision, scale);
		}
	}
Bruce Momjian's avatar
Bruce Momjian committed
8247

8248 8249 8250 8251
	/*
	 * char is an internal single-byte data type; Let's make sure we force
	 * it through with quotes. - thomas 1998-12-13
	 */
Bruce Momjian's avatar
Bruce Momjian committed
8252
	else if (strcmp(typname, "char") == 0)
8253
		appendPQExpBuffer(buf, "\"char\"");
8254
	else
8255
		appendPQExpBuffer(buf, "%s", fmtId(typname));
8256

8257 8258 8259 8260
	/* Append array qualifier for array types */
	if (isarray)
		appendPQExpBuffer(buf, "[]");

8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286
	result = strdup(buf->data);
	destroyPQExpBuffer(buf);

	return result;
}

/*
 * fmtQualifiedId - convert a qualified name to the proper format for
 * the source database.
 *
 * Like fmtId, use the result before calling again.
 */
static const char *
fmtQualifiedId(const char *schema, const char *id)
{
	static PQExpBuffer id_return = NULL;

	if (id_return)				/* first time through? */
		resetPQExpBuffer(id_return);
	else
		id_return = createPQExpBuffer();

	/* Suppress schema name if fetching from pre-7.3 DB */
	if (g_fout->remoteVersion >= 70300 && schema && *schema)
	{
		appendPQExpBuffer(id_return, "%s.",
8287
						  fmtId(schema));
8288 8289
	}
	appendPQExpBuffer(id_return, "%s",
8290
					  fmtId(id));
8291 8292 8293

	return id_return->data;
}
8294 8295

/*
8296 8297 8298
 * Return a column list clause for the given relation.
 *
 * Special case: if there are no undropped columns in the relation, return
8299
 * "", not an invalid "()" column list.
8300
 */
8301 8302
static const char *
fmtCopyColumnList(const TableInfo *ti)
8303 8304 8305
{
	static PQExpBuffer q = NULL;
	int			numatts = ti->numatts;
Bruce Momjian's avatar
Bruce Momjian committed
8306 8307 8308 8309
	char	  **attnames = ti->attnames;
	bool	   *attisdropped = ti->attisdropped;
	bool		needComma;
	int			i;
8310

Bruce Momjian's avatar
Bruce Momjian committed
8311
	if (q)						/* first time through? */
8312 8313 8314 8315
		resetPQExpBuffer(q);
	else
		q = createPQExpBuffer();

8316 8317
	appendPQExpBuffer(q, "(");
	needComma = false;
8318 8319
	for (i = 0; i < numatts; i++)
	{
8320 8321 8322
		if (attisdropped[i])
			continue;
		if (needComma)
8323 8324
			appendPQExpBuffer(q, ", ");
		appendPQExpBuffer(q, "%s", fmtId(attnames[i]));
8325
		needComma = true;
8326
	}
8327 8328

	if (!needComma)
8329
		return "";				/* no undropped columns */
8330

8331 8332 8333
	appendPQExpBuffer(q, ")");
	return q->data;
}
8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370

/*
 * Convenience subroutine to execute a SQL command and check for
 * COMMAND_OK status.
 */
static void
do_sql_command(PGconn *conn, const char *query)
{
	PGresult   *res;

	res = PQexec(conn, query);
	check_sql_result(res, conn, query, PGRES_COMMAND_OK);
	PQclear(res);
}

/*
 * Convenience subroutine to verify a SQL command succeeded,
 * and exit with a useful error message if not.
 */
static void
check_sql_result(PGresult *res, PGconn *conn, const char *query,
				 ExecStatusType expected)
{
	const char *err;

	if (res && PQresultStatus(res) == expected)
		return;					/* A-OK */

	write_msg(NULL, "SQL command failed\n");
	if (res)
		err = PQresultErrorMessage(res);
	else
		err = PQerrorMessage(conn);
	write_msg(NULL, "Error message from server: %s", err);
	write_msg(NULL, "The command was: %s\n", query);
	exit_nicely();
}