pg_dump.c 232 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.425 2006/01/06 19:08:33 momjian 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
		 * the following options don't have an equivalent short option letter,
		 * but are available as '-X long-name'
244
		 */
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
			case 'c':			/* clean (i.e., drop) schema prior to create */
297 298
				outputClean = 1;
				break;
299

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

363
			case 't':			/* Dump data for this table only */
Bruce Momjian's avatar
Bruce Momjian committed
364
				selectTableName = strdup(optarg);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
				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;

				/*
389 390 391 392 393
				 * 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'.
394 395
				 */
			case 'X':
396 397
				if (strcmp(optarg, "disable-dollar-quoting") == 0)
					disable_dollar_quoting = 1;
398 399
				else if (strcmp(optarg, "disable-triggers") == 0)
					disable_triggers = 1;
400
				else if (strcmp(optarg, "use-set-session-authorization") == 0)
401
					use_setsessauth = 1;
402 403 404
				else
				{
					fprintf(stderr,
405
							_("%s: invalid -X option -- %s\n"),
406
							progname, optarg);
407
					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
408 409 410
					exit(1);
				}
				break;
411

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

417 418
			case 0:
				break;
419

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

426 427
	if (optind < (argc - 1))
	{
428 429 430 431
		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);
432 433
		exit(1);
	}
434

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

519
	do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
520

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

Bruce Momjian's avatar
Bruce Momjian committed
524 525 526
	/* Set the client encoding */
	if (dumpencoding)
	{
527 528 529
		char	   *cmd = malloc(strlen(dumpencoding) + 32);

		sprintf(cmd, "SET client_encoding='%s'", dumpencoding);
Bruce Momjian's avatar
Bruce Momjian committed
530 531 532 533
		do_sql_command(g_conn, cmd);
		free(cmd);
	}

534 535 536 537 538 539 540 541
	/* 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 =";

542 543 544 545 546
	/*
	 * 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)
547
		do_sql_command(g_conn, "SET extra_float_digits TO 2");
548

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

560 561 562 563 564 565 566 567 568
	/*
	 * 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
569
	if (outputBlobs && hasBlobs(g_fout))
570
	{
Tom Lane's avatar
Tom Lane committed
571
		/* Add placeholders to allow correct sorting of blobs */
572 573 574 575 576 577 578
		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
579 580 581 582 583 584

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

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

	/*
	 * Sort the objects into a safe dump order (no forward references).
594 595 596
	 *
	 * 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
597 598 599
	 * 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.
600 601 602
	 */
	getDumpableObjects(&dobjs, &numObjs);

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

608 609 610
	sortDumpableObjects(dobjs, numObjs);

	/*
611 612
	 * Create archive TOC entries for all the objects to be dumped, in a safe
	 * order.
613 614
	 */

615 616 617
	/* First the special encoding entry. */
	dumpEncoding(g_fout);

618 619
	/* The database item is always second, unless we don't want it at all */
	if (!dataOnly && selectTableName == NULL && selectSchemaName == NULL)
620
		dumpDatabase(g_fout);
621

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

626 627 628
	/*
	 * And finally we can do the actual output.
	 */
629 630 631 632 633 634 635 636 637 638
	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;
639
		ropt->use_setsessauth = use_setsessauth;
640
		ropt->dataOnly = dataOnly;
641

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

647
		ropt->suppressDumpWarnings = true;		/* We've already shown them */
648

649 650
		RestoreArchive(g_fout, ropt);
	}
651

652
	CloseArchive(g_fout);
653

654
	PQfinish(g_conn);
655

656 657
	exit(0);
}
658 659


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

667 668 669 670 671 672 673 674 675 676 677
	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
678 679 680 681 682
	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
683
	printf(_("  -E, --encoding=ENCODING  dump the data in encoding ENCODING\n"));
684
	printf(_("  -n, --schema=SCHEMA      dump the named schema only\n"));
Bruce Momjian's avatar
Bruce Momjian committed
685
	printf(_("  -o, --oids               include OIDs in dump\n"));
686
	printf(_("  -O, --no-owner           skip restoration of object ownership\n"
687
			 "                           in plain text format\n"));
Bruce Momjian's avatar
Bruce Momjian committed
688 689 690
	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"));
691
	printf(_("  -t, --table=TABLE        dump the named table only\n"));
Bruce Momjian's avatar
Bruce Momjian committed
692
	printf(_("  -x, --no-privileges      do not dump privileges (grant/revoke)\n"));
693 694
	printf(_("  -X disable-dollar-quoting, --disable-dollar-quoting\n"
			 "                           disable dollar quoting, use SQL standard quoting\n"));
Bruce Momjian's avatar
Bruce Momjian committed
695 696
	printf(_("  -X disable-triggers, --disable-triggers\n"
			 "                           disable triggers during data-only restore\n"));
697 698 699
	printf(_("  -X use-set-session-authorization, --use-set-session-authorization\n"
			 "                           use SESSION AUTHORIZATION commands instead of\n"
			 "                           OWNER TO commands\n"));
700 701

	printf(_("\nConnection options:\n"));
702
	printf(_("  -h, --host=HOSTNAME      database server host or socket directory\n"));
703 704 705
	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"));
706

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

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

721 722 723 724 725 726 727 728 729
/*
 * 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
730 731
	 * namespaces.	If a specific namespace is being dumped, dump just that
	 * namespace. Otherwise, dump all non-system namespaces.
732
	 */
Bruce Momjian's avatar
Bruce Momjian committed
733
	if (selectTableName != NULL)
734
		nsinfo->dump = false;
Bruce Momjian's avatar
Bruce Momjian committed
735 736
	else if (selectSchemaName != NULL)
	{
737
		if (strcmp(nsinfo->dobj.name, selectSchemaName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
738 739 740 741
			nsinfo->dump = true;
		else
			nsinfo->dump = false;
	}
742 743
	else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
			 strcmp(nsinfo->dobj.name, "information_schema") == 0)
744 745 746 747
		nsinfo->dump = false;
	else
		nsinfo->dump = true;
}
748

749 750 751 752 753 754 755 756 757
/*
 * 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
758 759
	 * tablename has been specified, dump matching table name; else, do not
	 * dump.
760
	 */
761
	tbinfo->dump = false;
762
	if (tbinfo->dobj.namespace->dump)
763
		tbinfo->dump = true;
764
	else if (selectTableName != NULL &&
765
			 strcmp(tbinfo->dobj.name, selectTableName) == 0)
766 767 768 769
	{
		/* If both -s and -t specified, must match both to dump */
		if (selectSchemaName == NULL)
			tbinfo->dump = true;
770
		else if (strcmp(tbinfo->dobj.namespace->dobj.name, selectSchemaName) == 0)
771 772
			tbinfo->dump = true;
	}
773
}
774

775 776 777 778 779
/*
 *	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.
 */
780

781
#define COPYBUFSIZ		8192
782

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

798
	if (g_verbose)
799
		write_msg(NULL, "dumping contents of table %s\n", classname);
800

801 802
	/*
	 * Make sure we are in proper schema.  We will qualify the table name
803 804 805
	 * 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.
806
	 */
807
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
808

809 810
	/*
	 * If possible, specify the column list explicitly so that we have no
811 812 813
	 * 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.)
814 815 816 817 818
	 */
	if (g_fout->remoteVersion >= 70300)
		column_list = fmtCopyColumnList(tbinfo);
	else
		column_list = "";		/* can't select columns in COPY */
819

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

837
	copydone = false;
838

839
	while (!copydone)
840
	{
841
		ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
842

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
		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:
858
					archputs("\n", fout);
859 860 861 862 863
					break;
				case 1:
					break;
			}
		}
864

865 866 867
		/*
		 * THROTTLE:
		 *
868 869 870 871
		 * 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.
872
		 *
873 874
		 * 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'
875 876
		 * implementation was suggested. The latter failed because the loop
		 * was too tight. Finally, the following was implemented:
877
		 *
878 879
		 * 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
880
		 * 100ms, then sleep reset timer EndIf EndIf
881
		 *
882 883
		 * where the throttle value was the number of ms to sleep per ms of
		 * work. The calculation was done in each loop.
884
		 *
885 886 887
		 * 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
888 889
		 * multi-processor machines, it had little or no effect, for reasons
		 * that were unclear.
890 891 892
		 *
		 * Further discussion ensued, and the proposal was dropped.
		 *
893 894 895 896 897
		 * 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.
898 899 900
		 *
		 * select(0, NULL, NULL, NULL, &tvi);
		 *
901 902
		 * This will return after the interval specified in the structure tvi.
		 * Finally, call gettimeofday again to save the 'last sleep time'.
903
		 */
904
	}
905
	archprintf(fout, "\\.\n\n\n");
906

907 908
	ret = PQendcopy(g_conn);
	if (ret != 0)
909
	{
910 911 912 913
		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();
914 915
	}

916
	PQclear(res);
917 918 919
	destroyPQExpBuffer(q);
	return 1;
}
920

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

933
	/*
934
	 * Make sure we are in proper schema.  We will qualify the table name
935 936 937
	 * 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.
938
	 */
939
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
940

941 942 943 944
	if (fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
						  "SELECT * FROM ONLY %s",
945 946
						  fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
										 classname));
947
	}
948
	else
949 950 951
	{
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
						  "SELECT * FROM %s",
952 953
						  fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
										 classname));
954
	}
955

956
	res = PQexec(g_conn, q->data);
957
	check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
958

959 960 961
	do
	{
		PQclear(res);
962

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

				/* XXX This code is partially duplicated in ruleutils.c */
1001 1002 1003 1004
				switch (PQftype(res, field))
				{
					case INT2OID:
					case INT4OID:
1005 1006
					case INT8OID:
					case OIDOID:
1007
					case FLOAT4OID:
1008 1009
					case FLOAT8OID:
					case NUMERICOID:
Bruce Momjian's avatar
Bruce Momjian committed
1010 1011
						{
							/*
1012 1013 1014 1015 1016
							 * 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.
Bruce Momjian's avatar
Bruce Momjian committed
1017
							 *
1018 1019 1020
							 * In reality we only need to defend against
							 * infinity and NaN, so we need not get too crazy
							 * about pattern matching here.
Bruce Momjian's avatar
Bruce Momjian committed
1021 1022 1023 1024 1025 1026 1027 1028 1029
							 */
							const char *s = PQgetvalue(res, tuple, field);

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

1031 1032 1033 1034 1035
					case BITOID:
					case VARBITOID:
						archprintf(fout, "B'%s'",
								   PQgetvalue(res, tuple, field));
						break;
1036

1037
					case BOOLOID:
Bruce Momjian's avatar
Bruce Momjian committed
1038
						if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
1039 1040 1041 1042 1043 1044 1045
							archprintf(fout, "true");
						else
							archprintf(fout, "false");
						break;

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

1056
	PQclear(res);
1057

1058 1059 1060
	archprintf(fout, "\n\n");

	do_sql_command(g_conn, "CLOSE _pg_dump_cursor");
1061

1062 1063 1064
	destroyPQExpBuffer(q);
	return 1;
}
1065

1066

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

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

	ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
1101 1102
				 tbinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
1103
				 NULL,
1104
				 tbinfo->rolname, false,
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
				 "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)
{
1119
	int			i;
1120

1121 1122
	for (i = 0; i < numTables; i++)
	{
1123
		/* Skip VIEWs (no data to dump) */
1124 1125
		if (tblinfo[i].relkind == RELKIND_VIEW)
			continue;
1126 1127
		/* Skip SEQUENCEs (handled elsewhere) */
		if (tblinfo[i].relkind == RELKIND_SEQUENCE)
1128
			continue;
1129

1130 1131
		if (tblinfo[i].dump)
		{
1132
			TableDataInfo *tdinfo;
1133

1134
			tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
1135

1136
			tdinfo->dobj.objType = DO_TABLE_DATA;
Bruce Momjian's avatar
Bruce Momjian committed
1137

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

1154

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

	datname = PQdb(g_conn);
1180 1181

	if (g_verbose)
1182
		write_msg(NULL, "saving database definition\n");
1183

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

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

	res = PQexec(g_conn, dbQry->data);
	check_sql_result(res, g_conn, dbQry->data, PGRES_TUPLES_OK);
1226 1227 1228

	ntups = PQntuples(res);

1229 1230
	if (ntups <= 0)
	{
1231 1232
		write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
				  datname);
1233
		exit_nicely();
1234 1235
	}

1236 1237
	if (ntups != 1)
	{
1238
		write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
1239
				  ntups, datname);
1240
		exit_nicely();
1241 1242
	}

1243 1244
	i_tableoid = PQfnumber(res, "tableoid");
	i_oid = PQfnumber(res, "oid");
1245
	i_dba = PQfnumber(res, "dba");
1246
	i_encoding = PQfnumber(res, "encoding");
1247
	i_tablespace = PQfnumber(res, "tablespace");
1248 1249 1250

	dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
	dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
1251 1252
	dba = PQgetvalue(res, 0, i_dba);
	encoding = PQgetvalue(res, 0, i_encoding);
1253
	tablespace = PQgetvalue(res, 0, i_tablespace);
1254 1255

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

	appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
1268
					  fmtId(datname));
1269

1270 1271 1272 1273 1274
	dbDumpId = createDumpId();

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

1289 1290 1291 1292 1293 1294
	/* Dump DB comment if any */
	resetPQExpBuffer(dbQry);
	appendPQExpBuffer(dbQry, "DATABASE %s", fmtId(datname));
	dumpComment(AH, dbQry->data, NULL, "",
				dbCatId, 0, dbDumpId);

1295 1296
	PQclear(res);

1297 1298 1299
	destroyPQExpBuffer(dbQry);
	destroyPQExpBuffer(delQry);
	destroyPQExpBuffer(creaQry);
1300 1301 1302
}


1303 1304 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
/*
 * 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(),
1334
				 "ENCODING", NULL, NULL, "",
1335
				 false, "ENCODING", qry->data, "", NULL,
1336 1337 1338 1339 1340 1341 1342 1343 1344
				 NULL, 0,
				 NULL, NULL);

	PQclear(res);

	destroyPQExpBuffer(qry);
}


Tom Lane's avatar
Tom Lane committed
1345 1346 1347 1348 1349 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
/*
 * 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;
}

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

	if (g_verbose)
1390
		write_msg(NULL, "saving large objects\n");
1391

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

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

Tom Lane's avatar
Tom Lane committed
1401 1402
	res = PQexec(g_conn, blobQry);
	check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
1403

Tom Lane's avatar
Tom Lane committed
1404 1405
	/* Command to fetch from cursor */
	blobFetchQry = "FETCH 1000 IN bloboid";
1406

1407 1408
	do
	{
1409 1410
		PQclear(res);

1411
		/* Do a fetch */
Tom Lane's avatar
Tom Lane committed
1412 1413
		res = PQexec(g_conn, blobFetchQry);
		check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
1414 1415 1416 1417

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

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

			StartBlob(AH, blobOid);

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

1444 1445
				WriteData(AH, buf, cnt);
			} while (cnt > 0);
1446

1447
			lo_close(g_conn, loFd);
1448

1449 1450 1451
			EndBlob(AH, blobOid);
		}
	} while (PQntuples(res) > 0);
1452

1453 1454
	PQclear(res);

Tom Lane's avatar
Tom Lane committed
1455 1456 1457 1458 1459 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
	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++)
		{
1506 1507
			Oid			blobOid;
			char	   *comment;
Tom Lane's avatar
Tom Lane committed
1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529

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

1531
	return 1;
1532 1533 1534
}

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

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

1563 1564 1565 1566
		nsinfo[0].dobj.objType = DO_NAMESPACE;
		nsinfo[0].dobj.catId.tableoid = 0;
		nsinfo[0].dobj.catId.oid = 0;
		AssignDumpId(&nsinfo[0].dobj);
1567
		nsinfo[0].dobj.name = strdup("public");
1568
		nsinfo[0].rolname = strdup("");
1569 1570 1571 1572
		nsinfo[0].nspacl = strdup("");

		selectDumpableNamespace(&nsinfo[0]);

1573 1574 1575 1576
		nsinfo[1].dobj.objType = DO_NAMESPACE;
		nsinfo[1].dobj.catId.tableoid = 0;
		nsinfo[1].dobj.catId.oid = 1;
		AssignDumpId(&nsinfo[1].dobj);
1577
		nsinfo[1].dobj.name = strdup("pg_catalog");
1578
		nsinfo[1].rolname = strdup("");
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
		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");
1593

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

Bruce Momjian's avatar
Bruce Momjian committed
1603
	res = PQexec(g_conn, query->data);
1604
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1605 1606 1607

	ntups = PQntuples(res);

1608
	nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
1609

1610
	i_tableoid = PQfnumber(res, "tableoid");
1611
	i_oid = PQfnumber(res, "oid");
1612
	i_nspname = PQfnumber(res, "nspname");
1613
	i_rolname = PQfnumber(res, "rolname");
1614
	i_nspacl = PQfnumber(res, "nspacl");
1615 1616 1617

	for (i = 0; i < ntups; i++)
	{
1618 1619 1620 1621
		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);
1622
		nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
1623
		nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1624
		nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
1625

1626 1627
		/* Decide whether to dump this namespace */
		selectDumpableNamespace(&nsinfo[i]);
1628

1629
		if (strlen(nsinfo[i].rolname) == 0)
1630
			write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
1631
					  nsinfo[i].dobj.name);
1632 1633
	}

Bruce Momjian's avatar
Bruce Momjian committed
1634
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1635 1636
	 * 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
1637 1638 1639 1640
	 */
	if (selectSchemaName)
	{
		for (i = 0; i < ntups; i++)
1641
			if (strcmp(nsinfo[i].dobj.name, selectSchemaName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1642 1643 1644 1645 1646
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
1647
			write_msg(NULL, "specified schema \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
1648 1649 1650 1651 1652
					  selectSchemaName);
			exit_nicely();
		}
	}

1653
	PQclear(res);
1654 1655
	destroyPQExpBuffer(query);

1656 1657
	g_namespaces = nsinfo;
	g_numNamespaces = *numNamespaces = ntups;
1658

1659
	return nsinfo;
1660 1661
}

1662 1663 1664 1665 1666 1667
/*
 * 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
1668
 * a system object or not.	In 7.3 and later there is no guessing.
1669 1670
 */
static NamespaceInfo *
1671
findNamespace(Oid nsoid, Oid objoid)
1672
{
1673
	int			i;
1674

1675
	if (g_fout->remoteVersion >= 70300)
1676
	{
1677
		for (i = 0; i < g_numNamespaces; i++)
1678
		{
Bruce Momjian's avatar
Bruce Momjian committed
1679
			NamespaceInfo *nsinfo = &g_namespaces[i];
1680

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

1697
	return NULL;				/* keep compiler quiet */
1698
}
1699 1700

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

1731
	/*
1732 1733
	 * we include even the built-in types because those may be used as array
	 * elements by user-defined types
1734 1735
	 *
	 * we filter out the built-in types when we dump out the types
1736 1737
	 *
	 * same approach for undefined (shell) types
1738
	 */
1739

1740 1741 1742 1743
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

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

Bruce Momjian's avatar
Bruce Momjian committed
1785
	res = PQexec(g_conn, query->data);
1786
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1787 1788 1789

	ntups = PQntuples(res);

1790
	tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
1791

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

	for (i = 0; i < ntups; i++)
	{
1807 1808 1809 1810 1811 1812 1813
		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);
1814 1815
		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
1816
												tinfo[i].dobj.catId.oid);
1817
		tinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1818 1819 1820 1821
		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
1822
		tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
1823 1824
		tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);

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

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

1844 1845 1846 1847
		if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
			tinfo[i].isDefined = true;
		else
			tinfo[i].isDefined = false;
1848

1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
		/*
		 * 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
1859 1860 1861
		 * 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.)
1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
		 */
		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);

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

1877 1878
	*numTypes = ntups;

1879 1880
	PQclear(res);

1881 1882
	destroyPQExpBuffer(query);

1883
	return tinfo;
1884 1885 1886
}

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

1908
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1909 1910
	 * find all operators, including builtin operators; we filter out
	 * system-defined operators at dump-out time.
1911
	 */
1912

1913 1914 1915 1916
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

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

Bruce Momjian's avatar
Bruce Momjian committed
1946
	res = PQexec(g_conn, query->data);
1947
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1948 1949

	ntups = PQntuples(res);
1950
	*numOprs = ntups;
1951

1952
	oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
1953

1954
	i_tableoid = PQfnumber(res, "tableoid");
1955
	i_oid = PQfnumber(res, "oid");
1956 1957
	i_oprname = PQfnumber(res, "oprname");
	i_oprnamespace = PQfnumber(res, "oprnamespace");
1958
	i_rolname = PQfnumber(res, "rolname");
1959
	i_oprcode = PQfnumber(res, "oprcode");
1960 1961 1962

	for (i = 0; i < ntups; i++)
	{
1963 1964 1965 1966
		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);
1967 1968
		oprinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_oprname));
		oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)),
1969
												  oprinfo[i].dobj.catId.oid);
1970
		oprinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
1971
		oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
1972

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

	PQclear(res);

1980
	destroyPQExpBuffer(query);
1981

1982
	return oprinfo;
1983 1984
}

1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998
/*
 * 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
1999
	ConvInfo   *convinfo;
2000
	int			i_tableoid;
2001 2002 2003
	int			i_oid;
	int			i_conname;
	int			i_connamespace;
2004
	int			i_rolname;
2005 2006

	/* Conversions didn't exist pre-7.3 */
Bruce Momjian's avatar
Bruce Momjian committed
2007 2008
	if (g_fout->remoteVersion < 70300)
	{
2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020
		*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");

2021
	appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
2022
					  "connamespace, "
2023 2024 2025
					  "(%s conowner) as rolname "
					  "FROM pg_conversion",
					  username_subquery);
2026 2027

	res = PQexec(g_conn, query->data);
2028
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2029 2030 2031 2032 2033 2034

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

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

2035
	i_tableoid = PQfnumber(res, "tableoid");
2036 2037 2038
	i_oid = PQfnumber(res, "oid");
	i_conname = PQfnumber(res, "conname");
	i_connamespace = PQfnumber(res, "connamespace");
2039
	i_rolname = PQfnumber(res, "rolname");
2040 2041 2042

	for (i = 0; i < ntups; i++)
	{
2043 2044 2045 2046
		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);
2047 2048
		convinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace)),
2049
												 convinfo[i].dobj.catId.oid);
2050
		convinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2051 2052 2053 2054 2055 2056 2057 2058 2059
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return convinfo;
}

2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073
/*
 * 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
2074
	OpclassInfo *opcinfo;
2075
	int			i_tableoid;
2076 2077 2078
	int			i_oid;
	int			i_opcname;
	int			i_opcnamespace;
2079
	int			i_rolname;
2080 2081

	/*
Bruce Momjian's avatar
Bruce Momjian committed
2082 2083
	 * find all opclasses, including builtin opclasses; we filter out
	 * system-defined opclasses at dump-out time.
2084 2085 2086 2087 2088 2089 2090
	 */

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

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

	res = PQexec(g_conn, query->data);
2115
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2116 2117 2118 2119 2120 2121

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

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

2122
	i_tableoid = PQfnumber(res, "tableoid");
2123 2124 2125
	i_oid = PQfnumber(res, "oid");
	i_opcname = PQfnumber(res, "opcname");
	i_opcnamespace = PQfnumber(res, "opcnamespace");
2126
	i_rolname = PQfnumber(res, "rolname");
2127 2128 2129

	for (i = 0; i < ntups; i++)
	{
2130 2131 2132 2133
		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);
2134 2135
		opcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_opcname));
		opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)),
2136
												  opcinfo[i].dobj.catId.oid);
2137
		opcinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2138 2139 2140

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

	PQclear(res);

	destroyPQExpBuffer(query);

	return opcinfo;
}

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

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

2180 2181 2182
	/* find all user-defined aggregates */

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

Bruce Momjian's avatar
Bruce Momjian committed
2222
	res = PQexec(g_conn, query->data);
2223
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2224 2225

	ntups = PQntuples(res);
2226
	*numAggs = ntups;
2227

2228
	agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
2229

2230
	i_tableoid = PQfnumber(res, "tableoid");
2231 2232 2233
	i_oid = PQfnumber(res, "oid");
	i_aggname = PQfnumber(res, "aggname");
	i_aggnamespace = PQfnumber(res, "aggnamespace");
2234
	i_aggbasetype = PQfnumber(res, "aggbasetype");
2235
	i_rolname = PQfnumber(res, "rolname");
2236
	i_aggacl = PQfnumber(res, "aggacl");
2237 2238 2239

	for (i = 0; i < ntups; i++)
	{
2240 2241 2242 2243
		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);
2244 2245
		agginfo[i].aggfn.dobj.name = strdup(PQgetvalue(res, i, i_aggname));
		agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)),
2246
											agginfo[i].aggfn.dobj.catId.oid);
2247 2248
		agginfo[i].aggfn.rolname = strdup(PQgetvalue(res, i, i_rolname));
		if (strlen(agginfo[i].aggfn.rolname) == 0)
2249
			write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
2250
					  agginfo[i].aggfn.dobj.name);
2251
		agginfo[i].aggfn.lang = InvalidOid;		/* not currently interesting */
2252 2253 2254
		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
2255
		agginfo[i].aggfn.prorettype = InvalidOid;		/* not saved */
2256
		agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
2257
		agginfo[i].anybasetype = false; /* computed when it's dumped */
Bruce Momjian's avatar
Bruce Momjian committed
2258
		agginfo[i].fmtbasetype = NULL;	/* computed when it's dumped */
2259
	}
2260

2261
	PQclear(res);
2262

2263
	destroyPQExpBuffer(query);
2264

2265 2266
	return agginfo;
}
2267

2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282
/*
 * 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;
2283
	int			i_tableoid;
2284 2285 2286
	int			i_oid;
	int			i_proname;
	int			i_pronamespace;
2287
	int			i_rolname;
2288 2289 2290 2291
	int			i_prolang;
	int			i_pronargs;
	int			i_proargtypes;
	int			i_prorettype;
2292
	int			i_proacl;
2293

2294 2295
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
Bruce Momjian's avatar
Bruce Momjian committed
2296

2297
	/* find all user-defined funcs */
2298

2299 2300 2301
	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query,
2302
						  "SELECT tableoid, oid, proname, prolang, "
2303
						  "pronargs, proargtypes, prorettype, proacl, "
2304
						  "pronamespace, "
2305
						  "(%s proowner) as rolname "
2306 2307
						  "FROM pg_proc "
						  "WHERE NOT proisagg "
2308 2309 2310
						  "AND pronamespace != "
						  "(select oid from pg_namespace"
						  " where nspname = 'pg_catalog')",
2311
						  username_subquery);
2312
	}
2313 2314 2315 2316 2317 2318 2319
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, proname, prolang, "
						  "pronargs, proargtypes, prorettype, "
						  "'{=X}' as proacl, "
						  "0::oid as pronamespace, "
2320
						  "(%s proowner) as rolname "
2321 2322
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
2323
						  username_subquery,
2324 2325
						  g_last_builtin_oid);
	}
2326 2327 2328
	else
	{
		appendPQExpBuffer(query,
2329
						  "SELECT "
2330 2331
						  "(SELECT oid FROM pg_class "
						  " WHERE relname = 'pg_proc') AS tableoid, "
2332
						  "oid, proname, prolang, "
2333
						  "pronargs, proargtypes, prorettype, "
2334
						  "'{=X}' as proacl, "
2335
						  "0::oid as pronamespace, "
2336
						  "(%s proowner) as rolname "
2337 2338
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
2339
						  username_subquery,
2340 2341
						  g_last_builtin_oid);
	}
2342

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

2346
	ntups = PQntuples(res);
Bruce Momjian's avatar
Bruce Momjian committed
2347

2348
	*numFuncs = ntups;
2349

2350
	finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
2351

2352
	i_tableoid = PQfnumber(res, "tableoid");
2353 2354 2355
	i_oid = PQfnumber(res, "oid");
	i_proname = PQfnumber(res, "proname");
	i_pronamespace = PQfnumber(res, "pronamespace");
2356
	i_rolname = PQfnumber(res, "rolname");
2357 2358 2359 2360
	i_prolang = PQfnumber(res, "prolang");
	i_pronargs = PQfnumber(res, "pronargs");
	i_proargtypes = PQfnumber(res, "proargtypes");
	i_prorettype = PQfnumber(res, "prorettype");
2361
	i_proacl = PQfnumber(res, "proacl");
2362

2363 2364
	for (i = 0; i < ntups; i++)
	{
2365 2366 2367 2368
		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);
2369
		finfo[i].dobj.name = strdup(PQgetvalue(res, i, i_proname));
2370
		finfo[i].dobj.namespace =
2371
			findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)),
2372
						  finfo[i].dobj.catId.oid);
2373
		finfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2374
		finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
2375
		finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
2376
		finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
2377 2378 2379
		finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
		if (finfo[i].nargs == 0)
			finfo[i].argtypes = NULL;
2380
		else
2381
		{
2382 2383 2384
			finfo[i].argtypes = (Oid *) malloc(finfo[i].nargs * sizeof(Oid));
			parseOidArray(PQgetvalue(res, i, i_proargtypes),
						  finfo[i].argtypes, finfo[i].nargs);
2385
		}
2386

2387
		if (strlen(finfo[i].rolname) == 0)
2388 2389
			write_msg(NULL,
				 "WARNING: owner of function \"%s\" appears to be invalid\n",
2390
					  finfo[i].dobj.name);
2391
	}
2392

2393
	PQclear(res);
2394

2395
	destroyPQExpBuffer(query);
2396

2397 2398
	return finfo;
}
2399

2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416
/*
 * 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;
2417
	int			i_reltableoid;
2418 2419 2420 2421 2422
	int			i_reloid;
	int			i_relname;
	int			i_relnamespace;
	int			i_relkind;
	int			i_relacl;
2423
	int			i_rolname;
2424 2425 2426 2427 2428
	int			i_relchecks;
	int			i_reltriggers;
	int			i_relhasindex;
	int			i_relhasrules;
	int			i_relhasoids;
2429 2430
	int			i_owning_tab;
	int			i_owning_col;
2431
	int			i_reltablespace;
2432

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

2436 2437 2438
	/*
	 * Find all the tables (including views and sequences).
	 *
2439 2440
	 * We include system catalogs, so that we can work if a user table is
	 * defined to inherit from a system catalog (pretty weird, but...)
2441
	 *
2442 2443 2444
	 * We ignore tables that are not type 'r' (ordinary relation), 'S'
	 * (sequence), 'v' (view), or 'c' (composite type).
	 *
2445 2446
	 * 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
2447 2448
	 * composite type (pg_depend entries for columns of the composite type
	 * link to the pg_class entry not the pg_type entry).
2449
	 *
2450 2451 2452 2453
	 * Note: in this phase we should collect only a minimal amount of
	 * information about each table, basically just enough to decide if it is
	 * interesting. We must fetch all tables in this phase because otherwise
	 * we cannot correctly identify inherited columns, serial columns, etc.
2454
	 */
2455

2456
	if (g_fout->remoteVersion >= 80000)
2457
	{
2458 2459 2460 2461
		/*
		 * Left join to pick up dependency info linking sequences to their
		 * serial column, if any
		 */
2462
		appendPQExpBuffer(query,
2463 2464
						  "SELECT c.tableoid, c.oid, relname, "
						  "relacl, relkind, relnamespace, "
2465
						  "(%s relowner) as rolname, "
2466
						  "relchecks, reltriggers, "
2467 2468
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
2469 2470 2471 2472 2473
						  "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 "
2474
						  "d.classid = c.tableoid and d.objid = c.oid and "
2475
						  "d.objsubid = 0 and "
2476
						  "d.refclassid = c.tableoid and d.deptype = 'i') "
2477
						  "where relkind in ('%c', '%c', '%c', '%c') "
2478
						  "order by c.oid",
2479
						  username_subquery,
2480
						  RELKIND_SEQUENCE,
2481 2482
						  RELKIND_RELATION, RELKIND_SEQUENCE,
						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
2483 2484 2485 2486 2487 2488 2489 2490 2491 2492
	}
	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, "
2493
						  "(%s relowner) as rolname, "
2494 2495 2496 2497 2498
						  "relchecks, reltriggers, "
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
						  "d.refobjsubid as owning_col, "
						  "NULL as reltablespace "
2499 2500 2501
						  "from pg_class c "
						  "left join pg_depend d on "
						  "(c.relkind = '%c' and "
2502
						  "d.classid = c.tableoid and d.objid = c.oid and "
2503
						  "d.objsubid = 0 and "
2504
						  "d.refclassid = c.tableoid and d.deptype = 'i') "
2505
						  "where relkind in ('%c', '%c', '%c', '%c') "
2506
						  "order by c.oid",
2507
						  username_subquery,
2508
						  RELKIND_SEQUENCE,
2509 2510
						  RELKIND_RELATION, RELKIND_SEQUENCE,
						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
2511 2512 2513 2514
	}
	else if (g_fout->remoteVersion >= 70200)
	{
		appendPQExpBuffer(query,
2515
						  "SELECT tableoid, oid, relname, relacl, relkind, "
2516
						  "0::oid as relnamespace, "
2517
						  "(%s relowner) as rolname, "
2518
						  "relchecks, reltriggers, "
2519 2520
						  "relhasindex, relhasrules, relhasoids, "
						  "NULL::oid as owning_tab, "
2521 2522
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2523 2524 2525
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
2526 2527
						  username_subquery,
						  RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2528 2529 2530 2531 2532
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* all tables have oids in 7.1 */
		appendPQExpBuffer(query,
2533
						  "SELECT tableoid, oid, relname, relacl, relkind, "
2534
						  "0::oid as relnamespace, "
2535
						  "(%s relowner) as rolname, "
2536
						  "relchecks, reltriggers, "
2537 2538 2539
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2540 2541
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2542 2543 2544
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
2545 2546
						  username_subquery,
						  RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2547 2548 2549 2550
	}
	else
	{
		/*
2551 2552
		 * 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.
2553 2554
		 */
		appendPQExpBuffer(query,
2555
						  "SELECT "
2556
		"(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
2557
						  "oid, relname, relacl, "
2558
						  "CASE WHEN relhasrules and relkind = 'r' "
2559 2560
					  "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
					  "             r.ev_class = c.oid AND r.ev_type = '1') "
2561 2562 2563
						  "THEN '%c'::\"char\" "
						  "ELSE relkind END AS relkind,"
						  "0::oid as relnamespace, "
2564
						  "(%s relowner) as rolname, "
2565
						  "relchecks, reltriggers, "
2566 2567 2568
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2569 2570
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2571 2572 2573 2574
						  "from pg_class c "
						  "where relkind in ('%c', '%c') "
						  "order by oid",
						  RELKIND_VIEW,
2575
						  username_subquery,
2576 2577
						  RELKIND_RELATION, RELKIND_SEQUENCE);
	}
2578

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

2582
	ntups = PQntuples(res);
2583

2584
	*numTables = ntups;
2585

2586
	/*
2587 2588 2589
	 * 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.
2590
	 *
2591 2592 2593
	 * 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.
2594
	 */
2595
	tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
2596

2597
	i_reltableoid = PQfnumber(res, "tableoid");
2598 2599 2600 2601 2602
	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");
2603
	i_rolname = PQfnumber(res, "rolname");
2604 2605 2606 2607 2608
	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");
2609 2610
	i_owning_tab = PQfnumber(res, "owning_tab");
	i_owning_col = PQfnumber(res, "owning_col");
2611
	i_reltablespace = PQfnumber(res, "reltablespace");
2612

2613 2614
	for (i = 0; i < ntups; i++)
	{
2615 2616 2617 2618
		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);
2619 2620
		tblinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_relname));
		tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)),
2621
												  tblinfo[i].dobj.catId.oid);
2622
		tblinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
2623 2624 2625 2626 2627 2628 2629
		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));
2630 2631
		if (PQgetisnull(res, i, i_owning_tab))
		{
2632
			tblinfo[i].owning_tab = InvalidOid;
2633 2634 2635 2636
			tblinfo[i].owning_col = 0;
		}
		else
		{
2637
			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
2638 2639
			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
		}
2640
		tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
2641

2642
		/* other fields were zeroed above */
Bruce Momjian's avatar
Bruce Momjian committed
2643

2644
		/*
Bruce Momjian's avatar
Bruce Momjian committed
2645
		 * Decide whether we want to dump this table.  Sequences owned by
2646 2647
		 * serial columns are never dumpable on their own; we will transpose
		 * their owning table's dump flag to them below.
2648
		 */
2649 2650 2651
		if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
			tblinfo[i].dump = false;
		else if (OidIsValid(tblinfo[i].owning_tab))
2652
			tblinfo[i].dump = false;
2653 2654
		else
			selectDumpableTable(&tblinfo[i]);
2655
		tblinfo[i].interesting = tblinfo[i].dump;
2656

2657
		/*
2658 2659
		 * Read-lock target tables to make sure they aren't DROPPED or altered
		 * in schema before we get around to dumping them.
2660
		 *
2661 2662
		 * Note that we don't explicitly lock parents of the target tables; we
		 * assume our lock on the child is enough to prevent schema
2663 2664 2665 2666 2667 2668 2669 2670 2671 2672
		 * 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",
2673 2674
						 fmtQualifiedId(tblinfo[i].dobj.namespace->dobj.name,
										tblinfo[i].dobj.name));
2675
			do_sql_command(g_conn, lockquery->data);
2676
		}
2677

2678
		/* Emit notice if join for owner failed */
2679
		if (strlen(tblinfo[i].rolname) == 0)
2680
			write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
2681
					  tblinfo[i].dobj.name);
2682 2683
	}

Bruce Momjian's avatar
Bruce Momjian committed
2684
	/*
Bruce Momjian's avatar
Bruce Momjian committed
2685
	 * If the user is attempting to dump a specific table, check to ensure
2686 2687
	 * 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
2688 2689 2690 2691
	 */
	if (selectTableName)
	{
		for (i = 0; i < ntups; i++)
2692
			if (strcmp(tblinfo[i].dobj.name, selectTableName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
2693 2694 2695 2696 2697
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
2698
			write_msg(NULL, "specified table \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
2699 2700 2701 2702 2703
					  selectTableName);
			exit_nicely();
		}
	}

2704
	PQclear(res);
2705 2706
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
2707
	destroyPQExpBuffer(lockquery);
2708

2709
	return tblinfo;
2710 2711 2712 2713
}

/*
 * getInherits
2714
 *	  read all the inheritance information
2715 2716
 * from the system catalogs return them in the InhInfo* structure
 *
2717
 * numInherits is set to the number of pairs read in
2718
 */
2719
InhInfo *
2720 2721
getInherits(int *numInherits)
{
2722
	PGresult   *res;
2723 2724
	int			ntups;
	int			i;
2725 2726
	PQExpBuffer query = createPQExpBuffer();
	InhInfo    *inhinfo;
2727

2728
	int			i_inhrelid;
2729
	int			i_inhparent;
2730

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

2734 2735
	/* find all the inheritance information */

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

Bruce Momjian's avatar
Bruce Momjian committed
2738
	res = PQexec(g_conn, query->data);
2739
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2740 2741 2742 2743 2744 2745 2746

	ntups = PQntuples(res);

	*numInherits = ntups;

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

2747
	i_inhrelid = PQfnumber(res, "inhrelid");
2748 2749 2750 2751
	i_inhparent = PQfnumber(res, "inhparent");

	for (i = 0; i < ntups; i++)
	{
2752 2753
		inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
		inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
2754 2755 2756
	}

	PQclear(res);
2757 2758 2759

	destroyPQExpBuffer(query);

2760
	return inhinfo;
2761 2762 2763
}

/*
2764 2765
 * getIndexes
 *	  get information about every index on a dumpable table
2766
 *
2767 2768
 * Note: index data is not returned directly to the caller, but it
 * does get entered into the DumpableObject tables.
2769 2770
 */
void
2771
getIndexes(TableInfo tblinfo[], int numTables)
2772
{
2773
	int			i,
2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788
				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,
2789 2790
				i_conoid,
				i_tablespace;
2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805
	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",
2806
					  tbinfo->dobj.name);
2807 2808

		/* Make sure we are in proper schema so indexdef is right */
2809
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
2810 2811

		/*
2812 2813 2814 2815
		 * The point of the messy-looking outer join is to find a constraint
		 * that is related by an internal dependency link to 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.
2816 2817
		 */
		resetPQExpBuffer(query);
2818
		if (g_fout->remoteVersion >= 80000)
2819 2820 2821 2822
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
2823
					 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
2824 2825 2826 2827
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
2828
							  "c.oid as conoid, "
Bruce Momjian's avatar
Bruce Momjian committed
2829
							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace "
2830
							  "FROM pg_catalog.pg_index i "
2831
					  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847
							  "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, "
2848
					 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
2849 2850 2851 2852 2853 2854
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
							  "c.oid as conoid, "
							  "NULL as tablespace "
2855
							  "FROM pg_catalog.pg_index i "
2856
					  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872
							  "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, "
2873
							  "pg_get_indexdef(i.indexrelid) as indexdef, "
2874 2875 2876 2877 2878 2879
							  "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, "
2880 2881
							  "t.oid as conoid, "
							  "NULL as tablespace "
2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894
							  "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, "
2895
							  "pg_get_indexdef(i.indexrelid) as indexdef, "
2896 2897 2898 2899 2900 2901
							  "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, "
2902 2903
							  "t.oid as conoid, "
							  "NULL as tablespace "
2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 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);
		}

		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");
2927
		i_tablespace = PQfnumber(res, "tablespace");
2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939

		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);
2940 2941
			indxinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_indexname));
			indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
2942 2943 2944
			indxinfo[j].indextable = tbinfo;
			indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
			indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
2945
			indxinfo[j].tablespace = strdup(PQgetvalue(res, j, i_tablespace));
2946

2947 2948 2949
			/*
			 * In pre-7.4 releases, indkeys may contain more entries than
			 * indnkeys says (since indnkeys will be 1 for a functional
2950 2951 2952 2953 2954
			 * 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.
2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974
			 */
			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);
2975 2976
				constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
				constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
2977 2978 2979 2980 2981 2982 2983
				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;
2984

2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 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
				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",
3042
					  tbinfo->dobj.name);
3043 3044 3045 3046 3047

		/*
		 * select table schema to ensure constraint expr is qualified if
		 * needed
		 */
3048
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3049 3050 3051 3052

		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, conname, "
3053
						  "pg_catalog.pg_get_constraintdef(oid) as condef "
3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075
						  "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);
3076 3077
			constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
			constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115
			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;

	/*
3116 3117
	 * select appropriate schema to ensure names in constraint are properly
	 * qualified
3118
	 */
3119
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
3120 3121 3122 3123 3124

	query = createPQExpBuffer();

	if (g_fout->remoteVersion >= 70400)
		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
3125
						  "pg_catalog.pg_get_constraintdef(oid) AS consrc "
3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158
						  "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);
3159 3160
		constrinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		constrinfo[i].dobj.namespace = tinfo->dobj.namespace;
3161 3162 3163 3164 3165 3166 3167
		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
3168

3169
		/*
Bruce Momjian's avatar
Bruce Momjian committed
3170 3171
		 * Make the domain depend on the constraint, ensuring it won't be
		 * output till any constraint dependencies are OK.
3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 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
		 */
		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
3242
		Oid			ruletableoid;
3243 3244 3245 3246 3247

		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);
3248
		ruleinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_rulename));
3249 3250
		ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
		ruleinfo[i].ruletable = findTableByOid(ruletableoid);
3251 3252 3253 3254 3255 3256 3257
		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();
		}
3258
		ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
3259 3260 3261 3262 3263
		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)
		{
			/*
3264 3265 3266 3267
			 * 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.
3268 3269 3270
			 */
			if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
				ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
3271
			{
3272 3273
				addObjectDependency(&ruleinfo[i].ruletable->dobj,
									ruleinfo[i].dobj.dumpId);
3274 3275 3276
				/* We'll merge the rule into CREATE VIEW, if possible */
				ruleinfo[i].separate = false;
			}
3277
			else
3278
			{
3279 3280
				addObjectDependency(&ruleinfo[i].dobj,
									ruleinfo[i].ruletable->dobj.dumpId);
3281 3282
				ruleinfo[i].separate = true;
			}
3283
		}
3284 3285
		else
			ruleinfo[i].separate = true;
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320
	}

	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,
3321
				i_tgenabled,
3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334
				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",
3335
					  tbinfo->dobj.name);
3336 3337

		/*
3338
		 * select table schema to ensure regproc name is qualified if needed
3339
		 */
3340
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3341 3342 3343 3344 3345

		resetPQExpBuffer(query);
		if (g_fout->remoteVersion >= 70300)
		{
			/*
3346
			 * We ignore triggers that are tied to a foreign-key constraint
3347 3348 3349 3350
			 */
			appendPQExpBuffer(query,
							  "SELECT tgname, "
							  "tgfoid::pg_catalog.regproc as tgfname, "
3351
							  "tgtype, tgnargs, tgargs, tgenabled, "
3352 3353 3354
							  "tgisconstraint, tgconstrname, tgdeferrable, "
							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
					 "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366
							  "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,
3367
							  "SELECT tgname, tgfoid::regproc as tgfname, "
3368
							  "tgtype, tgnargs, tgargs, tgenabled, "
3369 3370 3371
							  "tgisconstraint, tgconstrname, tgdeferrable, "
							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
				  "(select relname from pg_class where oid = tgconstrrelid) "
3372 3373 3374 3375 3376 3377 3378 3379
							  "		as tgconstrrelname "
							  "from pg_trigger "
							  "where tgrelid = '%u'::oid",
							  tbinfo->dobj.catId.oid);
		}
		else
		{
			appendPQExpBuffer(query,
3380
							  "SELECT tgname, tgfoid::regproc as tgfname, "
3381
							  "tgtype, tgnargs, tgargs, tgenabled, "
3382
							  "tgisconstraint, tgconstrname, tgdeferrable, "
3383 3384 3385 3386
							  "tgconstrrelid, tginitdeferred, "
							  "(SELECT oid FROM pg_class WHERE relname = 'pg_trigger') AS tableoid, "

							  "oid, "
3387
				  "(select relname from pg_class where oid = tgconstrrelid) "
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404
							  "		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",
3405
					  tbinfo->ntrig, tbinfo->dobj.name, ntups);
3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418
			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");
3419
		i_tgenabled = PQfnumber(res, "tgenabled");
3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430
		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);
3431 3432
			tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
			tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
3433 3434 3435 3436 3437 3438
			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';
3439
			tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)) == 't';
3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451
			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",
3452
								  tginfo[j].dobj.name, tbinfo->dobj.name,
3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496
								  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;
3497 3498 3499
	int			i_lanvalidator;
	int			i_lanacl;
	int			i_lanowner;
3500 3501 3502 3503

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

3504
	if (g_fout->remoteVersion >= 80100)
3505
	{
3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526
		/* Languages are owned by the bootstrap superuser, OID 10 */
		appendPQExpBuffer(query, "SELECT tableoid, oid, *, "
						  "(%s '10') as lanowner "
						  "FROM pg_language "
						  "WHERE lanispl "
						  "ORDER BY oid",
						  username_subquery);
	}
	else if (g_fout->remoteVersion >= 70400)
	{
		/* Languages are owned by the bootstrap superuser, sysid 1 */
		appendPQExpBuffer(query, "SELECT tableoid, oid, *, "
						  "(%s '1') as lanowner "
						  "FROM pg_language "
						  "WHERE lanispl "
						  "ORDER BY oid",
						  username_subquery);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* No clear notion of an owner at all before 7.4 ... */
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
		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");
3554 3555 3556 3557
	/* these may fail and return -1: */
	i_lanvalidator = PQfnumber(res, "lanvalidator");
	i_lanacl = PQfnumber(res, "lanacl");
	i_lanowner = PQfnumber(res, "lanowner");
3558 3559 3560 3561 3562 3563 3564 3565

	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);

3566
		planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
3567 3568
		planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
		planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
3569
		if (i_lanvalidator >= 0)
3570 3571 3572
			planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
		else
			planginfo[i].lanvalidator = InvalidOid;
3573 3574 3575
		if (i_lanacl >= 0)
			planginfo[i].lanacl = strdup(PQgetvalue(res, i, i_lanacl));
		else
3576
			planginfo[i].lanacl = strdup("{=U}");
3577 3578 3579 3580
		if (i_lanowner >= 0)
			planginfo[i].lanowner = strdup(PQgetvalue(res, i, i_lanowner));
		else
			planginfo[i].lanowner = strdup("");
Bruce Momjian's avatar
Bruce Momjian committed
3581

3582 3583
		if (g_fout->remoteVersion < 70300)
		{
3584
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3585 3586
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
3587 3588
			 * mechanism will handle this for us.)
			 */
3589 3590
			FuncInfo   *funcInfo = findFuncByOid(planginfo[i].lanplcallfoid);

3591 3592 3593 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
			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 "
3642
						  "p.prorettype = t2.oid AND p.proname = t2.typname "
3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663
						  "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
3664
		PQExpBufferData namebuf;
3665 3666 3667
		TypeInfo   *sTypeInfo;
		TypeInfo   *tTypeInfo;

3668 3669 3670 3671 3672 3673 3674 3675 3676
		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));

3677
		/*
3678 3679 3680
		 * 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.
3681 3682 3683 3684 3685 3686 3687 3688 3689
		 */
		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;

3690 3691 3692
		if (OidIsValid(castinfo[i].castfunc))
		{
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3693 3694
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
3695 3696 3697 3698 3699 3700 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
			 * 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;
3740
	int			i_attnotnull;
3741
	int			i_atthasdef;
3742
	int			i_attisdropped;
3743
	int			i_attislocal;
3744
	PGresult   *res;
3745
	int			ntups;
3746
	bool		hasdefaults;
3747 3748 3749

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

3752
		/* Don't bother to collect info for sequences */
3753
		if (tbinfo->relkind == RELKIND_SEQUENCE)
3754 3755
			continue;

3756
		/* Don't bother with uninteresting tables, either */
3757
		if (!tbinfo->interesting)
3758 3759 3760 3761 3762 3763
			continue;

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

3766 3767 3768
		/* find all the user attributes and their types */

		/*
3769 3770 3771 3772 3773
		 * we must read the attribute names in attribute number order! because
		 * we will use the attnum to index into the attnames array 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.
3774 3775
		 */
		if (g_verbose)
3776
			write_msg(NULL, "finding the columns and types of table \"%s\"\n",
3777
					  tbinfo->dobj.name);
3778

Bruce Momjian's avatar
Bruce Momjian committed
3779
		resetPQExpBuffer(q);
3780

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

Bruce Momjian's avatar
Bruce Momjian committed
3824
		res = PQexec(g_conn, q->data);
3825
		check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
3826 3827 3828

		ntups = PQntuples(res);

3829
		i_attnum = PQfnumber(res, "attnum");
3830
		i_attname = PQfnumber(res, "attname");
3831
		i_atttypname = PQfnumber(res, "atttypname");
3832
		i_atttypmod = PQfnumber(res, "atttypmod");
3833
		i_attstattarget = PQfnumber(res, "attstattarget");
3834 3835
		i_attstorage = PQfnumber(res, "attstorage");
		i_typstorage = PQfnumber(res, "typstorage");
3836
		i_attnotnull = PQfnumber(res, "attnotnull");
3837
		i_atthasdef = PQfnumber(res, "atthasdef");
3838
		i_attisdropped = PQfnumber(res, "attisdropped");
3839
		i_attislocal = PQfnumber(res, "attislocal");
3840

3841 3842 3843 3844 3845
		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));
3846 3847
		tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
		tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
3848
		tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
3849
		tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
3850 3851
		tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
3852
		tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
3853 3854 3855
		tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
3856 3857
		hasdefaults = false;

3858 3859
		for (j = 0; j < ntups; j++)
		{
Bruce Momjian's avatar
Bruce Momjian committed
3860
			if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
3861
			{
3862
				write_msg(NULL, "invalid column numbering in table \"%s\"\n",
3863
						  tbinfo->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
3864
				exit_nicely();
3865
			}
3866 3867 3868 3869
			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));
3870 3871
			tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
			tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
3872
			tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
3873
			tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3874
			tbinfo->attisserial[j] = false;		/* fix below */
3875
			tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3876
			tbinfo->attrdefs[j] = NULL; /* fix below */
3877
			if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
3878 3879
				hasdefaults = true;
			/* these flags will be set in flagInhAttrs() */
3880 3881 3882
			tbinfo->inhAttrs[j] = false;
			tbinfo->inhAttrDef[j] = false;
			tbinfo->inhNotNull[j] = false;
3883
		}
3884

3885
		PQclear(res);
3886

3887 3888 3889
		/*
		 * Get info about column defaults
		 */
3890 3891
		if (hasdefaults)
		{
3892
			AttrDefInfo *attrdefs;
3893 3894 3895
			int			numDefaults;

			if (g_verbose)
3896
				write_msg(NULL, "finding default expressions of table \"%s\"\n",
3897
						  tbinfo->dobj.name);
3898 3899

			resetPQExpBuffer(q);
3900 3901
			if (g_fout->remoteVersion >= 70300)
			{
3902
				appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
3903
						   "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
3904 3905 3906 3907 3908 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
								  "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
3943
				int			adnum;
3944 3945 3946 3947 3948 3949 3950 3951 3952

				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));

3953 3954 3955
				attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
				attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;

3956
				/*
3957 3958 3959 3960 3961
				 * Defaults on a VIEW must always be dumped as separate ALTER
				 * TABLE commands.	Defaults on regular tables are 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.
3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979
				 */
				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",
3980
							  adnum, tbinfo->dobj.name);
3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997
					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",
3998
						  tbinfo->dobj.name);
3999 4000 4001 4002 4003

			resetPQExpBuffer(q);
			if (g_fout->remoteVersion >= 70400)
			{
				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
4004
							"pg_catalog.pg_get_constraintdef(oid) AS consrc "
4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020
								  "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);
4021 4022
			}
			else if (g_fout->remoteVersion >= 70200)
4023
			{
4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041
				/* 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);
4042 4043 4044
			}
			else
			{
4045 4046 4047 4048 4049 4050 4051 4052 4053
				/* 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);
4054 4055
			}
			res = PQexec(g_conn, q->data);
4056 4057 4058 4059
			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);

			numConstrs = PQntuples(res);
			if (numConstrs != tbinfo->ncheck)
4060
			{
4061
				write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
4062
						  tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
4063
				write_msg(NULL, "(The system catalogs might be corrupted.)\n");
4064 4065
				exit_nicely();
			}
4066

4067 4068
			constrs = (ConstraintInfo *) malloc(numConstrs * sizeof(ConstraintInfo));
			tbinfo->checkexprs = constrs;
4069

4070 4071 4072 4073 4074 4075
			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);
4076 4077
				constrs[j].dobj.name = strdup(PQgetvalue(res, j, 2));
				constrs[j].dobj.namespace = tbinfo->dobj.namespace;
4078 4079 4080 4081 4082 4083 4084
				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;
4085

4086
				/*
4087 4088 4089 4090
				 * 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.
4091
				 */
4092 4093
				addObjectDependency(&tbinfo->dobj,
									constrs[j].dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
4094

4095 4096 4097 4098 4099
				/*
				 * 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.
				 */
4100
			}
4101
			PQclear(res);
4102
		}
4103 4104

		/*
4105 4106 4107 4108
		 * 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.
4109 4110 4111 4112 4113
		 */
		for (j = 0; j < ntups; j++)
		{
			/*
			 * Note assumption that format_type will show these types as
4114 4115
			 * exactly "integer" and "bigint" regardless of schema path. This
			 * is correct in 7.3 but needs to be watched.
4116 4117 4118 4119
			 */
			if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
				strcmp(tbinfo->atttypnames[j], "bigint") != 0)
				continue;
4120
			if (tbinfo->attrdefs[j] == NULL)
4121 4122 4123
				continue;
			for (k = 0; k < numTables; k++)
			{
Bruce Momjian's avatar
Bruce Momjian committed
4124
				TableInfo  *seqinfo = &tblinfo[k];
4125

4126 4127
				if (OidIsValid(seqinfo->owning_tab) &&
					seqinfo->owning_tab == tbinfo->dobj.catId.oid &&
Bruce Momjian's avatar
Bruce Momjian committed
4128
					seqinfo->owning_col == j + 1)
4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140
				{
					/*
					 * 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;
				}
			}
		}
4141
	}
4142 4143

	destroyPQExpBuffer(q);
4144 4145 4146 4147
}


/*
4148
 * dumpComment --
Bruce Momjian's avatar
Bruce Momjian committed
4149
 *
4150
 * This routine is used to dump any comments associated with the
4151
 * object handed to this routine. The routine takes a constant character
4152
 * string for the target part of the comment-creation command, plus
4153
 * the namespace and owner of the object (for labeling the ArchiveEntry),
4154 4155
 * plus catalog ID and subid which are the lookup key for pg_description,
 * plus the dump ID for the object (for setting a dependency).
4156
 * If a matching pg_description entry is found, it is dumped.
4157 4158 4159 4160 4161 4162 4163
 *
 * 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.
4164
 */
4165
static void
4166 4167
dumpComment(Archive *fout, const char *target,
			const char *namespace, const char *owner,
4168
			CatalogId catalogId, int subid, DumpId dumpId)
4169
{
4170 4171
	CommentItem *comments;
	int			ncomments;
Bruce Momjian's avatar
Bruce Momjian committed
4172

4173 4174 4175 4176
	/* Comments are SCHEMA not data */
	if (dataOnly)
		return;

4177 4178 4179
	/* Search for comments associated with catalogId, using table */
	ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
							 &comments);
4180

4181 4182
	/* Is there one matching the subid? */
	while (ncomments > 0)
4183
	{
4184 4185 4186 4187
		if (comments->objsubid == subid)
			break;
		comments++;
		ncomments--;
4188
	}
Bruce Momjian's avatar
Bruce Momjian committed
4189

Bruce Momjian's avatar
Bruce Momjian committed
4190
	/* If a comment exists, build COMMENT ON statement */
4191
	if (ncomments > 0)
4192
	{
4193 4194
		PQExpBuffer query = createPQExpBuffer();

4195
		appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
4196
		appendStringLiteral(query, comments->descr, false);
4197
		appendPQExpBuffer(query, ";\n");
4198

4199
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
4200
					 target, namespace, NULL, owner, false,
4201 4202 4203
					 "COMMENT", query->data, "", NULL,
					 &(dumpId), 1,
					 NULL, NULL);
4204

4205 4206
		destroyPQExpBuffer(query);
	}
4207 4208 4209 4210 4211
}

/*
 * dumpTableComment --
 *
4212
 * As above, but dump comments for both the specified table (or view)
4213
 * and its columns.
4214 4215 4216
 */
static void
dumpTableComment(Archive *fout, TableInfo *tbinfo,
4217
				 const char *reltypename)
4218
{
4219 4220
	CommentItem *comments;
	int			ncomments;
4221 4222 4223 4224 4225 4226 4227
	PQExpBuffer query;
	PQExpBuffer target;

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

4228 4229 4230 4231 4232
	/* Search for comments associated with relation, using table */
	ncomments = findComments(fout,
							 tbinfo->dobj.catId.tableoid,
							 tbinfo->dobj.catId.oid,
							 &comments);
4233

4234 4235 4236
	/* If comments exist, build COMMENT ON statements */
	if (ncomments <= 0)
		return;
4237 4238 4239 4240

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

4241
	while (ncomments > 0)
4242
	{
4243 4244
		const char *descr = comments->descr;
		int			objsubid = comments->objsubid;
4245 4246 4247 4248 4249

		if (objsubid == 0)
		{
			resetPQExpBuffer(target);
			appendPQExpBuffer(target, "%s %s", reltypename,
4250
							  fmtId(tbinfo->dobj.name));
4251 4252 4253

			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4254
			appendStringLiteral(query, descr, false);
4255 4256
			appendPQExpBuffer(query, ";\n");

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

4274 4275
			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4276
			appendStringLiteral(query, descr, false);
4277 4278
			appendPQExpBuffer(query, ";\n");

4279 4280
			ArchiveEntry(fout, nilCatalogId, createDumpId(),
						 target->data,
4281 4282
						 tbinfo->dobj.namespace->dobj.name,
						 NULL,
4283
						 tbinfo->rolname,
4284
						 false, "COMMENT", query->data, "", NULL,
4285 4286
						 &(tbinfo->dobj.dumpId), 1,
						 NULL, NULL);
4287
		}
4288 4289 4290

		comments++;
		ncomments--;
4291
	}
Bruce Momjian's avatar
Bruce Momjian committed
4292

4293
	destroyPQExpBuffer(query);
4294
	destroyPQExpBuffer(target);
Bruce Momjian's avatar
Bruce Momjian committed
4295 4296
}

4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321
/*
 * 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);

	/*
4322 4323 4324
	 * 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.
4325 4326 4327 4328 4329 4330 4331 4332
	 */
	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
4333
	high = &comments[ncomments - 1];
4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357
	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
4358 4359
	 * invariant still holds: only items between low and high inclusive could
	 * match.
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
	 */
	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;
}

4465
/*
4466
 * dumpDumpableObject
Bruce Momjian's avatar
Bruce Momjian committed
4467
 *
4468 4469
 * This routine and its subsidiaries are responsible for creating
 * ArchiveEntries (TOC objects) for each object to be dumped.
4470
 */
4471 4472
static void
dumpDumpableObject(Archive *fout, DumpableObject *dobj)
4473
{
4474
	switch (dobj->objType)
4475
	{
4476 4477 4478 4479 4480 4481 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
		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;
4527 4528 4529 4530 4531
		case DO_TABLE_TYPE:
			/* table rowtypes are never dumped separately */
			break;
		case DO_BLOBS:
			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
4532
						 dobj->name, NULL, NULL, "",
4533
						 false, "BLOBS", "", "", NULL,
4534 4535 4536
						 NULL, 0,
						 dumpBlobs, NULL);
			break;
Tom Lane's avatar
Tom Lane committed
4537 4538 4539 4540 4541 4542 4543
		case DO_BLOB_COMMENTS:
			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
						 dobj->name, NULL, NULL, "",
						 false, "BLOB COMMENTS", "", "", NULL,
						 NULL, 0,
						 dumpBlobComments, NULL);
			break;
4544 4545 4546 4547
	}
}

/*
4548 4549
 * dumpNamespace
 *	  writes out to fout the queries to recreate a user-defined namespace
4550
 */
4551 4552
static void
dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
4553
{
4554 4555
	PQExpBuffer q;
	PQExpBuffer delq;
4556
	char	   *qnspname;
4557

4558 4559 4560
	/* skip if not to be dumped */
	if (!nspinfo->dump || dataOnly)
		return;
4561

4562
	/* don't dump dummy namespace from pre-7.3 source */
4563
	if (strlen(nspinfo->dobj.name) == 0)
4564
		return;
4565

4566 4567
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
4568

4569
	qnspname = strdup(fmtId(nspinfo->dobj.name));
4570

4571
	appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
4572

4573
	appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
4574

4575 4576
	ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
				 nspinfo->dobj.name,
4577
				 NULL, NULL,
4578
				 nspinfo->rolname,
4579 4580 4581
				 false, "SCHEMA", q->data, delq->data, NULL,
				 nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
				 NULL, NULL);
4582

4583 4584 4585 4586
	/* Dump Schema Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "SCHEMA %s", qnspname);
	dumpComment(fout, q->data,
4587
				NULL, nspinfo->rolname,
4588
				nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
4589

4590
	dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
4591
			qnspname, nspinfo->dobj.name, NULL,
4592
			nspinfo->rolname, nspinfo->nspacl);
4593

4594
	free(qnspname);
4595 4596 4597 4598 4599 4600

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

/*
4601 4602 4603 4604 4605 4606 4607
 * 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 */
4608
	if (!tinfo->dobj.namespace->dump || dataOnly)
4609 4610 4611
		return;

	/* skip complex types, except for standalone composite types */
4612
	/* (note: this test should now be unnecessary) */
4613 4614
	if (OidIsValid(tinfo->typrelid) &&
		tinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
4615 4616 4617 4618 4619 4620 4621
		return;

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

	/* skip all array types that start w/ underscore */
4622
	if ((tinfo->dobj.name[0] == '_') &&
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636
		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
4637
 *	  writes out to fout the queries to recreate a user-defined base type
4638 4639
 */
static void
4640
dumpBaseType(Archive *fout, TypeInfo *tinfo)
4641 4642 4643 4644 4645 4646 4647 4648 4649
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
	char	   *typlen;
	char	   *typinput;
	char	   *typoutput;
4650 4651
	char	   *typreceive;
	char	   *typsend;
4652
	char	   *typanalyze;
4653 4654 4655 4656
	Oid			typinputoid;
	Oid			typoutputoid;
	Oid			typreceiveoid;
	Oid			typsendoid;
4657
	Oid			typanalyzeoid;
4658 4659 4660 4661 4662 4663 4664
	char	   *typdelim;
	char	   *typdefault;
	char	   *typbyval;
	char	   *typalign;
	char	   *typstorage;

	/* Set proper schema search path so regproc references list correctly */
4665
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4666 4667

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

	res = PQexec(g_conn, query->data);
4754
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767

	/* 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"));
4768 4769
	typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
	typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
4770
	typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
4771 4772 4773 4774
	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")));
4775
	typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
4776 4777 4778 4779
	typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4780
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4781 4782 4783 4784
	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
4785
	/*
4786
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
4787
	 */
4788
	appendPQExpBuffer(delq, "DROP TYPE %s.",
4789
					  fmtId(tinfo->dobj.namespace->dobj.name));
4790
	appendPQExpBuffer(delq, "%s CASCADE;\n",
4791
					  fmtId(tinfo->dobj.name));
4792 4793

	appendPQExpBuffer(q,
4794
					  "CREATE TYPE %s (\n"
4795
					  "    INTERNALLENGTH = %s",
4796
					  fmtId(tinfo->dobj.name),
4797
					  (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
4798 4799 4800

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

4820 4821
	if (typdefault != NULL)
	{
4822 4823
		appendPQExpBuffer(q, ",\n    DEFAULT = ");
		appendStringLiteral(q, typdefault, true);
4824 4825 4826 4827 4828 4829 4830
	}

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

		/* reselect schema in case changed by function dump */
4831
		selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4832
		elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque);
4833
		appendPQExpBuffer(q, ",\n    ELEMENT = %s", elemType);
4834 4835 4836
		free(elemType);
	}

4837 4838 4839 4840 4841 4842
	if (typdelim && strcmp(typdelim, ",") != 0)
	{
		appendPQExpBuffer(q, ",\n    DELIMITER = ");
		appendStringLiteral(q, typdelim, true);
	}

4843
	if (strcmp(typalign, "c") == 0)
4844
		appendPQExpBuffer(q, ",\n    ALIGNMENT = char");
4845
	else if (strcmp(typalign, "s") == 0)
4846
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int2");
4847
	else if (strcmp(typalign, "i") == 0)
4848
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int4");
4849
	else if (strcmp(typalign, "d") == 0)
4850
		appendPQExpBuffer(q, ",\n    ALIGNMENT = double");
4851 4852

	if (strcmp(typstorage, "p") == 0)
4853
		appendPQExpBuffer(q, ",\n    STORAGE = plain");
4854
	else if (strcmp(typstorage, "e") == 0)
4855
		appendPQExpBuffer(q, ",\n    STORAGE = external");
4856
	else if (strcmp(typstorage, "x") == 0)
4857
		appendPQExpBuffer(q, ",\n    STORAGE = extended");
4858
	else if (strcmp(typstorage, "m") == 0)
4859
		appendPQExpBuffer(q, ",\n    STORAGE = main");
4860 4861

	if (strcmp(typbyval, "t") == 0)
4862 4863 4864
		appendPQExpBuffer(q, ",\n    PASSEDBYVALUE");

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

4866
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4867 4868
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4869
				 NULL,
4870
				 tinfo->rolname, false,
4871 4872 4873
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4874

Bruce Momjian's avatar
Bruce Momjian committed
4875
	/* Dump Type Comments */
4876 4877
	resetPQExpBuffer(q);

4878
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
4879
	dumpComment(fout, q->data,
4880
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
4881
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
4882 4883

	PQclear(res);
4884 4885
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
4886
	destroyPQExpBuffer(query);
Bruce Momjian's avatar
Bruce Momjian committed
4887 4888
}

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

4906
	/* Set proper schema search path so type references list correctly */
4907
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4908

4909
	/* Fetch domain specific details */
4910
	/* We assume here that remoteVersion must be at least 70300 */
4911
	appendPQExpBuffer(query, "SELECT typnotnull, "
4912
				"pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
4913
					  "typdefault "
4914
					  "FROM pg_catalog.pg_type "
4915 4916
					  "WHERE oid = '%u'::pg_catalog.oid",
					  tinfo->dobj.catId.oid);
4917 4918

	res = PQexec(g_conn, query->data);
4919
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4920 4921 4922 4923

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

4930 4931 4932 4933 4934
	typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
	typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4935
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4936 4937 4938

	appendPQExpBuffer(q,
					  "CREATE DOMAIN %s AS %s",
4939
					  fmtId(tinfo->dobj.name),
4940
					  typdefn);
4941

4942
	if (typnotnull[0] == 't')
4943 4944
		appendPQExpBuffer(q, " NOT NULL");

4945
	if (typdefault)
4946
		appendPQExpBuffer(q, " DEFAULT %s", typdefault);
4947

4948 4949 4950
	PQclear(res);

	/*
4951
	 * Add any CHECK constraints for the domain
4952
	 */
4953
	for (i = 0; i < tinfo->nDomChecks; i++)
4954
	{
4955
		ConstraintInfo *domcheck = &(tinfo->domChecks[i]);
4956

4957 4958
		if (!domcheck->separate)
			appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
4959
							  fmtId(domcheck->dobj.name), domcheck->condef);
4960
	}
Bruce Momjian's avatar
Bruce Momjian committed
4961

4962 4963
	appendPQExpBuffer(q, ";\n");

4964
	/*
4965
	 * DROP must be fully qualified in case same name appears in pg_catalog
4966 4967
	 */
	appendPQExpBuffer(delq, "DROP DOMAIN %s.",
4968
					  fmtId(tinfo->dobj.namespace->dobj.name));
4969
	appendPQExpBuffer(delq, "%s;\n",
4970
					  fmtId(tinfo->dobj.name));
4971

4972
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4973 4974
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4975
				 NULL,
4976
				 tinfo->rolname, false,
4977 4978 4979
				 "DOMAIN", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4980

Bruce Momjian's avatar
Bruce Momjian committed
4981
	/* Dump Domain Comments */
4982 4983
	resetPQExpBuffer(q);

4984
	appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->dobj.name));
4985
	dumpComment(fout, q->data,
4986
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
4987
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
4988 4989 4990 4991

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
4992 4993
}

Bruce Momjian's avatar
Bruce Momjian committed
4994
/*
4995
 * dumpCompositeType
Bruce Momjian's avatar
Bruce Momjian committed
4996
 *	  writes out to fout the queries to recreate a user-defined stand-alone
4997
 *	  composite type
Bruce Momjian's avatar
Bruce Momjian committed
4998 4999
 */
static void
5000
dumpCompositeType(Archive *fout, TypeInfo *tinfo)
Bruce Momjian's avatar
Bruce Momjian committed
5001 5002 5003 5004 5005 5006
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
Bruce Momjian's avatar
Bruce Momjian committed
5007 5008
	int			i_attname;
	int			i_atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
5009 5010 5011
	int			i;

	/* Set proper schema search path so type references list correctly */
5012
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
5013 5014 5015 5016 5017

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

	appendPQExpBuffer(query, "SELECT a.attname, "
5018 5019
			 "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
					  "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
5020
					  "WHERE t.oid = '%u'::pg_catalog.oid "
5021 5022 5023
					  "AND a.attrelid = t.typrelid "
					  "AND NOT a.attisdropped "
					  "ORDER BY a.attnum ",
5024
					  tinfo->dobj.catId.oid);
Bruce Momjian's avatar
Bruce Momjian committed
5025 5026

	res = PQexec(g_conn, query->data);
5027
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
Bruce Momjian's avatar
Bruce Momjian committed
5028 5029 5030 5031 5032

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

5037 5038
	i_attname = PQfnumber(res, "attname");
	i_atttypdefn = PQfnumber(res, "atttypdefn");
Bruce Momjian's avatar
Bruce Momjian committed
5039

5040
	appendPQExpBuffer(q, "CREATE TYPE %s AS (",
5041
					  fmtId(tinfo->dobj.name));
Bruce Momjian's avatar
Bruce Momjian committed
5042 5043 5044

	for (i = 0; i < ntups; i++)
	{
5045 5046
		char	   *attname;
		char	   *atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
5047

5048 5049
		attname = PQgetvalue(res, i, i_attname);
		atttypdefn = PQgetvalue(res, i, i_atttypdefn);
Bruce Momjian's avatar
Bruce Momjian committed
5050

5051 5052 5053
		appendPQExpBuffer(q, "\n\t%s %s", fmtId(attname), atttypdefn);
		if (i < ntups - 1)
			appendPQExpBuffer(q, ",");
Bruce Momjian's avatar
Bruce Momjian committed
5054
	}
5055
	appendPQExpBuffer(q, "\n);\n");
Bruce Momjian's avatar
Bruce Momjian committed
5056

Bruce Momjian's avatar
Bruce Momjian committed
5057
	/*
5058
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
5059
	 */
5060
	appendPQExpBuffer(delq, "DROP TYPE %s.",
5061
					  fmtId(tinfo->dobj.namespace->dobj.name));
5062
	appendPQExpBuffer(delq, "%s;\n",
5063
					  fmtId(tinfo->dobj.name));
5064

5065
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
5066 5067
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
5068
				 NULL,
5069
				 tinfo->rolname, false,
5070 5071 5072
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
5073

5074

5075 5076
	/* Dump Type Comments */
	resetPQExpBuffer(q);
5077

5078
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
5079
	dumpComment(fout, q->data,
5080
				tinfo->dobj.namespace->dobj.name, tinfo->rolname,
5081
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
5082

5083 5084 5085 5086 5087
	PQclear(res);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
}
5088

5089 5090 5091 5092 5093 5094 5095 5096 5097 5098
/*
 * Determine whether we want to dump definitions for procedural languages.
 * Since the languages themselves don't have schemas, we can't rely on
 * the normal schema-based selection mechanism.  We choose to dump them
 * whenever neither --schema nor --table was given.  (Before 8.1, we used
 * the dump flag of the PL's call handler function, but in 8.1 this will
 * probably always be false since call handlers are created in pg_catalog.)
 *
 * For some backwards compatibility with the older behavior, we forcibly
 * dump a PL if its handler function (and validator if any) are in a
5099
 * dumpable namespace.	That case is not checked here.
5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111
 */
static bool
shouldDumpProcLangs(void)
{
	if (selectTableName != NULL || selectSchemaName != NULL)
		return false;
	/* And they're schema not data */
	if (dataOnly)
		return false;
	return true;
}

5112 5113 5114 5115 5116 5117 5118 5119 5120 5121
/*
 * 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;
5122
	bool		useParams;
5123
	char	   *qlanname;
5124
	char	   *lanschema;
5125 5126
	FuncInfo   *funcInfo;
	FuncInfo   *validatorInfo = NULL;
5127

5128 5129
	if (dataOnly)
		return;
5130

5131
	/*
5132 5133 5134 5135 5136
	 * Try to find the support function(s).  It is not an error if we don't
	 * find them --- if the functions are in the pg_catalog schema, as is
	 * standard in 8.1 and up, then we won't have loaded them. (In this case
	 * we will emit a parameterless CREATE LANGUAGE command, which will
	 * require PL template knowledge in the backend to reload.)
5137
	 */
5138

5139
	funcInfo = findFuncByOid(plang->lanplcallfoid);
5140 5141
	if (funcInfo != NULL && !funcInfo->dobj.namespace->dump)
		funcInfo = NULL;		/* treat not-dumped same as not-found */
5142

5143 5144 5145
	if (OidIsValid(plang->lanvalidator))
	{
		validatorInfo = findFuncByOid(plang->lanvalidator);
5146 5147
		if (validatorInfo != NULL && !validatorInfo->dobj.namespace->dump)
			validatorInfo = NULL;
5148
	}
5149

5150 5151
	/*
	 * If the functions are dumpable then emit a traditional CREATE LANGUAGE
5152 5153
	 * with parameters.  Otherwise, dump only if shouldDumpProcLangs() says to
	 * dump it.
5154 5155 5156 5157 5158
	 */
	useParams = (funcInfo != NULL &&
				 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));

	if (!useParams && !shouldDumpProcLangs())
5159 5160
		return;

5161 5162
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
5163

5164
	qlanname = strdup(fmtId(plang->dobj.name));
5165

5166
	/*
5167 5168 5169
	 * If dumping a HANDLER clause, treat the language as being in the handler
	 * function's schema; this avoids cluttering the HANDLER clause. Otherwise
	 * it doesn't really have a schema.
5170 5171 5172 5173 5174 5175
	 */
	if (useParams)
		lanschema = funcInfo->dobj.namespace->dobj.name;
	else
		lanschema = NULL;

5176 5177
	appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
					  qlanname);
5178

5179
	appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
5180
					  (useParams && plang->lanpltrusted) ? "TRUSTED " : "",
5181
					  qlanname);
5182
	if (useParams)
5183
	{
5184 5185 5186 5187 5188 5189 5190 5191
		appendPQExpBuffer(defqry, " HANDLER %s",
						  fmtId(funcInfo->dobj.name));
		if (OidIsValid(plang->lanvalidator))
		{
			appendPQExpBuffer(defqry, " VALIDATOR ");
			/* Cope with possibility that validator is in different schema */
			if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
				appendPQExpBuffer(defqry, "%s.",
5192
							fmtId(validatorInfo->dobj.namespace->dobj.name));
5193 5194 5195
			appendPQExpBuffer(defqry, "%s",
							  fmtId(validatorInfo->dobj.name));
		}
5196
	}
5197
	appendPQExpBuffer(defqry, ";\n");
5198

5199
	ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
5200
				 plang->dobj.name,
5201
				 lanschema, NULL, plang->lanowner,
5202
				 false, "PROCEDURAL LANGUAGE",
5203 5204 5205
				 defqry->data, delqry->data, NULL,
				 plang->dobj.dependencies, plang->dobj.nDeps,
				 NULL, NULL);
5206

5207 5208 5209 5210 5211 5212
	/* Dump Proc Lang Comments */
	resetPQExpBuffer(defqry);
	appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
	dumpComment(fout, defqry->data,
				NULL, "",
				plang->dobj.catId, 0, plang->dobj.dumpId);
5213

5214 5215
	if (plang->lanpltrusted)
		dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
5216
				qlanname, plang->dobj.name,
5217
				lanschema,
5218
				plang->lanowner, plang->lanacl);
5219

5220 5221 5222 5223
	free(qlanname);

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5224 5225
}

5226
/*
5227
 * format_function_arguments: generate function name and argument list
5228 5229 5230
 *
 * The argument type names are qualified if needed.  The function name
 * is never qualified.
5231
 *
5232
 * Any or all of allargtypes, argmodes, argnames may be NULL.
5233
 */
5234
static char *
5235 5236 5237 5238
format_function_arguments(FuncInfo *finfo, int nallargs,
						  char **allargtypes,
						  char **argmodes,
						  char **argnames)
5239 5240 5241 5242 5243
{
	PQExpBufferData fn;
	int			j;

	initPQExpBuffer(&fn);
5244 5245
	appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
	for (j = 0; j < nallargs; j++)
5246
	{
5247
		Oid			typid;
5248
		char	   *typname;
5249 5250
		const char *argmode;
		const char *argname;
5251

5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275
		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 = "";
5276 5277 5278 5279 5280

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

5281
		appendPQExpBuffer(&fn, "%s%s%s%s%s",
5282
						  (j > 0) ? ", " : "",
5283
						  argmode,
5284 5285
						  argname ? fmtId(argname) : "",
						  argname ? " " : "",
5286 5287 5288 5289 5290 5291 5292
						  typname);
		free(typname);
	}
	appendPQExpBuffer(&fn, ")");
	return fn.data;
}

5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328
/*
 * 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;
}

5329

5330
/*
5331 5332
 * dumpFunc:
 *	  dump out one function
5333
 */
5334
static void
5335
dumpFunc(Archive *fout, FuncInfo *finfo)
5336
{
5337 5338 5339 5340 5341 5342 5343
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delqry;
	PQExpBuffer asPart;
	PGresult   *res;
	char	   *funcsig;
	char	   *funcsig_tag;
5344 5345 5346 5347
	int			ntups;
	char	   *proretset;
	char	   *prosrc;
	char	   *probin;
5348 5349
	char	   *proallargtypes;
	char	   *proargmodes;
5350
	char	   *proargnames;
5351 5352
	char	   *provolatile;
	char	   *proisstrict;
5353
	char	   *prosecdef;
5354
	char	   *lanname;
5355
	char	   *rettypename;
5356 5357 5358 5359
	int			nallargs;
	char	  **allargtypes = NULL;
	char	  **argmodes = NULL;
	char	  **argnames = NULL;
5360

5361 5362
	/* Dump only funcs in dumpable namespaces */
	if (!finfo->dobj.namespace->dump || dataOnly)
5363
		return;
5364

5365 5366 5367 5368
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delqry = createPQExpBuffer();
	asPart = createPQExpBuffer();
5369

5370
	/* Set proper schema search path so type references list correctly */
5371
	selectSourceSchema(finfo->dobj.namespace->dobj.name);
5372 5373

	/* Fetch function-specific details */
5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385
	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)
5386 5387 5388
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5389 5390
						  "null as proallargtypes, "
						  "null as proargmodes, "
5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401
						  "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, "
5402 5403 5404
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
5405
						  "provolatile, proisstrict, prosecdef, "
5406
						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
5407
						  "FROM pg_catalog.pg_proc "
5408 5409
						  "WHERE oid = '%u'::pg_catalog.oid",
						  finfo->dobj.catId.oid);
5410 5411 5412 5413 5414
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5415 5416 5417
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
5418
			 "case when proiscachable then 'i' else 'v' end as provolatile, "
5419
						  "proisstrict, "
5420
						  "'f'::boolean as prosecdef, "
5421
		  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5422
						  "FROM pg_proc "
5423 5424
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5425 5426 5427 5428 5429
	}
	else
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5430 5431 5432
						  "null as proallargtypes, "
						  "null as proargmodes, "
						  "null as proargnames, "
5433
			 "case when proiscachable then 'i' else 'v' end as provolatile, "
5434
						  "'f'::boolean as proisstrict, "
5435
						  "'f'::boolean as prosecdef, "
5436
		  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5437
						  "FROM pg_proc "
5438 5439
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5440
	}
5441

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

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

5454 5455 5456
	proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
	prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
	probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
5457 5458
	proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
	proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
5459
	proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
5460 5461
	provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
	proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
5462
	prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
5463
	lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
5464

5465
	/*
5466 5467
	 * See backend/commands/define.c for details of how the 'AS' clause is
	 * used.
5468
	 */
5469
	if (strcmp(probin, "-") != 0)
5470
	{
5471
		appendPQExpBuffer(asPart, "AS ");
5472
		appendStringLiteral(asPart, probin, true);
5473
		if (strcmp(prosrc, "-") != 0)
5474 5475
		{
			appendPQExpBuffer(asPart, ", ");
Bruce Momjian's avatar
Bruce Momjian committed
5476

5477
			/*
5478 5479 5480 5481 5482 5483 5484
			 * 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);
5485
		}
5486 5487 5488
	}
	else
	{
5489
		if (strcmp(prosrc, "-") != 0)
5490 5491
		{
			appendPQExpBuffer(asPart, "AS ");
5492 5493 5494 5495 5496
			/* with no bin, dollar quote src unconditionally if allowed */
			if (disable_dollar_quoting)
				appendStringLiteral(asPart, prosrc, false);
			else
				appendStringLiteralDQ(asPart, prosrc, NULL);
5497
		}
5498 5499
	}

5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531
	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;
		}
	}

5532 5533
	if (proargnames && *proargnames)
	{
Bruce Momjian's avatar
Bruce Momjian committed
5534
		int			nitems = 0;
5535

5536 5537
		if (!parsePGArray(proargnames, &argnames, &nitems) ||
			nitems != nallargs)
5538 5539
		{
			write_msg(NULL, "WARNING: could not parse proargnames array\n");
5540 5541 5542
			if (argnames)
				free(argnames);
			argnames = NULL;
5543 5544 5545
		}
	}

5546 5547 5548
	funcsig = format_function_arguments(finfo, nallargs, allargtypes,
										argmodes, argnames);
	funcsig_tag = format_function_signature(finfo, false);
5549

Bruce Momjian's avatar
Bruce Momjian committed
5550
	/*
5551
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
5552
	 */
5553
	appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
5554
					  fmtId(finfo->dobj.namespace->dobj.name),
5555
					  funcsig);
5556

5557
	rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
5558

5559
	appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
5560
	appendPQExpBuffer(q, "RETURNS %s%s\n    %s\n    LANGUAGE %s",
5561
					  (proretset[0] == 't') ? "SETOF " : "",
5562
					  rettypename,
5563
					  asPart->data,
5564
					  fmtId(lanname));
5565 5566

	free(rettypename);
5567

5568
	if (provolatile[0] != PROVOLATILE_VOLATILE)
5569
	{
5570
		if (provolatile[0] == PROVOLATILE_IMMUTABLE)
5571
			appendPQExpBuffer(q, " IMMUTABLE");
5572
		else if (provolatile[0] == PROVOLATILE_STABLE)
5573
			appendPQExpBuffer(q, " STABLE");
5574
		else if (provolatile[0] != PROVOLATILE_VOLATILE)
5575
		{
5576
			write_msg(NULL, "unrecognized provolatile value for function \"%s\"\n",
5577
					  finfo->dobj.name);
5578 5579
			exit_nicely();
		}
5580
	}
5581

5582 5583
	if (proisstrict[0] == 't')
		appendPQExpBuffer(q, " STRICT");
5584

5585 5586 5587
	if (prosecdef[0] == 't')
		appendPQExpBuffer(q, " SECURITY DEFINER");

5588 5589
	appendPQExpBuffer(q, ";\n");

5590 5591
	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
				 funcsig_tag,
5592
				 finfo->dobj.namespace->dobj.name,
5593
				 NULL,
5594
				 finfo->rolname, false,
5595 5596 5597
				 "FUNCTION", q->data, delqry->data, NULL,
				 finfo->dobj.dependencies, finfo->dobj.nDeps,
				 NULL, NULL);
5598

Bruce Momjian's avatar
Bruce Momjian committed
5599
	/* Dump Function Comments */
Bruce Momjian's avatar
Bruce Momjian committed
5600
	resetPQExpBuffer(q);
5601
	appendPQExpBuffer(q, "FUNCTION %s", funcsig);
5602
	dumpComment(fout, q->data,
5603
				finfo->dobj.namespace->dobj.name, finfo->rolname,
5604 5605 5606 5607
				finfo->dobj.catId, 0, finfo->dobj.dumpId);

	dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
			funcsig, funcsig_tag,
5608
			finfo->dobj.namespace->dobj.name,
5609
			finfo->rolname, finfo->proacl);
5610

5611 5612 5613
	PQclear(res);

	destroyPQExpBuffer(query);
5614 5615 5616
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delqry);
	destroyPQExpBuffer(asPart);
5617
	free(funcsig);
5618
	free(funcsig_tag);
5619 5620 5621 5622 5623 5624
	if (allargtypes)
		free(allargtypes);
	if (argmodes)
		free(argmodes);
	if (argnames)
		free(argnames);
5625 5626
}

5627 5628

/*
5629
 * Dump a user-defined cast
5630
 */
5631 5632
static void
dumpCast(Archive *fout, CastInfo *cast)
5633
{
5634 5635 5636 5637 5638 5639
	PQExpBuffer defqry;
	PQExpBuffer delqry;
	PQExpBuffer castsig;
	FuncInfo   *funcInfo = NULL;
	TypeInfo   *sourceInfo;
	TypeInfo   *targetInfo;
5640

5641 5642
	if (dataOnly)
		return;
5643

5644
	if (OidIsValid(cast->castfunc))
5645
	{
5646 5647 5648
		funcInfo = findFuncByOid(cast->castfunc);
		if (funcInfo == NULL)
			return;
5649 5650
	}

5651 5652 5653
	/*
	 * As per discussion we dump casts if one or more of the underlying
	 * objects (the conversion function and the two data types) are not
5654 5655 5656
	 * builtin AND if all of the non-builtin objects namespaces are included
	 * in the dump. Builtin meaning, the namespace name does not start with
	 * "pg_".
5657 5658 5659
	 */
	sourceInfo = findTypeByOid(cast->castsource);
	targetInfo = findTypeByOid(cast->casttarget);
5660

5661 5662
	if (sourceInfo == NULL || targetInfo == NULL)
		return;
5663

5664 5665 5666
	/*
	 * Skip this cast if all objects are from pg_
	 */
5667 5668 5669 5670
	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)
5671
		return;
5672

5673
	/*
5674
	 * Skip cast if function isn't from pg_ and that namespace is not dumped.
5675
	 */
5676
	if (funcInfo &&
5677 5678
		strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!funcInfo->dobj.namespace->dump)
5679
		return;
5680

5681 5682 5683
	/*
	 * Same for the Source type
	 */
5684 5685
	if (strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!sourceInfo->dobj.namespace->dump)
5686
		return;
5687

5688 5689 5690
	/*
	 * and the target type.
	 */
5691 5692
	if (strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!targetInfo->dobj.namespace->dump)
5693
		return;
5694

5695 5696
	/* Make sure we are in proper schema (needed for getFormattedTypeName) */
	selectSourceSchema("pg_catalog");
5697

5698 5699 5700
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
	castsig = createPQExpBuffer();
5701

5702 5703 5704
	appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5705

5706 5707 5708
	appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5709

5710 5711 5712
	if (!OidIsValid(cast->castfunc))
		appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
	else
5713 5714
	{
		/*
5715 5716
		 * Always qualify the function name, in case it is not in pg_catalog
		 * schema (format_function_signature won't qualify it).
5717 5718
		 */
		appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
5719
						  fmtId(funcInfo->dobj.namespace->dobj.name));
5720
		appendPQExpBuffer(defqry, "%s",
5721
						  format_function_signature(funcInfo, true));
5722
	}
5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735

	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,
5736
				 "pg_catalog", NULL, "",
5737
				 false, "CAST", defqry->data, delqry->data, NULL,
5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748
				 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);
5749 5750 5751

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5752
	destroyPQExpBuffer(castsig);
5753 5754
}

5755
/*
5756
 * dumpOpr
5757 5758 5759
 *	  write out a single operator definition
 */
static void
5760
dumpOpr(Archive *fout, OprInfo *oprinfo)
5761
{
5762 5763 5764 5765 5766
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer oprid;
	PQExpBuffer details;
5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796
	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;

5797
	/* Dump only operators in dumpable namespaces */
5798
	if (!oprinfo->dobj.namespace->dump || dataOnly)
5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813
		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();

5814
	/* Make sure we are in proper schema so regoperator works correctly */
5815
	selectSourceSchema(oprinfo->dobj.namespace->dobj.name);
5816 5817 5818

	if (g_fout->remoteVersion >= 70300)
	{
5819 5820 5821 5822 5823 5824 5825 5826
		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, "
5827
						  "oprcanhash, "
5828 5829 5830 5831 5832
						  "oprlsortop::pg_catalog.regoperator, "
						  "oprrsortop::pg_catalog.regoperator, "
						  "oprltcmpop::pg_catalog.regoperator, "
						  "oprgtcmpop::pg_catalog.regoperator "
						  "from pg_catalog.pg_operator "
5833 5834
						  "where oid = '%u'::pg_catalog.oid",
						  oprinfo->dobj.catId.oid);
5835 5836 5837 5838 5839
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
						  "CASE WHEN oprleft = 0 THEN '-' "
5840
						  "ELSE format_type(oprleft, NULL) END as oprleft, "
5841
						  "CASE WHEN oprright = 0 THEN '-' "
5842
						  "ELSE format_type(oprright, NULL) END as oprright, "
5843 5844 5845 5846
						  "oprcom, oprnegate, oprrest, oprjoin, "
						  "oprcanhash, oprlsortop, oprrsortop, "
						  "0 as oprltcmpop, 0 as oprgtcmpop "
						  "from pg_operator "
5847 5848
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860
	}
	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 "
5861 5862
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5863
	}
5864

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

5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904
	/* 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);

5905
	appendPQExpBuffer(details, "    PROCEDURE = %s",
5906 5907 5908
					  convertRegProcReference(oprcode));

	appendPQExpBuffer(oprid, "%s (",
5909
					  oprinfo->dobj.name);
5910 5911

	/*
Bruce Momjian's avatar
Bruce Momjian committed
5912 5913
	 * right unary means there's a left arg and left unary means there's a
	 * right arg
5914 5915 5916 5917 5918 5919 5920
	 */
	if (strcmp(oprkind, "r") == 0 ||
		strcmp(oprkind, "b") == 0)
	{
		if (g_fout->remoteVersion >= 70100)
			name = oprleft;
		else
5921 5922
			name = fmtId(oprleft);
		appendPQExpBuffer(details, ",\n    LEFTARG = %s", name);
5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933
		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
5934 5935
			name = fmtId(oprright);
		appendPQExpBuffer(details, ",\n    RIGHTARG = %s", name);
5936 5937 5938 5939 5940
		appendPQExpBuffer(oprid, ", %s)", name);
	}
	else
		appendPQExpBuffer(oprid, ", NONE)");

5941
	name = convertOperatorReference(oprcom);
5942
	if (name)
5943
		appendPQExpBuffer(details, ",\n    COMMUTATOR = %s", name);
5944

5945
	name = convertOperatorReference(oprnegate);
5946
	if (name)
5947
		appendPQExpBuffer(details, ",\n    NEGATOR = %s", name);
5948 5949

	if (strcmp(oprcanhash, "t") == 0)
5950
		appendPQExpBuffer(details, ",\n    HASHES");
5951 5952 5953

	name = convertRegProcReference(oprrest);
	if (name)
5954
		appendPQExpBuffer(details, ",\n    RESTRICT = %s", name);
5955 5956 5957

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

5960
	name = convertOperatorReference(oprlsortop);
5961
	if (name)
5962
		appendPQExpBuffer(details, ",\n    SORT1 = %s", name);
5963

5964
	name = convertOperatorReference(oprrsortop);
5965
	if (name)
5966
		appendPQExpBuffer(details, ",\n    SORT2 = %s", name);
5967

5968
	name = convertOperatorReference(oprltcmpop);
5969
	if (name)
5970
		appendPQExpBuffer(details, ",\n    LTCMP = %s", name);
5971

5972
	name = convertOperatorReference(oprgtcmpop);
5973
	if (name)
5974
		appendPQExpBuffer(details, ",\n    GTCMP = %s", name);
5975

Bruce Momjian's avatar
Bruce Momjian committed
5976
	/*
5977
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
5978
	 */
5979
	appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
5980
					  fmtId(oprinfo->dobj.namespace->dobj.name),
5981 5982
					  oprid->data);

5983
	appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n",
5984
					  oprinfo->dobj.name, details->data);
5985

5986
	ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
5987
				 oprinfo->dobj.name,
5988
				 oprinfo->dobj.namespace->dobj.name,
5989
				 NULL,
5990
				 oprinfo->rolname,
5991
				 false, "OPERATOR", q->data, delq->data, NULL,
5992 5993
				 oprinfo->dobj.dependencies, oprinfo->dobj.nDeps,
				 NULL, NULL);
5994

Bruce Momjian's avatar
Bruce Momjian committed
5995
	/* Dump Operator Comments */
5996 5997 5998
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
	dumpComment(fout, q->data,
5999
				oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
6000
				oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027

	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
6028 6029 6030
		char	   *name;
		char	   *paren;
		bool		inquote;
6031 6032 6033 6034 6035

		name = strdup(proc);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
6036
		{
6037
			if (*paren == '(' && !inquote)
6038
			{
6039 6040
				*paren = '\0';
				break;
6041
			}
6042 6043
			if (*paren == '"')
				inquote = !inquote;
6044
		}
6045 6046 6047 6048
		return name;
	}

	/* REGPROC before 7.3 does not quote its result */
6049
	return fmtId(proc);
6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061
}

/*
 * 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 *
6062
convertOperatorReference(const char *opr)
6063
{
Bruce Momjian's avatar
Bruce Momjian committed
6064
	OprInfo    *oprInfo;
6065 6066 6067 6068 6069 6070 6071

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

	if (g_fout->remoteVersion >= 70300)
	{
6072
		char	   *name;
Bruce Momjian's avatar
Bruce Momjian committed
6073 6074
		char	   *paren;
		bool		inquote;
6075

6076 6077 6078 6079
		name = strdup(opr);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
6080
		{
6081
			if (*paren == '(' && !inquote)
6082
			{
6083 6084
				*paren = '\0';
				break;
6085
			}
6086 6087
			if (*paren == '"')
				inquote = !inquote;
6088
		}
6089
		return name;
6090
	}
6091

6092 6093 6094
	oprInfo = findOprByOid(atooid(opr));
	if (oprInfo == NULL)
	{
6095
		write_msg(NULL, "WARNING: could not find operator with OID %s\n",
6096
				  opr);
6097
		return NULL;
6098
	}
6099
	return oprInfo->dobj.name;
6100 6101 6102
}

/*
6103
 * dumpOpclass
6104 6105 6106
 *	  write out a single operator class definition
 */
static void
6107
dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
6108
{
6109 6110 6111
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134
	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;

6135
	/* Dump only opclasses in dumpable namespaces */
6136
	if (!opcinfo->dobj.namespace->dump || dataOnly)
6137 6138
		return;

6139 6140 6141 6142 6143 6144 6145 6146
	/*
	 * 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;

6147 6148 6149 6150
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();

6151
	/* Make sure we are in proper schema so regoperator works correctly */
6152
	selectSourceSchema(opcinfo->dobj.namespace->dobj.name);
6153 6154 6155 6156 6157

	/* Get additional fields from the pg_opclass row */
	appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
					  "opckeytype::pg_catalog.regtype, "
					  "opcdefault, "
6158
	   "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
6159
					  "FROM pg_catalog.pg_opclass "
6160 6161
					  "WHERE oid = '%u'::pg_catalog.oid",
					  opcinfo->dobj.catId.oid);
6162 6163

	res = PQexec(g_conn, query->data);
6164
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182

	/* 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);
6183 6184
	/* amname will still be needed after we PQclear res */
	amname = strdup(PQgetvalue(res, 0, i_amname));
6185

Bruce Momjian's avatar
Bruce Momjian committed
6186
	/*
6187
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
6188
	 */
6189
	appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
6190
					  fmtId(opcinfo->dobj.namespace->dobj.name));
6191
	appendPQExpBuffer(delq, ".%s",
6192
					  fmtId(opcinfo->dobj.name));
6193
	appendPQExpBuffer(delq, " USING %s;\n",
6194
					  fmtId(amname));
6195 6196

	/* Build the fixed portion of the CREATE command */
6197
	appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n    ",
6198
					  fmtId(opcinfo->dobj.name));
6199 6200
	if (strcmp(opcdefault, "t") == 0)
		appendPQExpBuffer(q, "DEFAULT ");
6201
	appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n    ",
6202
					  opcintype,
6203
					  fmtId(amname));
6204 6205 6206 6207 6208

	needComma = false;

	if (strcmp(opckeytype, "-") != 0)
	{
6209
		appendPQExpBuffer(q, "STORAGE %s",
6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223
						  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 "
6224
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
6225
					  "ORDER BY amopstrategy",
6226
					  opcinfo->dobj.catId.oid);
6227 6228

	res = PQexec(g_conn, query->data);
6229
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243

	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)
6244
			appendPQExpBuffer(q, " ,\n    ");
6245

6246
		appendPQExpBuffer(q, "OPERATOR %s %s",
6247 6248
						  amopstrategy, amopopr);
		if (strcmp(amopreqcheck, "t") == 0)
6249
			appendPQExpBuffer(q, " RECHECK");
6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263

		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 "
6264
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
6265
					  "ORDER BY amprocnum",
6266
					  opcinfo->dobj.catId.oid);
6267 6268

	res = PQexec(g_conn, query->data);
6269
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281

	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)
6282
			appendPQExpBuffer(q, " ,\n    ");
6283

6284
		appendPQExpBuffer(q, "FUNCTION %s %s",
6285 6286 6287 6288 6289 6290 6291
						  amprocnum, amproc);

		needComma = true;
	}

	PQclear(res);

6292
	appendPQExpBuffer(q, ";\n");
6293

6294
	ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
6295
				 opcinfo->dobj.name,
6296
				 opcinfo->dobj.namespace->dobj.name,
6297
				 NULL,
6298
				 opcinfo->rolname,
6299
				 false, "OPERATOR CLASS", q->data, delq->data, NULL,
6300 6301
				 opcinfo->dobj.dependencies, opcinfo->dobj.nDeps,
				 NULL, NULL);
6302

6303 6304 6305
	/* Dump Operator Class Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR CLASS %s",
6306
					  fmtId(opcinfo->dobj.name));
6307 6308 6309
	appendPQExpBuffer(q, " USING %s",
					  fmtId(amname));
	dumpComment(fout, q->data,
6310
				NULL, opcinfo->rolname,
6311
				opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
6312 6313

	free(amname);
6314 6315 6316 6317 6318
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

6319
/*
6320
 * dumpConversion
6321 6322 6323
 *	  write out a single conversion definition
 */
static void
6324
dumpConversion(Archive *fout, ConvInfo *convinfo)
6325
{
6326 6327 6328 6329
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342
	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;

6343
	/* Dump only conversions in dumpable namespaces */
6344
	if (!convinfo->dobj.namespace->dump || dataOnly)
6345 6346 6347 6348 6349 6350 6351
		return;

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

6352
	/* Make sure we are in proper schema */
6353
	selectSourceSchema(convinfo->dobj.namespace->dobj.name);
6354 6355

	/* Get conversion-specific details */
6356
	appendPQExpBuffer(query, "SELECT conname, "
6357 6358
		 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
		   "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
6359 6360
					  "conproc, condefault "
					  "FROM pg_catalog.pg_conversion c "
6361 6362
					  "WHERE c.oid = '%u'::pg_catalog.oid",
					  convinfo->dobj.catId.oid);
6363 6364

	res = PQexec(g_conn, query->data);
6365
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388

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

	/*
6389
	 * DROP must be fully qualified in case same name appears in pg_catalog
6390 6391
	 */
	appendPQExpBuffer(delq, "DROP CONVERSION %s",
6392
					  fmtId(convinfo->dobj.namespace->dobj.name));
6393
	appendPQExpBuffer(delq, ".%s;\n",
6394
					  fmtId(convinfo->dobj.name));
6395 6396

	appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
Bruce Momjian's avatar
Bruce Momjian committed
6397 6398
					  (condefault) ? "DEFAULT " : "",
					  fmtId(convinfo->dobj.name));
6399 6400 6401 6402 6403
	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);
6404

6405
	ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
6406
				 convinfo->dobj.name,
6407 6408
				 convinfo->dobj.namespace->dobj.name,
				 NULL,
6409
				 convinfo->rolname,
6410
				 false, "CONVERSION", q->data, delq->data, NULL,
6411 6412
				 convinfo->dobj.dependencies, convinfo->dobj.nDeps,
				 NULL, NULL);
6413 6414 6415

	/* Dump Conversion Comments */
	resetPQExpBuffer(q);
6416
	appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->dobj.name));
6417
	dumpComment(fout, q->data,
6418
				convinfo->dobj.namespace->dobj.name, convinfo->rolname,
6419
				convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
6420

6421
	PQclear(res);
6422

6423 6424 6425 6426
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6427 6428
}

6429 6430 6431 6432 6433 6434
/*
 * format_aggregate_signature: generate aggregate name and argument list
 *
 * The argument type names are qualified if needed.  The aggregate name
 * is never qualified.
 */
6435
static char *
6436
format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
6437 6438 6439 6440
{
	PQExpBufferData buf;

	initPQExpBuffer(&buf);
6441 6442
	if (honor_quotes)
		appendPQExpBuffer(&buf, "%s",
6443
						  fmtId(agginfo->aggfn.dobj.name));
6444
	else
6445
		appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name);
6446

6447
	/* If using regtype or format_type, fmtbasetype is already quoted */
6448 6449
	if (fout->remoteVersion >= 70100)
	{
6450
		if (agginfo->anybasetype)
6451 6452
			appendPQExpBuffer(&buf, "(*)");
		else
6453
			appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
6454
	}
6455 6456
	else
	{
6457
		if (agginfo->anybasetype)
6458 6459 6460
			appendPQExpBuffer(&buf, "(*)");
		else
			appendPQExpBuffer(&buf, "(%s)",
6461
							  fmtId(agginfo->fmtbasetype));
6462 6463 6464
	}

	return buf.data;
6465 6466 6467
}

/*
6468
 * dumpAgg
6469 6470 6471
 *	  write out a single aggregate definition
 */
static void
6472
dumpAgg(Archive *fout, AggInfo *agginfo)
6473
{
6474 6475 6476 6477
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6478
	char	   *aggsig;
6479
	char	   *aggsig_tag;
6480 6481 6482 6483
	PGresult   *res;
	int			ntups;
	int			i_aggtransfn;
	int			i_aggfinalfn;
6484
	int			i_aggsortop;
6485 6486
	int			i_aggtranstype;
	int			i_agginitval;
6487
	int			i_anybasetype;
6488
	int			i_fmtbasetype;
6489 6490 6491
	int			i_convertok;
	const char *aggtransfn;
	const char *aggfinalfn;
6492
	const char *aggsortop;
6493 6494 6495 6496
	const char *aggtranstype;
	const char *agginitval;
	bool		convertok;

6497
	/* Dump only aggs in dumpable namespaces */
6498
	if (!agginfo->aggfn.dobj.namespace->dump || dataOnly)
6499 6500 6501 6502 6503 6504 6505
		return;

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

6506
	/* Make sure we are in proper schema */
6507
	selectSourceSchema(agginfo->aggfn.dobj.namespace->dobj.name);
6508 6509

	/* Get aggregate-specific details */
6510 6511 6512 6513 6514 6515 6516
	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, "
6517
						"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
6518
						  "'t'::boolean as convertok "
6519
					  "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
6520 6521 6522 6523 6524
						  "where a.aggfnoid = p.oid "
						  "and p.oid = '%u'::pg_catalog.oid",
						  agginfo->aggfn.dobj.catId.oid);
	}
	else if (g_fout->remoteVersion >= 70300)
6525 6526
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, "
6527
						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
6528
						  "0 as aggsortop, "
6529
						  "agginitval, "
6530
						  "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
6531
						"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
6532
						  "'t'::boolean as convertok "
6533
					  "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
6534
						  "where a.aggfnoid = p.oid "
6535 6536
						  "and p.oid = '%u'::pg_catalog.oid",
						  agginfo->aggfn.dobj.catId.oid);
6537 6538 6539 6540
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
6541
						  "format_type(aggtranstype, NULL) as aggtranstype, "
6542
						  "0 as aggsortop, "
6543
						  "agginitval, "
6544
						  "aggbasetype = 0 as anybasetype, "
6545
						  "CASE WHEN aggbasetype = 0 THEN '-' "
6546
				   "ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
6547
						  "'t'::boolean as convertok "
6548
						  "from pg_aggregate "
6549 6550
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6551 6552 6553 6554 6555 6556
	}
	else
	{
		appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
						  "aggfinalfn, "
						  "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
6557
						  "0 as aggsortop, "
6558
						  "agginitval1 as agginitval, "
6559
						  "aggbasetype = 0 as anybasetype, "
6560
						  "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
6561 6562
						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
						  "from pg_aggregate "
6563 6564
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6565
	}
6566

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

6570 6571 6572 6573 6574 6575 6576 6577
	/* 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
6578

6579 6580
	i_aggtransfn = PQfnumber(res, "aggtransfn");
	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
6581
	i_aggsortop = PQfnumber(res, "aggsortop");
6582 6583
	i_aggtranstype = PQfnumber(res, "aggtranstype");
	i_agginitval = PQfnumber(res, "agginitval");
6584
	i_anybasetype = PQfnumber(res, "anybasetype");
6585
	i_fmtbasetype = PQfnumber(res, "fmtbasetype");
6586
	i_convertok = PQfnumber(res, "convertok");
6587

6588 6589
	aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
6590
	aggsortop = PQgetvalue(res, 0, i_aggsortop);
6591 6592
	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
	agginitval = PQgetvalue(res, 0, i_agginitval);
6593
	/* we save anybasetype for format_aggregate_signature */
6594
	agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
6595
	/* we save fmtbasetype for format_aggregate_signature */
6596
	agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
6597
	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
6598

6599 6600
	aggsig = format_aggregate_signature(agginfo, fout, true);
	aggsig_tag = format_aggregate_signature(agginfo, fout, false);
6601

6602 6603 6604
	if (!convertok)
	{
		write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
6605
				  aggsig);
6606 6607
		return;
	}
6608

6609 6610 6611
	if (g_fout->remoteVersion >= 70300)
	{
		/* If using 7.3's regproc or regtype, data is already quoted */
6612
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6613 6614
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6615 6616 6617 6618 6619 6620
						  aggtransfn,
						  aggtranstype);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* format_type quotes, regproc does not */
6621
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6622 6623
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6624
						  fmtId(aggtransfn),
6625 6626 6627 6628 6629
						  aggtranstype);
	}
	else
	{
		/* need quotes all around */
6630
		appendPQExpBuffer(details, "    BASETYPE = %s,\n",
6631
						  agginfo->anybasetype ? "'any'" :
6632 6633 6634 6635 6636
						  fmtId(agginfo->fmtbasetype));
		appendPQExpBuffer(details, "    SFUNC = %s,\n",
						  fmtId(aggtransfn));
		appendPQExpBuffer(details, "    STYPE = %s",
						  fmtId(aggtranstype));
6637
	}
6638

6639 6640
	if (!PQgetisnull(res, 0, i_agginitval))
	{
6641 6642
		appendPQExpBuffer(details, ",\n    INITCOND = ");
		appendStringLiteral(details, agginitval, true);
6643
	}
6644

6645 6646
	if (strcmp(aggfinalfn, "-") != 0)
	{
6647
		appendPQExpBuffer(details, ",\n    FINALFUNC = %s",
6648 6649
						  aggfinalfn);
	}
6650

6651 6652 6653 6654 6655 6656 6657
	aggsortop = convertOperatorReference(aggsortop);
	if (aggsortop)
	{
		appendPQExpBuffer(details, ",\n    SORTOP = %s",
						  aggsortop);
	}

Bruce Momjian's avatar
Bruce Momjian committed
6658
	/*
6659
	 * DROP must be fully qualified in case same name appears in pg_catalog
Bruce Momjian's avatar
Bruce Momjian committed
6660
	 */
6661
	appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
6662
					  fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
6663
					  aggsig);
6664

6665
	appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
6666
					  fmtId(agginfo->aggfn.dobj.name),
6667
					  details->data);
6668

6669 6670
	ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
				 aggsig_tag,
6671 6672
				 agginfo->aggfn.dobj.namespace->dobj.name,
				 NULL,
6673
				 agginfo->aggfn.rolname,
6674
				 false, "AGGREGATE", q->data, delq->data, NULL,
6675
				 agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps,
6676
				 NULL, NULL);
6677

Bruce Momjian's avatar
Bruce Momjian committed
6678
	/* Dump Aggregate Comments */
6679
	resetPQExpBuffer(q);
6680
	appendPQExpBuffer(q, "AGGREGATE %s", aggsig);
6681
	dumpComment(fout, q->data,
6682
			agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
6683 6684 6685
				agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);

	/*
6686 6687 6688
	 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
	 * command look like a function's GRANT; in particular this affects the
	 * syntax for aggregates on ANY.
6689 6690 6691 6692
	 */
	free(aggsig);
	free(aggsig_tag);

6693 6694
	aggsig = format_function_signature(&agginfo->aggfn, true);
	aggsig_tag = format_function_signature(&agginfo->aggfn, false);
6695 6696 6697 6698

	dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
			"FUNCTION",
			aggsig, aggsig_tag,
6699
			agginfo->aggfn.dobj.namespace->dobj.name,
6700
			agginfo->aggfn.rolname, agginfo->aggfn.proacl);
6701 6702 6703

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

6705
	PQclear(res);
6706

6707
	destroyPQExpBuffer(query);
6708 6709 6710
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6711 6712
}

6713

6714
/*----------
6715 6716
 * Write out grant/revoke information
 *
6717 6718
 * 'objCatId' is the catalog ID of the underlying object.
 * 'objDumpId' is the dump ID of the underlying object.
6719
 * 'type' must be TABLE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE.
Bruce Momjian's avatar
Bruce Momjian committed
6720
 * 'name' is the formatted name of the object.	Must be quoted etc. already.
6721
 * 'tag' is the tag for the archive entry (typ. unquoted name of object).
6722
 * 'nspname' is the namespace the object is in (NULL if none).
6723
 * 'owner' is the owner, NULL if there is no owner (for languages).
6724 6725
 * 'acls' is the string read out of the fooacl system catalog field;
 * it will be parsed here.
6726
 *----------
Bruce Momjian's avatar
Bruce Momjian committed
6727
 */
6728
static void
6729 6730
dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
		const char *type, const char *name,
6731
		const char *tag, const char *nspname, const char *owner,
6732
		const char *acls)
Bruce Momjian's avatar
Bruce Momjian committed
6733
{
6734
	PQExpBuffer sql;
6735

6736 6737 6738
	/* Do nothing if ACL dump is not enabled */
	if (dataOnly || aclsSkip)
		return;
6739

6740
	sql = createPQExpBuffer();
6741

6742
	if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql))
6743
	{
6744
		write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
6745 6746
				  acls, name, type);
		exit_nicely();
Bruce Momjian's avatar
Bruce Momjian committed
6747
	}
6748

6749
	if (sql->len > 0)
6750 6751
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
					 tag, nspname,
6752
					 NULL,
6753
					 owner ? owner : "",
6754
					 false, "ACL", sql->data, "", NULL,
6755 6756
					 &(objDumpId), 1,
					 NULL, NULL);
6757 6758

	destroyPQExpBuffer(sql);
Bruce Momjian's avatar
Bruce Momjian committed
6759 6760
}

6761
/*
6762 6763
 * dumpTable
 *	  write out to fout the declarations (not data) of a user-defined table
6764
 */
6765 6766
static void
dumpTable(Archive *fout, TableInfo *tbinfo)
6767
{
6768
	char	   *namecopy;
6769

6770
	if (tbinfo->dump)
6771
	{
6772 6773
		if (tbinfo->relkind == RELKIND_SEQUENCE)
			dumpSequence(fout, tbinfo);
6774
		else if (!dataOnly)
6775 6776 6777
			dumpTableSchema(fout, tbinfo);

		/* Handle the ACL here */
6778
		namecopy = strdup(fmtId(tbinfo->dobj.name));
6779
		dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
6780
				namecopy, tbinfo->dobj.name,
6781
				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
6782 6783
				tbinfo->relacl);
		free(namecopy);
6784
	}
6785
}
6786

6787
/*
6788
 * dumpTableSchema
6789
 *	  write the declaration (not data) of one user-defined table or view
6790
 */
6791
static void
6792
dumpTableSchema(Archive *fout, TableInfo *tbinfo)
6793
{
6794
	PQExpBuffer query = createPQExpBuffer();
6795
	PQExpBuffer q = createPQExpBuffer();
6796
	PQExpBuffer delq = createPQExpBuffer();
6797
	PGresult   *res;
6798
	int			numParents;
6799
	TableInfo **parents;
Bruce Momjian's avatar
Bruce Momjian committed
6800
	int			actual_atts;	/* number of attrs in this CREATE statment */
6801
	char	   *reltypename;
6802
	char	   *storage;
6803 6804
	int			j,
				k;
6805

6806
	/* Make sure we are in proper schema */
6807
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
6808

6809 6810
	/* Is it a table or a view? */
	if (tbinfo->relkind == RELKIND_VIEW)
6811
	{
6812
		char	   *viewdef;
6813

6814
		reltypename = "VIEW";
6815

6816 6817 6818
		/* Fetch the view definition */
		if (g_fout->remoteVersion >= 70300)
		{
6819 6820 6821 6822
			/* 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);
6823 6824 6825
		}
		else
		{
6826
			appendPQExpBuffer(query, "SELECT definition as viewdef "
6827
							  " from pg_views where viewname = ");
6828
			appendStringLiteral(query, tbinfo->dobj.name, true);
6829 6830
			appendPQExpBuffer(query, ";");
		}
6831

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

6835 6836 6837 6838
		if (PQntuples(res) != 1)
		{
			if (PQntuples(res) < 1)
				write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
6839
						  tbinfo->dobj.name);
6840
			else
6841
				write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
6842
						  tbinfo->dobj.name);
6843 6844
			exit_nicely();
		}
Bruce Momjian's avatar
Bruce Momjian committed
6845

6846
		viewdef = PQgetvalue(res, 0, 0);
6847

6848 6849 6850
		if (strlen(viewdef) == 0)
		{
			write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
6851
					  tbinfo->dobj.name);
6852 6853
			exit_nicely();
		}
6854

Bruce Momjian's avatar
Bruce Momjian committed
6855 6856 6857 6858
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6859
		appendPQExpBuffer(delq, "DROP VIEW %s.",
6860
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6861
		appendPQExpBuffer(delq, "%s;\n",
6862
						  fmtId(tbinfo->dobj.name));
6863

6864
		appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
6865
						  fmtId(tbinfo->dobj.name), viewdef);
6866

6867 6868 6869 6870 6871 6872
		PQclear(res);
	}
	else
	{
		reltypename = "TABLE";
		numParents = tbinfo->numParents;
6873
		parents = tbinfo->parents;
6874

Bruce Momjian's avatar
Bruce Momjian committed
6875 6876 6877 6878
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6879
		appendPQExpBuffer(delq, "DROP TABLE %s.",
6880
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6881
		appendPQExpBuffer(delq, "%s;\n",
6882
						  fmtId(tbinfo->dobj.name));
6883

6884
		appendPQExpBuffer(q, "CREATE TABLE %s (",
6885
						  fmtId(tbinfo->dobj.name));
6886 6887 6888
		actual_atts = 0;
		for (j = 0; j < tbinfo->numatts; j++)
		{
6889 6890
			/* Is this one of the table's own attrs, and not dropped ? */
			if (!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j])
6891 6892 6893
			{
				/* Format properly if not first attr */
				if (actual_atts > 0)
6894 6895
					appendPQExpBuffer(q, ",");
				appendPQExpBuffer(q, "\n    ");
6896

6897
				/* Attribute name */
6898
				appendPQExpBuffer(q, "%s ",
6899
								  fmtId(tbinfo->attnames[j]));
6900

6901
				/* Attribute type */
6902
				if (g_fout->remoteVersion >= 70100)
6903
				{
Bruce Momjian's avatar
Bruce Momjian committed
6904
					char	   *typname = tbinfo->atttypnames[j];
6905 6906 6907 6908 6909 6910 6911 6912 6913 6914

					if (tbinfo->attisserial[j])
					{
						if (strcmp(typname, "integer") == 0)
							typname = "serial";
						else if (strcmp(typname, "bigint") == 0)
							typname = "bigserial";
					}
					appendPQExpBuffer(q, "%s", typname);
				}
6915
				else
6916 6917
				{
					/* If no format_type, fake it */
6918 6919 6920
					appendPQExpBuffer(q, "%s",
									  myFormatType(tbinfo->atttypnames[j],
												   tbinfo->atttypmod[j]));
6921
				}
6922

6923
				/*
6924 6925
				 * Default value --- suppress if inherited, serial, or to be
				 * printed separately.
6926 6927
				 */
				if (tbinfo->attrdefs[j] != NULL &&
6928
					!tbinfo->inhAttrDef[j] &&
6929 6930
					!tbinfo->attisserial[j] &&
					!tbinfo->attrdefs[j]->separate)
6931
					appendPQExpBuffer(q, " DEFAULT %s",
6932
									  tbinfo->attrdefs[j]->adef_expr);
Bruce Momjian's avatar
Bruce Momjian committed
6933

6934 6935 6936
				/*
				 * Not Null constraint --- suppress if inherited
				 *
6937 6938
				 * Note: we could suppress this for serial columns since
				 * SERIAL implies NOT NULL.  We choose not to for forward
6939 6940 6941 6942
				 * compatibility, since there has been some talk of making
				 * SERIAL not imply NOT NULL, in which case the explicit
				 * specification would be needed.
				 */
6943 6944
				if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
					appendPQExpBuffer(q, " NOT NULL");
6945

6946
				actual_atts++;
6947
			}
6948
		}
6949

6950
		/*
6951
		 * Add non-inherited CHECK constraints, if any.
6952
		 */
6953
		for (j = 0; j < tbinfo->ncheck; j++)
6954
		{
6955
			ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
6956

6957 6958
			if (constr->coninherited || constr->separate)
				continue;
6959

6960 6961
			if (actual_atts > 0)
				appendPQExpBuffer(q, ",\n    ");
6962

6963
			appendPQExpBuffer(q, "CONSTRAINT %s ",
6964
							  fmtId(constr->dobj.name));
6965
			appendPQExpBuffer(q, "%s", constr->condef);
6966

6967
			actual_atts++;
6968
		}
6969

6970
		appendPQExpBuffer(q, "\n)");
6971

6972 6973 6974 6975 6976
		if (numParents > 0)
		{
			appendPQExpBuffer(q, "\nINHERITS (");
			for (k = 0; k < numParents; k++)
			{
6977
				TableInfo  *parentRel = parents[k];
6978

6979 6980
				if (k > 0)
					appendPQExpBuffer(q, ", ");
6981
				if (parentRel->dobj.namespace != tbinfo->dobj.namespace)
6982
					appendPQExpBuffer(q, "%s.",
6983
								fmtId(parentRel->dobj.namespace->dobj.name));
6984
				appendPQExpBuffer(q, "%s",
6985
								  fmtId(parentRel->dobj.name));
6986 6987 6988
			}
			appendPQExpBuffer(q, ")");
		}
6989

6990
		appendPQExpBuffer(q, ";\n");
6991

6992
		/* Loop dumping statistics and storage statements */
Bruce Momjian's avatar
Bruce Momjian committed
6993
		for (j = 0; j < tbinfo->numatts; j++)
6994
		{
6995
			/*
6996 6997 6998
			 * 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)
6999
			 */
7000 7001
			if (tbinfo->attstattarget[j] >= 0 &&
				!tbinfo->attisdropped[j])
7002
			{
7003
				appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
7004
								  fmtId(tbinfo->dobj.name));
7005
				appendPQExpBuffer(q, "ALTER COLUMN %s ",
7006
								  fmtId(tbinfo->attnames[j]));
7007 7008 7009
				appendPQExpBuffer(q, "SET STATISTICS %d;\n",
								  tbinfo->attstattarget[j]);
			}
7010 7011

			/*
Bruce Momjian's avatar
Bruce Momjian committed
7012
			 * Dump per-column storage information.  The statement is only
7013
			 * dumped if the storage has been changed from the type's default.
7014
			 */
Bruce Momjian's avatar
Bruce Momjian committed
7015
			if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
7016
			{
Bruce Momjian's avatar
Bruce Momjian committed
7017 7018
				switch (tbinfo->attstorage[j])
				{
7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033
					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
7034 7035

				/*
7036
				 * Only dump the statement if it's a storage type we recognize
Bruce Momjian's avatar
Bruce Momjian committed
7037 7038 7039
				 */
				if (storage != NULL)
				{
7040
					appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
7041
									  fmtId(tbinfo->dobj.name));
7042 7043 7044 7045 7046 7047
					appendPQExpBuffer(q, "ALTER COLUMN %s ",
									  fmtId(tbinfo->attnames[j]));
					appendPQExpBuffer(q, "SET STORAGE %s;\n",
									  storage);
				}
			}
7048
		}
7049 7050
	}

7051
	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
7052
				 tbinfo->dobj.name,
7053
				 tbinfo->dobj.namespace->dobj.name,
7054
			(tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
7055
				 tbinfo->rolname,
7056
			   (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
7057 7058 7059
				 reltypename, q->data, delq->data, NULL,
				 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
				 NULL, NULL);
7060

7061
	/* Dump Table Comments */
7062
	dumpTableComment(fout, tbinfo, reltypename);
7063

7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074
	/* 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);
	}

7075 7076 7077
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
7078 7079
}

7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095
/*
 * 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
7096
	if (tbinfo->inhAttrDef[adnum - 1] || tbinfo->attisserial[adnum - 1])
7097 7098 7099 7100 7101 7102
		return;

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

	appendPQExpBuffer(q, "ALTER TABLE %s ",
7103
					  fmtId(tbinfo->dobj.name));
7104 7105 7106 7107 7108
	appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
					  fmtId(tbinfo->attnames[adnum - 1]),
					  adinfo->adef_expr);

	/*
7109
	 * DROP must be fully qualified in case same name appears in pg_catalog
7110 7111
	 */
	appendPQExpBuffer(delq, "ALTER TABLE %s.",
7112
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7113
	appendPQExpBuffer(delq, "%s ",
7114
					  fmtId(tbinfo->dobj.name));
7115 7116 7117 7118 7119
	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],
7120
				 tbinfo->dobj.namespace->dobj.name,
7121
				 NULL,
7122
				 tbinfo->rolname,
7123
				 false, "DEFAULT", q->data, delq->data, NULL,
7124 7125 7126 7127 7128 7129 7130
				 adinfo->dobj.dependencies, adinfo->dobj.nDeps,
				 NULL, NULL);

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141
/*
 * 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)
7142
		return tblInfo->attnames[attrnum - 1];
7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159
	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";
	}
7160
	write_msg(NULL, "invalid column number %d for table \"%s\"\n",
7161
			  attrnum, tblInfo->dobj.name);
7162
	exit_nicely();
7163 7164 7165
	return NULL;				/* keep compiler quiet */
}

7166
/*
7167 7168
 * dumpIndex
 *	  write out to fout a user-defined index
7169
 */
7170 7171
static void
dumpIndex(Archive *fout, IndxInfo *indxinfo)
7172
{
7173 7174 7175
	TableInfo  *tbinfo = indxinfo->indextable;
	PQExpBuffer q;
	PQExpBuffer delq;
7176

7177 7178
	if (dataOnly)
		return;
7179

7180 7181
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
7182

7183
	/*
7184 7185 7186
	 * If there's an associated constraint, don't dump the index per se, but
	 * do dump any comment for it.	(This is safe because dependency ordering
	 * will have ensured the constraint is emitted first.)
7187 7188 7189 7190 7191
	 */
	if (indxinfo->indexconstraint == 0)
	{
		/* Plain secondary index */
		appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
7192

7193 7194 7195 7196
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
7197
							  fmtId(tbinfo->dobj.name));
7198
			appendPQExpBuffer(q, " ON %s;\n",
7199
							  fmtId(indxinfo->dobj.name));
7200
		}
7201

7202
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7203 7204
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
7205
		 */
7206
		appendPQExpBuffer(delq, "DROP INDEX %s.",
7207
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7208
		appendPQExpBuffer(delq, "%s;\n",
7209
						  fmtId(indxinfo->dobj.name));
7210

7211
		ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
7212 7213
					 indxinfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7214
					 indxinfo->tablespace,
7215
					 tbinfo->rolname, false,
7216 7217 7218 7219
					 "INDEX", q->data, delq->data, NULL,
					 indxinfo->dobj.dependencies, indxinfo->dobj.nDeps,
					 NULL, NULL);
	}
7220

7221 7222 7223
	/* Dump Index Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "INDEX %s",
7224
					  fmtId(indxinfo->dobj.name));
7225
	dumpComment(fout, q->data,
7226
				tbinfo->dobj.namespace->dobj.name,
7227
				tbinfo->rolname,
7228
				indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId);
7229

7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262
	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
7263
			write_msg(NULL, "missing index for constraint \"%s\"\n",
7264
					  coninfo->dobj.name);
7265 7266
			exit_nicely();
		}
7267

7268
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
7269
						  fmtId(tbinfo->dobj.name));
7270
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s (",
7271
						  fmtId(coninfo->dobj.name),
7272
						  coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
7273 7274

		for (k = 0; k < indxinfo->indnkeys; k++)
7275
		{
7276 7277
			int			indkey = (int) indxinfo->indkeys[k];
			const char *attname;
7278

7279 7280 7281
			if (indkey == InvalidAttrNumber)
				break;
			attname = getAttrName(indkey, tbinfo);
7282

7283 7284 7285 7286
			appendPQExpBuffer(q, "%s%s",
							  (k == 0) ? "" : ", ",
							  fmtId(attname));
		}
7287

7288
		appendPQExpBuffer(q, ");\n");
7289

7290 7291 7292 7293
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
7294
							  fmtId(tbinfo->dobj.name));
7295
			appendPQExpBuffer(q, " ON %s;\n",
7296
							  fmtId(indxinfo->dobj.name));
7297
		}
7298

7299
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7300 7301
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
7302 7303
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
7304
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7305
		appendPQExpBuffer(delq, "%s ",
7306
						  fmtId(tbinfo->dobj.name));
7307
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7308
						  fmtId(coninfo->dobj.name));
7309

7310
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7311 7312
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7313
					 indxinfo->tablespace,
7314
					 tbinfo->rolname, false,
7315 7316 7317 7318 7319 7320 7321
					 "CONSTRAINT", q->data, delq->data, NULL,
					 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
					 NULL, NULL);
	}
	else if (coninfo->contype == 'f')
	{
		/*
7322 7323
		 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
		 * current table data is not processed
7324 7325
		 */
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
7326
						  fmtId(tbinfo->dobj.name));
7327
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7328
						  fmtId(coninfo->dobj.name),
7329
						  coninfo->condef);
7330

7331 7332 7333 7334 7335
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
7336
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7337
		appendPQExpBuffer(delq, "%s ",
7338
						  fmtId(tbinfo->dobj.name));
7339
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7340
						  fmtId(coninfo->dobj.name));
7341

7342
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7343 7344
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7345
					 NULL,
7346
					 tbinfo->rolname, false,
7347 7348 7349 7350 7351 7352 7353
					 "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 */
7354

7355 7356 7357 7358 7359
		/* 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",
7360
							  fmtId(tbinfo->dobj.name));
7361
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7362
							  fmtId(coninfo->dobj.name),
7363
							  coninfo->condef);
7364

7365 7366 7367 7368 7369
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER TABLE %s.",
7370
							  fmtId(tbinfo->dobj.namespace->dobj.name));
7371
			appendPQExpBuffer(delq, "%s ",
7372
							  fmtId(tbinfo->dobj.name));
7373
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7374
							  fmtId(coninfo->dobj.name));
7375

7376
			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7377 7378
						 coninfo->dobj.name,
						 tbinfo->dobj.namespace->dobj.name,
7379
						 NULL,
7380
						 tbinfo->rolname, false,
7381 7382 7383 7384 7385 7386 7387 7388 7389
						 "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
7390

7391
		/* Ignore if not to be dumped separately, or if not dumping domain */
7392
		if (coninfo->separate && tinfo->dobj.namespace->dump)
7393 7394
		{
			appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
7395
							  fmtId(tinfo->dobj.name));
7396
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7397
							  fmtId(coninfo->dobj.name),
7398
							  coninfo->condef);
7399

7400 7401 7402 7403 7404
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER DOMAIN %s.",
7405
							  fmtId(tinfo->dobj.namespace->dobj.name));
7406
			appendPQExpBuffer(delq, "%s ",
7407
							  fmtId(tinfo->dobj.name));
7408
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7409
							  fmtId(coninfo->dobj.name));
7410 7411

			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7412 7413
						 coninfo->dobj.name,
						 tinfo->dobj.namespace->dobj.name,
7414
						 NULL,
7415
						 tinfo->rolname, false,
7416 7417 7418
						 "CHECK CONSTRAINT", q->data, delq->data, NULL,
						 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
						 NULL, NULL);
7419
		}
7420 7421 7422
	}
	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
7423
		write_msg(NULL, "unrecognized constraint type: %c\n", coninfo->contype);
7424 7425
		exit_nicely();
	}
7426

7427
	/* Dump Constraint Comments --- only works for table constraints */
7428 7429
	if (tbinfo && coninfo->separate)
		dumpTableConstraintComment(fout, coninfo);
7430 7431 7432

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
7433 7434
}

7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453
/*
 * 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,
7454
				tbinfo->rolname,
7455
				coninfo->dobj.catId, 0,
7456
			 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
7457 7458 7459 7460

	destroyPQExpBuffer(q);
}

7461 7462
/*
 * findLastBuiltInOid -
7463
 * find the last built in oid
7464 7465 7466
 *
 * For 7.1 and 7.2, we do this by retrieving datlastsysoid from the
 * pg_database entry for the current database
7467
 */
7468
static Oid
7469
findLastBuiltinOid_V71(const char *dbname)
7470
{
Bruce Momjian's avatar
Bruce Momjian committed
7471
	PGresult   *res;
7472
	int			ntups;
7473
	Oid			last_oid;
7474 7475 7476
	PQExpBuffer query = createPQExpBuffer();

	resetPQExpBuffer(query);
7477
	appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
7478
	appendStringLiteral(query, dbname, true);
7479

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

7483
	ntups = PQntuples(res);
7484
	if (ntups < 1)
7485
	{
7486
		write_msg(NULL, "missing pg_database entry for this database\n");
7487
		exit_nicely();
7488 7489 7490
	}
	if (ntups > 1)
	{
7491
		write_msg(NULL, "found more than one pg_database entry for this database\n");
7492
		exit_nicely();
7493
	}
7494
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
7495
	PQclear(res);
7496
	destroyPQExpBuffer(query);
7497
	return last_oid;
7498 7499
}

7500 7501 7502
/*
 * findLastBuiltInOid -
 * find the last built in oid
7503 7504
 *
 * For 7.0, we do this by assuming that the last thing that initdb does is to
Bruce Momjian's avatar
Bruce Momjian committed
7505
 * create the pg_indexes view.	This sucks in general, but seeing that 7.0.x
7506 7507
 * initdb won't be changing anymore, it'll do.
 */
7508
static Oid
7509 7510 7511 7512 7513 7514 7515
findLastBuiltinOid_V70(void)
{
	PGresult   *res;
	int			ntups;
	int			last_oid;

	res = PQexec(g_conn,
7516
				 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'");
7517
	check_sql_result(res, g_conn,
7518
					 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'",
7519
					 PGRES_TUPLES_OK);
7520 7521 7522
	ntups = PQntuples(res);
	if (ntups < 1)
	{
7523
		write_msg(NULL, "could not find entry for pg_indexes in pg_class\n");
7524
		exit_nicely();
7525 7526 7527
	}
	if (ntups > 1)
	{
7528
		write_msg(NULL, "found more than one entry for pg_indexes in pg_class\n");
7529
		exit_nicely();
7530 7531 7532 7533 7534
	}
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
	PQclear(res);
	return last_oid;
}
7535

7536
static void
7537
dumpSequence(Archive *fout, TableInfo *tbinfo)
7538
{
7539
	PGresult   *res;
7540 7541
	char	   *last,
			   *incby,
7542 7543
			   *maxv = NULL,
			   *minv = NULL,
7544
			   *cache;
7545 7546
	char		bufm[100],
				bufx[100];
7547
	bool		cycled,
Bruce Momjian's avatar
Bruce Momjian committed
7548
				called;
7549
	PQExpBuffer query = createPQExpBuffer();
7550
	PQExpBuffer delqry = createPQExpBuffer();
7551

7552
	/* Make sure we are in proper schema */
7553
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7554

7555 7556 7557
	snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
	snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);

Bruce Momjian's avatar
Bruce Momjian committed
7558
	appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7559
					  "SELECT sequence_name, last_value, increment_by, "
7560 7561
				   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
				   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
Bruce Momjian's avatar
Bruce Momjian committed
7562 7563
					  "     ELSE max_value "
					  "END AS max_value, "
7564 7565
					"CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
				   "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
Bruce Momjian's avatar
Bruce Momjian committed
7566 7567 7568
					  "     ELSE min_value "
					  "END AS min_value, "
					  "cache_value, is_cycled, is_called from %s",
7569
					  bufx, bufm,
7570
					  fmtId(tbinfo->dobj.name));
7571

Bruce Momjian's avatar
Bruce Momjian committed
7572
	res = PQexec(g_conn, query->data);
7573
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
7574 7575 7576

	if (PQntuples(res) != 1)
	{
7577
		write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
7578
				  tbinfo->dobj.name, PQntuples(res));
7579
		exit_nicely();
7580 7581
	}

7582 7583
	/* Disable this check: it fails if sequence has been renamed */
#ifdef NOT_USED
7584
	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
7585
	{
7586
		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
7587
				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
7588
		exit_nicely();
7589
	}
7590
#endif
7591

7592 7593
	last = PQgetvalue(res, 0, 1);
	incby = PQgetvalue(res, 0, 2);
7594 7595 7596 7597
	if (!PQgetisnull(res, 0, 3))
		maxv = PQgetvalue(res, 0, 3);
	if (!PQgetisnull(res, 0, 4))
		minv = PQgetvalue(res, 0, 4);
7598 7599 7600
	cache = PQgetvalue(res, 0, 5);
	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
	called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
7601

7602
	/*
7603 7604
	 * The logic we use for restoring sequences is as follows:
	 *
7605 7606 7607
	 * 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.
7608
	 *
7609 7610
	 * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
	 * data. We do this for serial sequences too.
7611
	 */
7612

7613
	if (!dataOnly && !OidIsValid(tbinfo->owning_tab))
7614 7615
	{
		resetPQExpBuffer(delqry);
7616

Bruce Momjian's avatar
Bruce Momjian committed
7617 7618 7619 7620
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
7621
		appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
7622
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7623
		appendPQExpBuffer(delqry, "%s;\n",
7624
						  fmtId(tbinfo->dobj.name));
7625

7626 7627
		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7628
						  "CREATE SEQUENCE %s\n",
7629
						  fmtId(tbinfo->dobj.name));
7630 7631 7632 7633 7634

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

		appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);
7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646

		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,
7647
						  "    CACHE %s%s",
7648
						  cache, (cycled ? "\n    CYCLE" : ""));
7649

7650 7651
		appendPQExpBuffer(query, ";\n");

7652
		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
7653
					 tbinfo->dobj.name,
7654
					 tbinfo->dobj.namespace->dobj.name,
7655
					 NULL,
7656
					 tbinfo->rolname,
7657
					 false, "SEQUENCE", query->data, delqry->data, NULL,
7658 7659
					 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
					 NULL, NULL);
7660
	}
7661

7662 7663
	if (!schemaOnly)
	{
Bruce Momjian's avatar
Bruce Momjian committed
7664
		TableInfo  *owning_tab;
7665

7666
		resetPQExpBuffer(query);
7667
		appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
Bruce Momjian's avatar
Bruce Momjian committed
7668

7669
		/*
7670 7671 7672 7673
		 * 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.
7674 7675 7676 7677 7678 7679 7680
		 */
		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
7681
			appendStringLiteral(query, owning_tab->attnames[tbinfo->owning_col - 1], true);
7682 7683 7684 7685
			appendPQExpBuffer(query, ")");
		}
		else
			appendStringLiteral(query, fmtId(tbinfo->dobj.name), true);
7686 7687
		appendPQExpBuffer(query, ", %s, %s);\n",
						  last, (called ? "true" : "false"));
7688

7689
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
7690
					 tbinfo->dobj.name,
7691 7692
					 tbinfo->dobj.namespace->dobj.name,
					 NULL,
7693
					 tbinfo->rolname,
7694
					 false, "SEQUENCE SET", query->data, "", NULL,
7695 7696
					 &(tbinfo->dobj.dumpId), 1,
					 NULL, NULL);
7697
	}
Bruce Momjian's avatar
Bruce Momjian committed
7698

7699 7700 7701 7702
	if (!dataOnly)
	{
		/* Dump Sequence Comments */
		resetPQExpBuffer(query);
7703
		appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
7704
		dumpComment(fout, query->data,
7705
					tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
7706
					tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
7707
	}
7708

7709 7710
	PQclear(res);

7711 7712
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
7713
}
7714

7715
static void
7716
dumpTrigger(Archive *fout, TriggerInfo *tginfo)
7717
{
7718
	TableInfo  *tbinfo = tginfo->tgtable;
7719 7720
	PQExpBuffer query;
	PQExpBuffer delqry;
7721 7722
	const char *p;
	int			findx;
7723

7724
	if (dataOnly)
7725 7726
		return;

7727 7728
	query = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
7729

7730
	/*
7731
	 * DROP must be fully qualified in case same name appears in pg_catalog
7732 7733
	 */
	appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
7734
					  fmtId(tginfo->dobj.name));
7735
	appendPQExpBuffer(delqry, "ON %s.",
7736
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7737
	appendPQExpBuffer(delqry, "%s;\n",
7738
					  fmtId(tbinfo->dobj.name));
7739

7740 7741 7742
	if (tginfo->tgisconstraint)
	{
		appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
7743
		appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
7744 7745 7746 7747
	}
	else
	{
		appendPQExpBuffer(query, "CREATE TRIGGER ");
7748
		appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
7749 7750
	}
	appendPQExpBuffer(query, "\n    ");
7751

7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778
	/* 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",
7779
					  fmtId(tbinfo->dobj.name));
7780

7781 7782 7783 7784 7785 7786 7787 7788
	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);
7789
			else
7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800
				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");
	}
7801

7802 7803 7804 7805
	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
7806

7807 7808 7809 7810 7811 7812 7813
	/* 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));
7814

7815 7816 7817
	p = tginfo->tgargs;
	for (findx = 0; findx < tginfo->tgnargs; findx++)
	{
7818 7819
		const char *s = p,
				   *s2 = p;
7820

7821
		/* Set 'p' to end of arg string. marked by '\000' */
7822 7823 7824 7825
		for (;;)
		{
			p = strchr(p, '\\');
			if (p == NULL)
7826
			{
7827 7828
				write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
						  tginfo->tgargs,
7829 7830
						  tginfo->dobj.name,
						  tbinfo->dobj.name);
7831
				exit_nicely();
7832
			}
7833
			p++;
7834
			if (*p == '\\')		/* is it '\\'? */
7835
			{
7836 7837
				p++;
				continue;
7838
			}
7839
			if (p[0] == '0' && p[1] == '0' && p[2] == '0')		/* is it '\000'? */
7840 7841 7842
				break;
		}
		p--;
7843 7844 7845 7846

		while (s2 < p)
			if (*s2++ == '\\')
			{
7847
				appendPQExpBufferChar(query, ESCAPE_STRING_SYNTAX);
7848 7849 7850
				break;
			}

7851 7852 7853
		appendPQExpBufferChar(query, '\'');
		while (s < p)
		{
7854
			if (*s == '\'')		/* bytea already doubles backslashes */
7855
				appendPQExpBufferChar(query, '\'');
7856 7857 7858 7859 7860 7861 7862 7863
			appendPQExpBufferChar(query, *s++);
		}
		appendPQExpBufferChar(query, '\'');
		appendPQExpBuffer(query,
						  (findx < tginfo->tgnargs - 1) ? ", " : "");
		p = p + 4;
	}
	appendPQExpBuffer(query, ");\n");
7864

7865 7866 7867 7868 7869 7870 7871 7872
	if (!tginfo->tgenabled)
	{
		appendPQExpBuffer(query, "\nALTER TABLE %s ",
						  fmtId(tbinfo->dobj.name));
		appendPQExpBuffer(query, "DISABLE TRIGGER %s;\n",
						  fmtId(tginfo->dobj.name));
	}

7873
	ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
7874 7875
				 tginfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7876
				 NULL,
7877
				 tbinfo->rolname, false,
7878 7879 7880
				 "TRIGGER", query->data, delqry->data, NULL,
				 tginfo->dobj.dependencies, tginfo->dobj.nDeps,
				 NULL, NULL);
7881

7882 7883
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "TRIGGER %s ",
7884
					  fmtId(tginfo->dobj.name));
7885
	appendPQExpBuffer(query, "ON %s",
7886
					  fmtId(tbinfo->dobj.name));
7887

7888
	dumpComment(fout, query->data,
7889
				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
7890
				tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
7891

7892 7893 7894
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
}
7895

7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907
/*
 * dumpRule
 *		Dump a rule
 */
static void
dumpRule(Archive *fout, RuleInfo *rinfo)
{
	TableInfo  *tbinfo = rinfo->ruletable;
	PQExpBuffer query;
	PQExpBuffer cmd;
	PQExpBuffer delcmd;
	PGresult   *res;
7908

7909 7910 7911 7912 7913
	/*
	 * Ignore rules for not-to-be-dumped tables
	 */
	if (tbinfo == NULL || !tbinfo->dump || dataOnly)
		return;
7914

7915
	/*
7916 7917
	 * 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.
7918
	 */
7919
	if (!rinfo->separate)
7920
		return;
7921

7922 7923 7924
	/*
	 * Make sure we are in proper schema.
	 */
7925
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7926

7927 7928 7929
	query = createPQExpBuffer();
	cmd = createPQExpBuffer();
	delcmd = createPQExpBuffer();
7930

7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941
	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",
7942
						  rinfo->dobj.name);
7943
	}
7944

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

7948 7949 7950
	if (PQntuples(res) != 1)
	{
		write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
7951
				  rinfo->dobj.name, tbinfo->dobj.name);
7952
		exit_nicely();
7953
	}
7954

7955 7956 7957
	printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

	/*
7958
	 * DROP must be fully qualified in case same name appears in pg_catalog
7959 7960
	 */
	appendPQExpBuffer(delcmd, "DROP RULE %s ",
7961
					  fmtId(rinfo->dobj.name));
7962
	appendPQExpBuffer(delcmd, "ON %s.",
7963
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7964
	appendPQExpBuffer(delcmd, "%s;\n",
7965
					  fmtId(tbinfo->dobj.name));
7966 7967

	ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
7968 7969
				 rinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7970
				 NULL,
7971
				 tbinfo->rolname, false,
7972 7973 7974 7975 7976 7977 7978
				 "RULE", cmd->data, delcmd->data, NULL,
				 rinfo->dobj.dependencies, rinfo->dobj.nDeps,
				 NULL, NULL);

	/* Dump rule comments */
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "RULE %s",
7979
					  fmtId(rinfo->dobj.name));
7980
	appendPQExpBuffer(query, " ON %s",
7981
					  fmtId(tbinfo->dobj.name));
7982
	dumpComment(fout, query->data,
7983
				tbinfo->dobj.namespace->dobj.name,
7984
				tbinfo->rolname,
7985 7986 7987 7988
				rinfo->dobj.catId, 0, rinfo->dobj.dumpId);

	PQclear(res);

7989
	destroyPQExpBuffer(query);
7990 7991
	destroyPQExpBuffer(cmd);
	destroyPQExpBuffer(delcmd);
7992
}
7993

7994 7995 7996
/*
 * getDependencies --- obtain available dependency data
 */
7997
static void
7998
getDependencies(void)
7999
{
8000
	PQExpBuffer query;
Bruce Momjian's avatar
Bruce Momjian committed
8001
	PGresult   *res;
8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014
	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;
8015 8016

	if (g_verbose)
Peter Eisentraut's avatar
Peter Eisentraut committed
8017
		write_msg(NULL, "reading dependency data\n");
8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039

	/* 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");
8040 8041

	/*
8042
	 * Since we ordered the SELECT by referencing ID, we can expect that
8043 8044
	 * multiple entries for the same object will appear together; this saves
	 * on searches.
8045
	 */
8046 8047 8048
	dobj = NULL;

	for (i = 0; i < ntups; i++)
8049
	{
8050 8051 8052
		CatalogId	objId;
		CatalogId	refobjId;
		char		deptype;
8053

8054 8055 8056 8057 8058
		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));
8059

8060 8061 8062 8063
		if (dobj == NULL ||
			dobj->catId.tableoid != objId.tableoid ||
			dobj->catId.oid != objId.oid)
			dobj = findObjectByCatalogId(objId);
8064

8065
		/*
8066 8067
		 * Failure to find objects mentioned in pg_depend is not unexpected,
		 * since for example we don't collect info about TOAST tables.
8068
		 */
8069
		if (dobj == NULL)
8070
		{
8071 8072 8073 8074 8075
#ifdef NOT_USED
			fprintf(stderr, "no referencing object %u %u\n",
					objId.tableoid, objId.oid);
#endif
			continue;
8076 8077
		}

8078
		refdobj = findObjectByCatalogId(refobjId);
Bruce Momjian's avatar
Bruce Momjian committed
8079

8080
		if (refdobj == NULL)
8081
		{
8082 8083 8084 8085 8086
#ifdef NOT_USED
			fprintf(stderr, "no referenced object %u %u\n",
					refobjId.tableoid, refobjId.oid);
#endif
			continue;
Bruce Momjian's avatar
Bruce Momjian committed
8087 8088
		}

8089 8090
		/*
		 * Ordinarily, table rowtypes have implicit dependencies on their
8091 8092 8093 8094
		 * 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.
8095 8096 8097 8098 8099
		 */
		if (deptype == 'i' &&
			dobj->objType == DO_TABLE &&
			refdobj->objType == DO_TYPE)
			addObjectDependency(refdobj, dobj->dumpId);
8100 8101
		else
			/* normal case */
8102
			addObjectDependency(dobj, refdobj->dumpId);
8103
	}
8104

8105 8106
	PQclear(res);

8107
	destroyPQExpBuffer(query);
8108
}
8109

8110

8111 8112 8113 8114
/*
 * selectSourceSchema - make the specified schema the active search path
 * in the source database.
 *
8115 8116 8117 8118 8119 8120 8121
 * 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!
8122 8123 8124 8125
 */
static void
selectSourceSchema(const char *schemaName)
{
Bruce Momjian's avatar
Bruce Momjian committed
8126
	static char *curSchemaName = NULL;
8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140
	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",
8141
					  fmtId(schemaName));
8142 8143
	if (strcmp(schemaName, "pg_catalog") != 0)
		appendPQExpBuffer(query, ", pg_catalog");
8144

8145 8146 8147
	do_sql_command(g_conn, query->data);

	destroyPQExpBuffer(query);
8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160
	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 *
8161
getFormattedTypeName(Oid oid, OidOptions opts)
8162 8163 8164 8165 8166 8167
{
	char	   *result;
	PQExpBuffer query;
	PGresult   *res;
	int			ntups;

8168
	if (oid == 0)
8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180
	{
		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();
8181 8182
	if (g_fout->remoteVersion >= 70300)
	{
8183
		appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
8184 8185 8186
						  oid);
	}
	else if (g_fout->remoteVersion >= 70100)
8187
	{
8188
		appendPQExpBuffer(query, "SELECT format_type('%u'::oid, NULL)",
8189 8190 8191 8192 8193 8194
						  oid);
	}
	else
	{
		appendPQExpBuffer(query, "SELECT typname "
						  "FROM pg_type "
8195
						  "WHERE oid = '%u'::oid",
8196 8197 8198 8199
						  oid);
	}

	res = PQexec(g_conn, query->data);
8200
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
8201 8202 8203 8204 8205

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
8206
		write_msg(NULL, "query yielded %d rows instead of one: %s\n",
8207 8208 8209 8210
				  ntups, query->data);
		exit_nicely();
	}

8211 8212 8213 8214 8215 8216 8217 8218
	if (g_fout->remoteVersion >= 70100)
	{
		/* already quoted */
		result = strdup(PQgetvalue(res, 0, 0));
	}
	else
	{
		/* may need to quote it */
8219
		result = strdup(fmtId(PQgetvalue(res, 0, 0)));
8220
	}
8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234

	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
8235
	bool		isarray = false;
8236 8237
	PQExpBuffer buf = createPQExpBuffer();

8238 8239 8240 8241 8242 8243 8244
	/* Handle array types */
	if (typname[0] == '_')
	{
		isarray = true;
		typname++;
	}

8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277
	/* 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
8278

8279
	/*
8280 8281
	 * char is an internal single-byte data type; Let's make sure we force it
	 * through with quotes. - thomas 1998-12-13
8282
	 */
Bruce Momjian's avatar
Bruce Momjian committed
8283
	else if (strcmp(typname, "char") == 0)
8284
		appendPQExpBuffer(buf, "\"char\"");
8285
	else
8286
		appendPQExpBuffer(buf, "%s", fmtId(typname));
8287

8288 8289 8290 8291
	/* Append array qualifier for array types */
	if (isarray)
		appendPQExpBuffer(buf, "[]");

8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317
	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.",
8318
						  fmtId(schema));
8319 8320
	}
	appendPQExpBuffer(id_return, "%s",
8321
					  fmtId(id));
8322 8323 8324

	return id_return->data;
}
8325 8326

/*
8327 8328 8329
 * Return a column list clause for the given relation.
 *
 * Special case: if there are no undropped columns in the relation, return
8330
 * "", not an invalid "()" column list.
8331
 */
8332 8333
static const char *
fmtCopyColumnList(const TableInfo *ti)
8334 8335 8336
{
	static PQExpBuffer q = NULL;
	int			numatts = ti->numatts;
Bruce Momjian's avatar
Bruce Momjian committed
8337 8338 8339 8340
	char	  **attnames = ti->attnames;
	bool	   *attisdropped = ti->attisdropped;
	bool		needComma;
	int			i;
8341

Bruce Momjian's avatar
Bruce Momjian committed
8342
	if (q)						/* first time through? */
8343 8344 8345 8346
		resetPQExpBuffer(q);
	else
		q = createPQExpBuffer();

8347 8348
	appendPQExpBuffer(q, "(");
	needComma = false;
8349 8350
	for (i = 0; i < numatts; i++)
	{
8351 8352 8353
		if (attisdropped[i])
			continue;
		if (needComma)
8354 8355
			appendPQExpBuffer(q, ", ");
		appendPQExpBuffer(q, "%s", fmtId(attnames[i]));
8356
		needComma = true;
8357
	}
8358 8359

	if (!needComma)
8360
		return "";				/* no undropped columns */
8361

8362 8363 8364
	appendPQExpBuffer(q, ")");
	return q->data;
}
8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401

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