pg_dump.c 223 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
 *
Bruce Momjian's avatar
Bruce Momjian committed
7
 * Portions Copyright (c) 1996-2004, 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.389 2004/10/18 00:20:41 tgl Exp $
16
 *
17
 *-------------------------------------------------------------------------
18 19
 */

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

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

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

41
#include "getopt_long.h"
42 43

#ifndef HAVE_OPTRESET
Bruce Momjian's avatar
Bruce Momjian committed
44
int			optreset;
45 46
#endif

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

54 55
#include "commands/sequence.h"

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

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

64
#define _(x) gettext((x))
65

Bruce Momjian's avatar
Bruce Momjian committed
66
extern char *optarg;
67
extern int	optind,
Bruce Momjian's avatar
Bruce Momjian committed
68
			opterr;
69

70

71 72 73 74 75 76 77 78 79
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;


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

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

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 112 113
/* need the name of the database's default tablespace */
static char *dbDefaultTableSpace;

114
/* flag to turn on/off dollar quoting */
Bruce Momjian's avatar
Bruce Momjian committed
115
static int	disable_dollar_quoting = 0;
116

117

118 119 120 121 122 123
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
124 125
static int findComments(Archive *fout, Oid classoid, Oid objoid,
			 CommentItem **items);
126
static int	collectComments(Archive *fout, CommentItem **items);
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
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);

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);
157
static char *format_function_signature(FuncInfo *finfo, char **argnames,
Bruce Momjian's avatar
Bruce Momjian committed
158
						  bool honor_quotes);
159 160 161 162 163 164 165 166 167 168 169
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 setMaxOid(Archive *fout);
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);
static int	dumpBlobs(Archive *AH, void *arg);
static void dumpDatabase(Archive *AH);
170
static void dumpTimestamp(Archive *AH, char *msg);
171
static void dumpEncoding(Archive *AH);
172 173 174 175
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
176
				 ExecStatusType expected);
177 178


179 180
int
main(int argc, char **argv)
181
{
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;
	bool		oids = false;
190
	TableInfo  *tblinfo;
191
	int			numTables;
192 193 194
	DumpableObject **dobjs;
	int			numObjs;
	int			i;
195 196 197 198 199 200 201 202 203 204 205
	bool		force_password = false;
	int			compressLevel = -1;
	bool		ignore_version = false;
	int			plainText = 0;
	int			outputClean = 0;
	int			outputCreate = 0;
	int			outputBlobs = 0;
	int			outputNoOwner = 0;
	static int	use_setsessauth = 0;
	static int	disable_triggers = 0;
	char	   *outputSuperuser = NULL;
206

207
	RestoreOptions *ropt;
Bruce Momjian's avatar
Hi,  
Bruce Momjian committed
208

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	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
225
		{"schema", required_argument, NULL, 'n'},
226 227 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'},
		{"help", no_argument, NULL, '?'},
		{"version", no_argument, NULL, 'V'},
237

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

246 247 248
		{NULL, 0, NULL, 0}
	};
	int			optindex;
249

250
	set_pglocale_pgservice(argv[0], "pg_dump");
251

252
	g_verbose = false;
253

254 255 256
	strcpy(g_comment_start, "-- ");
	g_comment_end[0] = '\0';
	strcpy(g_opaque_type, "opaque");
257

258
	dataOnly = schemaOnly = dumpInserts = attrNames = false;
259

260
	progname = get_progname(argv[0]);
261

262 263
	/* Set default options based on progname */
	if (strcmp(progname, "pg_backup") == 0)
264
	{
265 266
		format = "c";
		outputBlobs = true;
267
	}
268 269

	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, "abcCdDf: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 293 294
			case 'b':			/* Dump blobs */
				outputBlobs = true;
				break;
295

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

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

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

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

315 316 317
			case 'f':
				filename = optarg;
				break;
318

319 320 321
			case 'F':
				format = optarg;
				break;
322

323 324 325
			case 'h':			/* server host */
				pghost = optarg;
				break;
326

327 328 329
			case 'i':			/* ignore database version mismatch */
				ignore_version = true;
				break;
330

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

335 336 337
			case 'o':			/* Dump oids */
				oids = true;
				break;
338

339 340 341
			case 'O':			/* Don't reconnect to match owner */
				outputNoOwner = 1;
				break;
342

343 344 345
			case 'p':			/* server port */
				pgport = optarg;
				break;
346

347 348
			case 'R':
				/* no-op, still accepted for backwards compatibility */
349
				break;
350

351 352 353
			case 's':			/* dump schema only */
				schemaOnly = true;
				break;
354

355 356 357 358
			case 'S':			/* Username for superuser in plain text
								 * output */
				outputSuperuser = strdup(optarg);
				break;
359

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

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

			case 'U':
				username = optarg;
				break;

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

			case 'W':
				force_password = true;
				break;

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

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

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

414 415
			case 0:
				break;
416

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

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

432
	/* Get database name from command line */
433 434
	if (optind < argc)
		dbname = argv[optind];
435

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

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

448
	if (outputBlobs && selectTableName != NULL)
449
	{
450 451
		write_msg(NULL, "large-object output not supported for a single table\n");
		write_msg(NULL, "use a full dump instead\n");
452 453
		exit(1);
	}
454

Bruce Momjian's avatar
Bruce Momjian committed
455 456
	if (outputBlobs && selectSchemaName != NULL)
	{
457 458
		write_msg(NULL, "large-object output not supported for a single schema\n");
		write_msg(NULL, "use a full dump instead\n");
Bruce Momjian's avatar
Bruce Momjian committed
459 460 461
		exit(1);
	}

462
	if (dumpInserts == true && oids == true)
463
	{
464
		write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together\n");
465
		write_msg(NULL, "(The INSERT command cannot set OIDs.)\n");
466 467 468
		exit(1);
	}

469 470
	if (outputBlobs == true && (format[0] == 'p' || format[0] == 'P'))
	{
471
		write_msg(NULL, "large-object output is not supported for plain-text dump files\n");
472 473 474
		write_msg(NULL, "(Use a different output format.)\n");
		exit(1);
	}
475

476 477 478 479 480 481 482
	/* open the output file */
	switch (format[0])
	{
		case 'c':
		case 'C':
			g_fout = CreateArchive(filename, archCustom, compressLevel);
			break;
483

484 485 486 487
		case 'f':
		case 'F':
			g_fout = CreateArchive(filename, archFiles, compressLevel);
			break;
488

489 490 491 492 493
		case 'p':
		case 'P':
			plainText = 1;
			g_fout = CreateArchive(filename, archNull, 0);
			break;
494

495 496 497 498
		case 't':
		case 'T':
			g_fout = CreateArchive(filename, archTar, compressLevel);
			break;
499

500
		default:
501
			write_msg(NULL, "invalid output format \"%s\" specified\n", format);
502 503
			exit(1);
	}
504

505 506
	if (g_fout == NULL)
	{
507
		write_msg(NULL, "could not open output file \"%s\" for writing\n", filename);
508 509
		exit(1);
	}
Bruce Momjian's avatar
Hi,  
Bruce Momjian committed
510

511 512
	/* Let the archiver know how noisy to be */
	g_fout->verbose = g_verbose;
513

514 515 516 517
	g_fout->minRemoteVersion = 70000;	/* we can handle back to 7.0 */
	g_fout->maxRemoteVersion = parse_version(PG_VERSION);
	if (g_fout->maxRemoteVersion < 0)
	{
518
		write_msg(NULL, "could not parse version string \"%s\"\n", PG_VERSION);
519 520 521
		exit(1);
	}

522 523 524 525
	/*
	 * Open the database using the Archiver, so it knows about it. Errors
	 * mean death.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
526 527
	g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport,
							 username, force_password, ignore_version);
528

529
	/*
530
	 * Start serializable transaction to dump consistent data.
531
	 */
532
	do_sql_command(g_conn, "BEGIN");
533

534
	do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
535

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

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

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

557 558 559 560 561 562 563 564 565
	/*
	 * 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);

566 567 568 569 570 571 572 573 574 575 576 577
	if (outputBlobs)
	{
		/* This is just a placeholder to allow correct sorting of blobs */
		DumpableObject *blobobj;

		blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
		blobobj->objType = DO_BLOBS;
		blobobj->catId = nilCatalogId;
		AssignDumpId(blobobj);
		blobobj->name = strdup("BLOBS");
	}

578 579 580 581 582 583 584
	/*
	 * Collect dependency data to assist in ordering the objects.
	 */
	getDependencies();

	/*
	 * Sort the objects into a safe dump order (no forward references).
585 586 587
	 *
	 * In 7.3 or later, we can rely on dependency information to help us
	 * determine a safe order, so the initial sort is mostly for cosmetic
Bruce Momjian's avatar
Bruce Momjian committed
588 589 590 591
	 * 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.
592 593 594
	 */
	getDumpableObjects(&dobjs, &numObjs);

595 596 597 598 599
	if (g_fout->remoteVersion >= 70300)
		sortDumpableObjectsByTypeName(dobjs, numObjs);
	else
		sortDumpableObjectsByTypeOid(dobjs, numObjs);

600 601 602
	sortDumpableObjects(dobjs, numObjs);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
603 604
	 * Create archive TOC entries for all the objects to be dumped, in a
	 * safe order.
605 606
	 */

607 608 609
	if (g_fout->verbose)
		dumpTimestamp(g_fout, "Started on");

610 611 612 613
	/* First the special encoding entry. */
	dumpEncoding(g_fout);

	/* The database item is always second. */
614 615
	if (!dataOnly)
		dumpDatabase(g_fout);
616

617
	/* Max OID is next. */
618 619
	if (oids == true)
		setMaxOid(g_fout);
620

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

625 626 627
	if (g_fout->verbose)
		dumpTimestamp(g_fout, "Completed on");

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

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

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

651 652
		RestoreArchive(g_fout, ropt);
	}
653

654
	CloseArchive(g_fout);
655

656
	PQfinish(g_conn);
657

658 659
	exit(0);
}
660 661


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

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

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

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

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

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

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

777 778 779 780 781
/*
 *	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.
 */
782

783
#define COPYBUFSIZ		8192
784

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

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

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

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

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

839
	copydone = false;
840

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

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

867 868 869
		/*
		 * THROTTLE:
		 *
Bruce Momjian's avatar
Bruce Momjian committed
870 871 872 873
		 * 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.
874
		 *
Bruce Momjian's avatar
Bruce Momjian committed
875 876 877 878
		 * Initial attempts based on sleeping for a number of ms for each ms
		 * of work were deemed too complex, then a simple 'sleep in each
		 * loop' implementation was suggested. The latter failed because
		 * the loop was too tight. Finally, the following was implemented:
879
		 *
Bruce Momjian's avatar
Bruce Momjian committed
880 881 882
		 * If throttle is non-zero, then See how long since the last sleep.
		 * Work out how long to sleep (based on ratio). If sleep is more
		 * than 100ms, then sleep reset timer EndIf EndIf
883
		 *
Bruce Momjian's avatar
Bruce Momjian committed
884 885
		 * where the throttle value was the number of ms to sleep per ms of
		 * work. The calculation was done in each loop.
886
		 *
Bruce Momjian's avatar
Bruce Momjian committed
887 888 889 890 891
		 * Most of the hard work is done in the backend, and this solution
		 * still did not work particularly well: on slow machines, the
		 * ratio was 50:1, and on medium paced machines, 1:1, and on fast
		 * multi-processor machines, it had little or no effect, for
		 * reasons that were unclear.
892 893 894
		 *
		 * Further discussion ensued, and the proposal was dropped.
		 *
Bruce Momjian's avatar
Bruce Momjian committed
895 896 897 898 899
		 * 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.
900 901 902
		 *
		 * select(0, NULL, NULL, NULL, &tvi);
		 *
Bruce Momjian's avatar
Bruce Momjian committed
903
		 * This will return after the interval specified in the structure
904
		 * tvi. Finally, call gettimeofday again to save the 'last sleep
Bruce Momjian's avatar
Bruce Momjian committed
905
		 * time'.
906
		 */
907
	}
908
	archprintf(fout, "\\.\n\n\n");
909

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

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

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

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

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

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

962 963 964
	do
	{
		PQclear(res);
965

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

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

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

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

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

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

1060
	PQclear(res);
1061

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

	do_sql_command(g_conn, "CLOSE _pg_dump_cursor");
1065

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

1070

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

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

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

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

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

1137
			tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
1138

1139
			tdinfo->dobj.objType = DO_TABLE_DATA;
Bruce Momjian's avatar
Bruce Momjian committed
1140

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

1157

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

	datname = PQdb(g_conn);
1183 1184

	if (g_verbose)
1185
		write_msg(NULL, "saving database definition\n");
1186

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

1190
	/* Get the database owner and parameters from pg_database */
1191
	if (g_fout->remoteVersion >= 80000)
1192
	{
1193
		appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
Bruce Momjian's avatar
Bruce Momjian committed
1194
		 "(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
1195
						  "pg_encoding_to_char(encoding) as encoding, "
1196 1197 1198 1199 1200 1201 1202 1203
						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace "
						  "FROM pg_database "
						  "WHERE datname = ");
		appendStringLiteral(dbQry, datname, true);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
Bruce Momjian's avatar
Bruce Momjian committed
1204
		 "(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
1205 1206
						  "pg_encoding_to_char(encoding) as encoding, "
						  "NULL as tablespace "
1207 1208 1209
						  "FROM pg_database "
						  "WHERE datname = ");
		appendStringLiteral(dbQry, datname, true);
1210
	}
1211 1212 1213 1214 1215
	else
	{
		appendPQExpBuffer(dbQry, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_database') AS tableoid, "
						  "oid, "
Bruce Momjian's avatar
Bruce Momjian committed
1216
		 "(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
1217
						  "pg_encoding_to_char(encoding) as encoding, "
1218
						  "NULL as tablespace "
1219 1220 1221 1222 1223 1224 1225
						  "FROM pg_database "
						  "WHERE datname = ");
		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 1256 1257
	/* save dattablespace name for later dump routines */
	dbDefaultTableSpace = strdup(tablespace);

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

	appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
1271
					  fmtId(datname));
1272

1273 1274 1275 1276 1277
	dbDumpId = createDumpId();

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

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

1297 1298
	PQclear(res);

1299 1300 1301
	destroyPQExpBuffer(dbQry);
	destroyPQExpBuffer(delQry);
	destroyPQExpBuffer(creaQry);
1302 1303 1304
}


1305 1306 1307 1308 1309 1310
/*
 * dumpTimestamp
 */
static void
dumpTimestamp(Archive *AH, char *msg)
{
Bruce Momjian's avatar
Bruce Momjian committed
1311 1312
	char		buf[256];
	time_t		now = time(NULL);
1313 1314

	if (strftime(buf, 256, "%Y-%m-%d %H:%M:%S %Z", localtime(&now)) != 0)
Bruce Momjian's avatar
Bruce Momjian committed
1315
	{
1316
		PQExpBuffer qry = createPQExpBuffer();
1317

1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
		appendPQExpBuffer(qry, "-- ");
		appendPQExpBuffer(qry, msg);
		appendPQExpBuffer(qry, " ");
		appendPQExpBuffer(qry, buf);
		appendPQExpBuffer(qry, "\n");

		ArchiveEntry(AH, nilCatalogId, createDumpId(),
					 "DUMP TIMESTAMP", NULL, "",
					 false, "DUMP TIMESTAMP", qry->data, "", NULL,
					 NULL, 0,
					 NULL, NULL);
		destroyPQExpBuffer(qry);
	}
}


1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
/*
 * 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(),
				 "ENCODING", NULL, "",
1366
				 false, "ENCODING", qry->data, "", NULL,
1367 1368 1369 1370 1371 1372 1373 1374 1375
				 NULL, 0,
				 NULL, NULL);

	PQclear(res);

	destroyPQExpBuffer(qry);
}


1376 1377 1378 1379 1380 1381
/*
 * dumpBlobs:
 *	dump all blobs
 *
 */

1382
#define loBufSize 16384
1383 1384
#define loFetchSize 1000

1385
static int
1386
dumpBlobs(Archive *AH, void *arg)
1387
{
1388 1389 1390 1391 1392 1393
	PQExpBuffer oidQry = createPQExpBuffer();
	PQExpBuffer oidFetchQry = createPQExpBuffer();
	PGresult   *res;
	int			i;
	int			loFd;
	char		buf[loBufSize];
1394
	int			cnt;
1395
	Oid			blobOid;
1396 1397

	if (g_verbose)
1398
		write_msg(NULL, "saving large objects\n");
1399

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

1403
	/* Cursor to get all BLOB tables */
1404
	if (AH->remoteVersion >= 70100)
1405
		appendPQExpBuffer(oidQry, "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject");
1406
	else
1407
		appendPQExpBuffer(oidQry, "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'");
1408 1409

	res = PQexec(g_conn, oidQry->data);
1410
	check_sql_result(res, g_conn, oidQry->data, PGRES_COMMAND_OK);
1411 1412

	/* Fetch for cursor */
1413
	appendPQExpBuffer(oidFetchQry, "FETCH %d IN bloboid", loFetchSize);
1414

1415 1416
	do
	{
1417 1418 1419
		/* Do a fetch */
		PQclear(res);

1420 1421
		res = PQexec(g_conn, oidFetchQry->data);
		check_sql_result(res, g_conn, oidFetchQry->data, PGRES_TUPLES_OK);
1422 1423 1424 1425

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

			StartBlob(AH, blobOid);

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

1449
				WriteData(AH, buf, cnt);
1450

1451
			} while (cnt > 0);
1452

1453
			lo_close(g_conn, loFd);
1454

1455
			EndBlob(AH, blobOid);
1456

1457 1458
		}
	} while (PQntuples(res) > 0);
1459

1460 1461
	destroyPQExpBuffer(oidQry);
	destroyPQExpBuffer(oidFetchQry);
1462

1463
	return 1;
1464 1465 1466
}

/*
1467 1468 1469
 * getNamespaces:
 *	  read all namespaces in the system catalogs and return them in the
 * NamespaceInfo* structure
1470
 *
1471
 *	numNamespaces is set to the number of namespaces read in
1472
 */
1473 1474
NamespaceInfo *
getNamespaces(int *numNamespaces)
1475
{
1476
	PGresult   *res;
1477 1478
	int			ntups;
	int			i;
1479 1480
	PQExpBuffer query;
	NamespaceInfo *nsinfo;
1481
	int			i_tableoid;
1482
	int			i_oid;
1483
	int			i_nspname;
1484
	int			i_usename;
1485
	int			i_nsptablespace;
1486
	int			i_nspacl;
1487 1488

	/*
1489 1490
	 * Before 7.3, there are no real namespaces; create two dummy entries,
	 * one for user stuff and one for system stuff.
1491
	 */
1492 1493 1494 1495
	if (g_fout->remoteVersion < 70300)
	{
		nsinfo = (NamespaceInfo *) malloc(2 * sizeof(NamespaceInfo));

1496 1497 1498 1499
		nsinfo[0].dobj.objType = DO_NAMESPACE;
		nsinfo[0].dobj.catId.tableoid = 0;
		nsinfo[0].dobj.catId.oid = 0;
		AssignDumpId(&nsinfo[0].dobj);
1500
		nsinfo[0].dobj.name = strdup("public");
1501 1502
		nsinfo[0].usename = strdup("");
		nsinfo[0].nspacl = strdup("");
1503
		nsinfo[0].nsptablespace = strdup("");
1504 1505 1506

		selectDumpableNamespace(&nsinfo[0]);

1507 1508 1509 1510
		nsinfo[1].dobj.objType = DO_NAMESPACE;
		nsinfo[1].dobj.catId.tableoid = 0;
		nsinfo[1].dobj.catId.oid = 1;
		AssignDumpId(&nsinfo[1].dobj);
1511
		nsinfo[1].dobj.name = strdup("pg_catalog");
1512 1513
		nsinfo[1].usename = strdup("");
		nsinfo[1].nspacl = strdup("");
1514
		nsinfo[0].nsptablespace = strdup("");
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527

		selectDumpableNamespace(&nsinfo[1]);

		g_namespaces = nsinfo;
		g_numNamespaces = *numNamespaces = 2;

		return nsinfo;
	}

	query = createPQExpBuffer();

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

1529 1530 1531 1532
	/*
	 * we fetch all namespaces including system ones, so that every object
	 * we read in can be linked to a containing namespace.
	 */
1533
	if (g_fout->remoteVersion >= 80000)
1534 1535
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
Bruce Momjian's avatar
Bruce Momjian committed
1536
						  "(select usename from pg_user where nspowner = usesysid) as usename, "
1537
						  "nspacl, "
Bruce Momjian's avatar
Bruce Momjian committed
1538
						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = nsptablespace) AS nsptablespace "
1539 1540 1541 1542 1543
						  "FROM pg_namespace");
	}
	else
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
Bruce Momjian's avatar
Bruce Momjian committed
1544
						  "(select usename from pg_user where nspowner = usesysid) as usename, "
1545 1546 1547
						  "nspacl, NULL AS nsptablespace "
						  "FROM pg_namespace");
	}
1548

Bruce Momjian's avatar
Bruce Momjian committed
1549
	res = PQexec(g_conn, query->data);
1550
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1551 1552 1553

	ntups = PQntuples(res);

1554
	nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
1555

1556
	i_tableoid = PQfnumber(res, "tableoid");
1557
	i_oid = PQfnumber(res, "oid");
1558
	i_nspname = PQfnumber(res, "nspname");
1559
	i_usename = PQfnumber(res, "usename");
1560
	i_nspacl = PQfnumber(res, "nspacl");
1561
	i_nsptablespace = PQfnumber(res, "nsptablespace");
1562 1563 1564

	for (i = 0; i < ntups; i++)
	{
1565 1566 1567 1568
		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);
1569
		nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
1570 1571
		nsinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
		nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
1572
		nsinfo[i].nsptablespace = strdup(PQgetvalue(res, i, i_nsptablespace));
1573

1574 1575
		/* Decide whether to dump this namespace */
		selectDumpableNamespace(&nsinfo[i]);
1576

1577
		if (strlen(nsinfo[i].usename) == 0)
1578
			write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
1579
					  nsinfo[i].dobj.name);
1580 1581
	}

Bruce Momjian's avatar
Bruce Momjian committed
1582
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1583 1584
	 * 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
1585 1586 1587 1588
	 */
	if (selectSchemaName)
	{
		for (i = 0; i < ntups; i++)
1589
			if (strcmp(nsinfo[i].dobj.name, selectSchemaName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1590 1591 1592 1593 1594
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
1595
			write_msg(NULL, "specified schema \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
1596 1597 1598 1599 1600
					  selectSchemaName);
			exit_nicely();
		}
	}

1601
	PQclear(res);
1602 1603
	destroyPQExpBuffer(query);

1604 1605
	g_namespaces = nsinfo;
	g_numNamespaces = *numNamespaces = ntups;
1606

1607
	return nsinfo;
1608 1609
}

1610 1611 1612 1613 1614 1615
/*
 * 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
1616
 * a system object or not.	In 7.3 and later there is no guessing.
1617 1618
 */
static NamespaceInfo *
1619
findNamespace(Oid nsoid, Oid objoid)
1620
{
1621
	int			i;
1622

1623
	if (g_fout->remoteVersion >= 70300)
1624
	{
1625
		for (i = 0; i < g_numNamespaces; i++)
1626
		{
Bruce Momjian's avatar
Bruce Momjian committed
1627
			NamespaceInfo *nsinfo = &g_namespaces[i];
1628

1629
			if (nsoid == nsinfo->dobj.catId.oid)
1630
				return nsinfo;
1631
		}
1632
		write_msg(NULL, "schema with OID %u does not exist\n", nsoid);
1633
		exit_nicely();
1634
	}
1635 1636 1637
	else
	{
		/* This code depends on the layout set up by getNamespaces. */
1638
		if (objoid > g_last_builtin_oid)
1639 1640 1641 1642
			i = 0;				/* user object */
		else
			i = 1;				/* system object */
		return &g_namespaces[i];
1643 1644
	}

1645
	return NULL;				/* keep compiler quiet */
1646
}
1647 1648

/*
1649 1650 1651
 * getTypes:
 *	  read all types in the system catalogs and return them in the
 * TypeInfo* structure
1652
 *
1653
 *	numTypes is set to the number of types read in
1654 1655 1656
 *
 * NB: this must run after getFuncs() because we assume we can do
 * findFuncByOid().
1657
 */
1658 1659
TypeInfo *
getTypes(int *numTypes)
1660
{
Bruce Momjian's avatar
Bruce Momjian committed
1661
	PGresult   *res;
1662 1663
	int			ntups;
	int			i;
1664
	PQExpBuffer query = createPQExpBuffer();
1665
	TypeInfo   *tinfo;
1666
	int			i_tableoid;
1667
	int			i_oid;
1668 1669
	int			i_typname;
	int			i_typnamespace;
1670
	int			i_usename;
1671 1672
	int			i_typinput;
	int			i_typoutput;
1673 1674
	int			i_typelem;
	int			i_typrelid;
Bruce Momjian's avatar
Bruce Momjian committed
1675
	int			i_typrelkind;
1676 1677
	int			i_typtype;
	int			i_typisdefined;
1678

1679 1680 1681 1682 1683
	/*
	 * we include even the built-in types because those may be used as
	 * array elements by user-defined types
	 *
	 * we filter out the built-in types when we dump out the types
1684 1685
	 *
	 * same approach for undefined (shell) types
1686
	 */
1687

1688 1689 1690 1691
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
1692
	{
1693
		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
1694 1695
						  "typnamespace, "
						  "(select usename from pg_user where typowner = usesysid) as usename, "
1696
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1697
					   "typoutput::oid as typoutput, typelem, typrelid, "
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708
						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
						  "typtype, typisdefined "
						  "FROM pg_type");
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
						  "0::oid as typnamespace, "
						  "(select usename from pg_user where typowner = usesysid) as usename, "
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1709
					   "typoutput::oid as typoutput, typelem, typrelid, "
1710 1711
						  "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
1712
						  "typtype, typisdefined "
1713
						  "FROM pg_type");
1714
	}
1715 1716
	else
	{
1717 1718 1719
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_type') AS tableoid, "
						  "oid, typname, "
1720 1721
						  "0::oid as typnamespace, "
						  "(select usename from pg_user where typowner = usesysid) as usename, "
1722
						  "typinput::oid as typinput, "
Bruce Momjian's avatar
Bruce Momjian committed
1723
					   "typoutput::oid as typoutput, typelem, typrelid, "
1724 1725
						  "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
1726
						  "typtype, typisdefined "
1727
						  "FROM pg_type");
1728
	}
1729

Bruce Momjian's avatar
Bruce Momjian committed
1730
	res = PQexec(g_conn, query->data);
1731
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1732 1733 1734

	ntups = PQntuples(res);

1735
	tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
1736

1737
	i_tableoid = PQfnumber(res, "tableoid");
1738
	i_oid = PQfnumber(res, "oid");
1739 1740
	i_typname = PQfnumber(res, "typname");
	i_typnamespace = PQfnumber(res, "typnamespace");
1741
	i_usename = PQfnumber(res, "usename");
1742 1743
	i_typinput = PQfnumber(res, "typinput");
	i_typoutput = PQfnumber(res, "typoutput");
1744 1745
	i_typelem = PQfnumber(res, "typelem");
	i_typrelid = PQfnumber(res, "typrelid");
Bruce Momjian's avatar
Bruce Momjian committed
1746
	i_typrelkind = PQfnumber(res, "typrelkind");
1747 1748
	i_typtype = PQfnumber(res, "typtype");
	i_typisdefined = PQfnumber(res, "typisdefined");
1749 1750 1751

	for (i = 0; i < ntups; i++)
	{
1752 1753 1754 1755 1756 1757 1758
		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);
1759 1760
		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
1761
												tinfo[i].dobj.catId.oid);
1762
		tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1763 1764 1765 1766
		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
1767
		tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
1768 1769
		tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);

1770 1771
		/*
		 * If it's a table's rowtype, use special type code to facilitate
Bruce Momjian's avatar
Bruce Momjian committed
1772
		 * sorting into the desired order.	(We don't want to consider it
1773 1774 1775 1776 1777 1778
		 * an ordinary type because that would bring the table up into the
		 * datatype part of the dump order.)
		 */
		if (OidIsValid(tinfo[i].typrelid) && tinfo[i].typrelkind != 'c')
			tinfo[i].dobj.objType = DO_TABLE_TYPE;

1779 1780 1781
		/*
		 * check for user-defined array types, omit system generated ones
		 */
1782
		if (OidIsValid(tinfo[i].typelem) &&
1783
			tinfo[i].dobj.name[0] != '_')
1784 1785 1786
			tinfo[i].isArray = true;
		else
			tinfo[i].isArray = false;
1787

1788 1789 1790 1791
		if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
			tinfo[i].isDefined = true;
		else
			tinfo[i].isDefined = false;
1792

1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
		/*
		 * If it's a domain, fetch info about its constraints, if any
		 */
		tinfo[i].nDomChecks = 0;
		tinfo[i].domChecks = NULL;
		if (tinfo[i].typtype == 'd')
			getDomainConstraints(&(tinfo[i]));

		/*
		 * Make sure there are dependencies from the type to its input and
Bruce Momjian's avatar
Bruce Momjian committed
1803 1804 1805
		 * 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.)
1806 1807 1808 1809 1810 1811 1812 1813 1814 1815
		 */
		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);

1816
		if (strlen(tinfo[i].usename) == 0 && tinfo[i].isDefined)
1817
			write_msg(NULL, "WARNING: owner of data type \"%s\" appears to be invalid\n",
1818
					  tinfo[i].dobj.name);
1819 1820
	}

1821 1822
	*numTypes = ntups;

1823 1824
	PQclear(res);

1825 1826
	destroyPQExpBuffer(query);

1827
	return tinfo;
1828 1829 1830
}

/*
1831 1832 1833
 * getOperators:
 *	  read all operators in the system catalogs and return them in the
 * OprInfo* structure
1834
 *
1835
 *	numOprs is set to the number of operators read in
1836
 */
1837 1838
OprInfo *
getOperators(int *numOprs)
1839
{
1840
	PGresult   *res;
1841 1842
	int			ntups;
	int			i;
1843
	PQExpBuffer query = createPQExpBuffer();
1844
	OprInfo    *oprinfo;
1845
	int			i_tableoid;
1846
	int			i_oid;
1847 1848
	int			i_oprname;
	int			i_oprnamespace;
1849
	int			i_usename;
1850
	int			i_oprcode;
1851

1852
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1853 1854
	 * find all operators, including builtin operators; we filter out
	 * system-defined operators at dump-out time.
1855
	 */
1856

1857 1858 1859 1860
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");

	if (g_fout->remoteVersion >= 70300)
1861
	{
1862
		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
1863 1864
						  "oprnamespace, "
						  "(select usename from pg_user where oprowner = usesysid) as usename, "
1865
						  "oprcode::oid as oprcode "
1866 1867 1868 1869 1870 1871 1872 1873 1874
						  "FROM pg_operator");
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
						  "0::oid as oprnamespace, "
						  "(select usename from pg_user where oprowner = usesysid) as usename, "
						  "oprcode::oid as oprcode "
						  "FROM pg_operator");
1875 1876 1877
	}
	else
	{
1878 1879 1880
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_operator') AS tableoid, "
						  "oid, oprname, "
1881 1882
						  "0::oid as oprnamespace, "
						  "(select usename from pg_user where oprowner = usesysid) as usename, "
1883
						  "oprcode::oid as oprcode "
1884
						  "FROM pg_operator");
1885
	}
1886

Bruce Momjian's avatar
Bruce Momjian committed
1887
	res = PQexec(g_conn, query->data);
1888
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1889 1890

	ntups = PQntuples(res);
1891
	*numOprs = ntups;
1892

1893
	oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
1894

1895
	i_tableoid = PQfnumber(res, "tableoid");
1896
	i_oid = PQfnumber(res, "oid");
1897 1898
	i_oprname = PQfnumber(res, "oprname");
	i_oprnamespace = PQfnumber(res, "oprnamespace");
1899
	i_usename = PQfnumber(res, "usename");
1900
	i_oprcode = PQfnumber(res, "oprcode");
1901 1902 1903

	for (i = 0; i < ntups; i++)
	{
1904 1905 1906 1907
		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);
1908 1909
		oprinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_oprname));
		oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
1910
											  oprinfo[i].dobj.catId.oid);
1911
		oprinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1912
		oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
1913

1914 1915
		if (strlen(oprinfo[i].usename) == 0)
			write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
1916
					  oprinfo[i].dobj.name);
1917 1918 1919 1920
	}

	PQclear(res);

1921
	destroyPQExpBuffer(query);
1922

1923
	return oprinfo;
1924 1925
}

1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
/*
 * 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
1940
	ConvInfo   *convinfo;
1941
	int			i_tableoid;
1942 1943 1944 1945 1946 1947
	int			i_oid;
	int			i_conname;
	int			i_connamespace;
	int			i_usename;

	/* Conversions didn't exist pre-7.3 */
Bruce Momjian's avatar
Bruce Momjian committed
1948 1949
	if (g_fout->remoteVersion < 70300)
	{
1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961
		*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");

1962
	appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
1963
					  "connamespace, "
Bruce Momjian's avatar
Bruce Momjian committed
1964
	"(select usename from pg_user where conowner = usesysid) as usename "
1965
					  "FROM pg_conversion");
1966 1967

	res = PQexec(g_conn, query->data);
1968
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
1969 1970 1971 1972 1973 1974

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

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

1975
	i_tableoid = PQfnumber(res, "tableoid");
1976 1977 1978 1979 1980 1981 1982
	i_oid = PQfnumber(res, "oid");
	i_conname = PQfnumber(res, "conname");
	i_connamespace = PQfnumber(res, "connamespace");
	i_usename = PQfnumber(res, "usename");

	for (i = 0; i < ntups; i++)
	{
1983 1984 1985 1986
		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);
1987 1988
		convinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
1989
											 convinfo[i].dobj.catId.oid);
1990 1991 1992 1993 1994 1995 1996 1997 1998 1999
		convinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return convinfo;
}

2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
/*
 * 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
2014
	OpclassInfo *opcinfo;
2015
	int			i_tableoid;
2016 2017 2018 2019 2020 2021
	int			i_oid;
	int			i_opcname;
	int			i_opcnamespace;
	int			i_usename;

	/*
Bruce Momjian's avatar
Bruce Momjian committed
2022 2023
	 * find all opclasses, including builtin opclasses; we filter out
	 * system-defined opclasses at dump-out time.
2024 2025 2026 2027 2028 2029 2030
	 */

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

	if (g_fout->remoteVersion >= 70300)
	{
2031
		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
2032 2033
						  "opcnamespace, "
						  "(select usename from pg_user where opcowner = usesysid) as usename "
2034 2035 2036 2037 2038 2039 2040 2041
						  "FROM pg_opclass");
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
						  "0::oid as opcnamespace, "
						  "''::name as usename "
						  "FROM pg_opclass");
2042 2043 2044
	}
	else
	{
2045 2046 2047
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_opclass') AS tableoid, "
						  "oid, opcname, "
2048 2049
						  "0::oid as opcnamespace, "
						  "''::name as usename "
2050
						  "FROM pg_opclass");
2051 2052 2053
	}

	res = PQexec(g_conn, query->data);
2054
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2055 2056 2057 2058 2059 2060

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

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

2061
	i_tableoid = PQfnumber(res, "tableoid");
2062 2063 2064 2065 2066 2067 2068
	i_oid = PQfnumber(res, "oid");
	i_opcname = PQfnumber(res, "opcname");
	i_opcnamespace = PQfnumber(res, "opcnamespace");
	i_usename = PQfnumber(res, "usename");

	for (i = 0; i < ntups; i++)
	{
2069 2070 2071 2072
		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);
2073 2074
		opcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_opcname));
		opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2075
											  opcinfo[i].dobj.catId.oid);
2076 2077 2078 2079 2080
		opcinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));

		if (g_fout->remoteVersion >= 70300)
		{
			if (strlen(opcinfo[i].usename) == 0)
2081
				write_msg(NULL, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
2082
						  opcinfo[i].dobj.name);
2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return opcinfo;
}

2093
/*
2094 2095 2096
 * getAggregates:
 *	  read all the user-defined aggregates in the system catalogs and
 * return them in the AggInfo* structure
2097
 *
2098
 * numAggs is set to the number of aggregates read in
2099
 */
2100 2101
AggInfo *
getAggregates(int *numAggs)
2102
{
2103
	PGresult   *res;
2104 2105
	int			ntups;
	int			i;
2106
	PQExpBuffer query = createPQExpBuffer();
2107
	AggInfo    *agginfo;
2108
	int			i_tableoid;
2109 2110 2111
	int			i_oid;
	int			i_aggname;
	int			i_aggnamespace;
2112
	int			i_aggbasetype;
2113
	int			i_usename;
2114
	int			i_aggacl;
2115

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

2119 2120 2121
	/* find all user-defined aggregates */

	if (g_fout->remoteVersion >= 70300)
2122
	{
2123
		appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
2124
						  "pronamespace as aggnamespace, "
2125
						  "proargtypes[0] as aggbasetype, "
2126 2127
						  "(select usename from pg_user where proowner = usesysid) as usename, "
						  "proacl as aggacl "
2128 2129 2130
						  "FROM pg_proc "
						  "WHERE proisagg "
						  "AND pronamespace != "
Bruce Momjian's avatar
Bruce Momjian committed
2131
		  "(select oid from pg_namespace where nspname = 'pg_catalog')");
2132
	}
2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, aggname, "
						  "0::oid as aggnamespace, "
						  "aggbasetype, "
						  "(select usename from pg_user where aggowner = usesysid) as usename, "
						  "'{=X}' as aggacl "
						  "FROM pg_aggregate "
						  "where oid > '%u'::oid",
						  g_last_builtin_oid);
	}
2144 2145
	else
	{
2146 2147 2148
		appendPQExpBuffer(query, "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_aggregate') AS tableoid, "
						  "oid, aggname, "
2149
						  "0::oid as aggnamespace, "
2150
						  "aggbasetype, "
2151
						  "(select usename from pg_user where aggowner = usesysid) as usename, "
2152
						  "'{=X}' as aggacl "
2153
						  "FROM pg_aggregate "
2154 2155
						  "where oid > '%u'::oid",
						  g_last_builtin_oid);
2156
	}
2157

Bruce Momjian's avatar
Bruce Momjian committed
2158
	res = PQexec(g_conn, query->data);
2159
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2160 2161

	ntups = PQntuples(res);
2162
	*numAggs = ntups;
2163

2164
	agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
2165

2166
	i_tableoid = PQfnumber(res, "tableoid");
2167 2168 2169
	i_oid = PQfnumber(res, "oid");
	i_aggname = PQfnumber(res, "aggname");
	i_aggnamespace = PQfnumber(res, "aggnamespace");
2170
	i_aggbasetype = PQfnumber(res, "aggbasetype");
2171
	i_usename = PQfnumber(res, "usename");
2172
	i_aggacl = PQfnumber(res, "aggacl");
2173 2174 2175

	for (i = 0; i < ntups; i++)
	{
2176 2177 2178 2179
		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);
2180 2181
		agginfo[i].aggfn.dobj.name = strdup(PQgetvalue(res, i, i_aggname));
		agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2182
										agginfo[i].aggfn.dobj.catId.oid);
2183 2184
		agginfo[i].aggfn.usename = strdup(PQgetvalue(res, i, i_usename));
		if (strlen(agginfo[i].aggfn.usename) == 0)
2185
			write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
2186
					  agginfo[i].aggfn.dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
2187 2188
		agginfo[i].aggfn.lang = InvalidOid;		/* not currently
												 * interesting */
2189 2190 2191
		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
2192
		agginfo[i].aggfn.prorettype = InvalidOid;		/* not saved */
2193
		agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
2194
		agginfo[i].anybasetype = false; /* computed when it's dumped */
Bruce Momjian's avatar
Bruce Momjian committed
2195
		agginfo[i].fmtbasetype = NULL;	/* computed when it's dumped */
2196
	}
2197

2198
	PQclear(res);
2199

2200
	destroyPQExpBuffer(query);
2201

2202 2203
	return agginfo;
}
2204

2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219
/*
 * 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;
2220
	int			i_tableoid;
2221 2222 2223 2224 2225 2226 2227 2228
	int			i_oid;
	int			i_proname;
	int			i_pronamespace;
	int			i_usename;
	int			i_prolang;
	int			i_pronargs;
	int			i_proargtypes;
	int			i_prorettype;
2229
	int			i_proacl;
2230

2231 2232
	/* Make sure we are in proper schema */
	selectSourceSchema("pg_catalog");
Bruce Momjian's avatar
Bruce Momjian committed
2233

2234
	/* find all user-defined funcs */
2235

2236 2237 2238
	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query,
2239
						  "SELECT tableoid, oid, proname, prolang, "
2240
						  "pronargs, proargtypes, prorettype, proacl, "
2241 2242 2243 2244 2245
						  "pronamespace, "
						  "(select usename from pg_user where proowner = usesysid) as usename "
						  "FROM pg_proc "
						  "WHERE NOT proisagg "
						  "AND pronamespace != "
Bruce Momjian's avatar
Bruce Momjian committed
2246
		  "(select oid from pg_namespace where nspname = 'pg_catalog')");
2247
	}
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, proname, prolang, "
						  "pronargs, proargtypes, prorettype, "
						  "'{=X}' as proacl, "
						  "0::oid as pronamespace, "
						  "(select usename from pg_user where proowner = usesysid) as usename "
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
						  g_last_builtin_oid);
	}
2260 2261 2262
	else
	{
		appendPQExpBuffer(query,
2263 2264 2265
						  "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_proc') AS tableoid, "
						  "oid, proname, prolang, "
2266
						  "pronargs, proargtypes, prorettype, "
2267
						  "'{=X}' as proacl, "
2268 2269 2270 2271 2272 2273
						  "0::oid as pronamespace, "
						  "(select usename from pg_user where proowner = usesysid) as usename "
						  "FROM pg_proc "
						  "where pg_proc.oid > '%u'::oid",
						  g_last_builtin_oid);
	}
2274

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

2278
	ntups = PQntuples(res);
Bruce Momjian's avatar
Bruce Momjian committed
2279

2280
	*numFuncs = ntups;
2281

2282
	finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
2283

2284
	i_tableoid = PQfnumber(res, "tableoid");
2285 2286 2287 2288 2289 2290 2291 2292
	i_oid = PQfnumber(res, "oid");
	i_proname = PQfnumber(res, "proname");
	i_pronamespace = PQfnumber(res, "pronamespace");
	i_usename = PQfnumber(res, "usename");
	i_prolang = PQfnumber(res, "prolang");
	i_pronargs = PQfnumber(res, "pronargs");
	i_proargtypes = PQfnumber(res, "proargtypes");
	i_prorettype = PQfnumber(res, "prorettype");
2293
	i_proacl = PQfnumber(res, "proacl");
2294

2295 2296
	for (i = 0; i < ntups; i++)
	{
2297 2298 2299 2300
		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);
2301 2302
		finfo[i].dobj.name = strdup(PQgetvalue(res, i, i_proname));
		finfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2303
												finfo[i].dobj.catId.oid);
2304 2305
		finfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
		finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
2306
		finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
2307
		finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
2308 2309 2310
		finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
		if (finfo[i].nargs == 0)
			finfo[i].argtypes = NULL;
2311
		else
2312
		{
2313 2314 2315
			finfo[i].argtypes = (Oid *) malloc(finfo[i].nargs * sizeof(Oid));
			parseOidArray(PQgetvalue(res, i, i_proargtypes),
						  finfo[i].argtypes, finfo[i].nargs);
2316
		}
2317

2318 2319
		if (strlen(finfo[i].usename) == 0)
			write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n",
2320
					  finfo[i].dobj.name);
2321
	}
2322

2323
	PQclear(res);
2324

2325
	destroyPQExpBuffer(query);
2326

2327 2328
	return finfo;
}
2329

2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346
/*
 * 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;
2347
	int			i_reltableoid;
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358
	int			i_reloid;
	int			i_relname;
	int			i_relnamespace;
	int			i_relkind;
	int			i_relacl;
	int			i_usename;
	int			i_relchecks;
	int			i_reltriggers;
	int			i_relhasindex;
	int			i_relhasrules;
	int			i_relhasoids;
2359 2360
	int			i_owning_tab;
	int			i_owning_col;
2361
	int			i_reltablespace;
2362

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

2366 2367 2368
	/*
	 * Find all the tables (including views and sequences).
	 *
Bruce Momjian's avatar
Bruce Momjian committed
2369 2370
	 * We include system catalogs, so that we can work if a user table is
	 * defined to inherit from a system catalog (pretty weird, but...)
2371 2372 2373 2374 2375
	 *
	 * We ignore tables that are not type 'r' (ordinary relation) or 'S'
	 * (sequence) or 'v' (view).
	 *
	 * Note: in this phase we should collect only a minimal amount of
Bruce Momjian's avatar
Bruce Momjian committed
2376 2377
	 * information about each table, basically just enough to decide if it
	 * is interesting.	We must fetch all tables in this phase because
2378 2379
	 * otherwise we cannot correctly identify inherited columns, serial
	 * columns, etc.
2380
	 */
2381

2382
	if (g_fout->remoteVersion >= 80000)
2383
	{
2384 2385 2386 2387
		/*
		 * Left join to pick up dependency info linking sequences to their
		 * serial column, if any
		 */
2388
		appendPQExpBuffer(query,
2389 2390
						  "SELECT c.tableoid, c.oid, relname, "
						  "relacl, relkind, relnamespace, "
2391 2392
						  "(select usename from pg_user where relowner = usesysid) as usename, "
						  "relchecks, reltriggers, "
2393 2394
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
2395 2396 2397 2398 2399 2400 2401 2402
						  "d.refobjsubid as owning_col, "
						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace "
						  "from pg_class c "
						  "left join pg_depend d on "
						  "(c.relkind = '%c' and "
						"d.classid = c.tableoid and d.objid = c.oid and "
						  "d.objsubid = 0 and "
						"d.refclassid = c.tableoid and d.deptype = 'i') "
2403
						  "where relkind in ('%c', '%c', '%c') "
2404 2405
						  "order by c.oid",
						  RELKIND_SEQUENCE,
2406
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422
	}
	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, "
						  "(select usename from pg_user where relowner = usesysid) as usename, "
						  "relchecks, reltriggers, "
						  "relhasindex, relhasrules, relhasoids, "
						  "d.refobjid as owning_tab, "
						  "d.refobjsubid as owning_col, "
						  "NULL as reltablespace "
2423 2424 2425
						  "from pg_class c "
						  "left join pg_depend d on "
						  "(c.relkind = '%c' and "
Bruce Momjian's avatar
Bruce Momjian committed
2426
						"d.classid = c.tableoid and d.objid = c.oid and "
2427
						  "d.objsubid = 0 and "
Bruce Momjian's avatar
Bruce Momjian committed
2428
						"d.refclassid = c.tableoid and d.deptype = 'i') "
2429
						  "where relkind in ('%c', '%c', '%c') "
2430 2431
						  "order by c.oid",
						  RELKIND_SEQUENCE,
2432
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2433 2434 2435 2436
	}
	else if (g_fout->remoteVersion >= 70200)
	{
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
2437
					   "SELECT tableoid, oid, relname, relacl, relkind, "
2438 2439 2440
						  "0::oid as relnamespace, "
						  "(select usename from pg_user where relowner = usesysid) as usename, "
						  "relchecks, reltriggers, "
2441 2442
						  "relhasindex, relhasrules, relhasoids, "
						  "NULL::oid as owning_tab, "
2443 2444
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2445 2446 2447 2448 2449 2450 2451 2452 2453
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* all tables have oids in 7.1 */
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
2454
					   "SELECT tableoid, oid, relname, relacl, relkind, "
2455 2456 2457
						  "0::oid as relnamespace, "
						  "(select usename from pg_user where relowner = usesysid) as usename, "
						  "relchecks, reltriggers, "
2458 2459 2460
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2461 2462
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474
						  "from pg_class "
						  "where relkind in ('%c', '%c', '%c') "
						  "order by oid",
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
	}
	else
	{
		/*
		 * Before 7.1, view relkind was not set to 'v', so we must check
		 * if we have a view by looking for a rule in pg_rewrite.
		 */
		appendPQExpBuffer(query,
2475 2476 2477
						  "SELECT "
						  "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
						  "oid, relname, relacl, "
2478 2479 2480 2481 2482 2483 2484 2485
						  "CASE WHEN relhasrules and relkind = 'r' "
				  "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
				  "             r.ev_class = c.oid AND r.ev_type = '1') "
						  "THEN '%c'::\"char\" "
						  "ELSE relkind END AS relkind,"
						  "0::oid as relnamespace, "
						  "(select usename from pg_user where relowner = usesysid) as usename, "
						  "relchecks, reltriggers, "
2486 2487 2488
						  "relhasindex, relhasrules, "
						  "'t'::bool as relhasoids, "
						  "NULL::oid as owning_tab, "
2489 2490
						  "NULL::int4 as owning_col, "
						  "NULL as reltablespace "
2491 2492 2493 2494 2495 2496
						  "from pg_class c "
						  "where relkind in ('%c', '%c') "
						  "order by oid",
						  RELKIND_VIEW,
						  RELKIND_RELATION, RELKIND_SEQUENCE);
	}
2497

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

2501
	ntups = PQntuples(res);
2502

2503
	*numTables = ntups;
2504

2505 2506 2507 2508 2509 2510 2511 2512 2513
	/*
	 * Extract data from result and lock dumpable tables.  We do the
	 * locking before anything else, to minimize the window wherein a
	 * table could disappear under us.
	 *
	 * Note that we have to save info about all tables here, even when
	 * dumping only one, because we don't yet know which tables might be
	 * inheritance ancestors of the target table.
	 */
2514
	tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
2515

2516
	i_reltableoid = PQfnumber(res, "tableoid");
2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
	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");
	i_usename = PQfnumber(res, "usename");
	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");
2528 2529
	i_owning_tab = PQfnumber(res, "owning_tab");
	i_owning_col = PQfnumber(res, "owning_col");
2530
	i_reltablespace = PQfnumber(res, "reltablespace");
2531

2532 2533
	for (i = 0; i < ntups; i++)
	{
2534 2535 2536 2537
		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);
2538 2539
		tblinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_relname));
		tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)),
Bruce Momjian's avatar
Bruce Momjian committed
2540
											  tblinfo[i].dobj.catId.oid);
2541 2542 2543 2544 2545 2546 2547 2548
		tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
		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));
2549 2550
		if (PQgetisnull(res, i, i_owning_tab))
		{
2551
			tblinfo[i].owning_tab = InvalidOid;
2552 2553 2554 2555
			tblinfo[i].owning_col = 0;
		}
		else
		{
2556
			tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
2557 2558
			tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
		}
2559
		tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
2560

2561
		/* other fields were zeroed above */
Bruce Momjian's avatar
Bruce Momjian committed
2562

2563
		/*
Bruce Momjian's avatar
Bruce Momjian committed
2564 2565
		 * Decide whether we want to dump this table.  Sequences owned by
		 * serial columns are never dumpable on their own; we will
2566
		 * transpose their owning table's dump flag to them below.
2567
		 */
2568
		if (OidIsValid(tblinfo[i].owning_tab))
2569
			tblinfo[i].dump = false;
2570 2571
		else
			selectDumpableTable(&tblinfo[i]);
2572
		tblinfo[i].interesting = tblinfo[i].dump;
2573

2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589
		/*
		 * Read-lock target tables to make sure they aren't DROPPED or
		 * altered in schema before we get around to dumping them.
		 *
		 * Note that we don't explicitly lock parents of the target tables;
		 * we assume our lock on the child is enough to prevent schema
		 * alterations to parent tables.
		 *
		 * NOTE: it'd be kinda nice to lock views and sequences too, not only
		 * plain tables, but the backend doesn't presently allow that.
		 */
		if (tblinfo[i].dump && tblinfo[i].relkind == RELKIND_RELATION)
		{
			resetPQExpBuffer(lockquery);
			appendPQExpBuffer(lockquery,
							  "LOCK TABLE %s IN ACCESS SHARE MODE",
Bruce Momjian's avatar
Bruce Momjian committed
2590 2591
					 fmtQualifiedId(tblinfo[i].dobj.namespace->dobj.name,
									tblinfo[i].dobj.name));
2592
			do_sql_command(g_conn, lockquery->data);
2593
		}
2594

2595 2596 2597
		/* Emit notice if join for owner failed */
		if (strlen(tblinfo[i].usename) == 0)
			write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
2598
					  tblinfo[i].dobj.name);
2599 2600
	}

Bruce Momjian's avatar
Bruce Momjian committed
2601
	/*
Bruce Momjian's avatar
Bruce Momjian committed
2602
	 * If the user is attempting to dump a specific table, check to ensure
Bruce Momjian's avatar
Bruce Momjian committed
2603 2604 2605
	 * 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
2606 2607 2608 2609
	 */
	if (selectTableName)
	{
		for (i = 0; i < ntups; i++)
2610
			if (strcmp(tblinfo[i].dobj.name, selectTableName) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
2611 2612 2613 2614 2615
				break;

		/* Didn't find a match */
		if (i == ntups)
		{
2616
			write_msg(NULL, "specified table \"%s\" does not exist\n",
Bruce Momjian's avatar
Bruce Momjian committed
2617 2618 2619 2620 2621
					  selectTableName);
			exit_nicely();
		}
	}

2622
	PQclear(res);
2623 2624
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
2625
	destroyPQExpBuffer(lockquery);
2626

2627
	return tblinfo;
2628 2629 2630 2631
}

/*
 * getInherits
2632
 *	  read all the inheritance information
2633 2634
 * from the system catalogs return them in the InhInfo* structure
 *
2635
 * numInherits is set to the number of pairs read in
2636
 */
2637
InhInfo *
2638 2639
getInherits(int *numInherits)
{
2640
	PGresult   *res;
2641 2642
	int			ntups;
	int			i;
2643 2644
	PQExpBuffer query = createPQExpBuffer();
	InhInfo    *inhinfo;
2645

2646
	int			i_inhrelid;
2647
	int			i_inhparent;
2648

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

2652 2653
	/* find all the inheritance information */

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

Bruce Momjian's avatar
Bruce Momjian committed
2656
	res = PQexec(g_conn, query->data);
2657
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
2658 2659 2660 2661 2662 2663 2664

	ntups = PQntuples(res);

	*numInherits = ntups;

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

2665
	i_inhrelid = PQfnumber(res, "inhrelid");
2666 2667 2668 2669
	i_inhparent = PQfnumber(res, "inhparent");

	for (i = 0; i < ntups; i++)
	{
2670 2671
		inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
		inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
2672 2673 2674
	}

	PQclear(res);
2675 2676 2677

	destroyPQExpBuffer(query);

2678
	return inhinfo;
2679 2680 2681
}

/*
2682 2683
 * getIndexes
 *	  get information about every index on a dumpable table
2684
 *
2685 2686
 * Note: index data is not returned directly to the caller, but it
 * does get entered into the DumpableObject tables.
2687 2688
 */
void
2689
getIndexes(TableInfo tblinfo[], int numTables)
2690
{
2691
	int			i,
2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706
				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,
2707 2708
				i_conoid,
				i_tablespace;
2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723
	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",
2724
					  tbinfo->dobj.name);
2725 2726

		/* Make sure we are in proper schema so indexdef is right */
2727
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
2728 2729 2730 2731

		/*
		 * The point of the messy-looking outer join is to find a
		 * constraint that is related by an internal dependency link to
Bruce Momjian's avatar
Bruce Momjian committed
2732 2733 2734
		 * 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.
2735 2736
		 */
		resetPQExpBuffer(query);
2737
		if (g_fout->remoteVersion >= 80000)
2738 2739 2740 2741 2742 2743 2744 2745 2746
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
				 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
2747
							  "c.oid as conoid, "
Bruce Momjian's avatar
Bruce Momjian committed
2748
							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace "
2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773
							  "FROM pg_catalog.pg_index i "
				  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
							  "LEFT JOIN pg_catalog.pg_depend d "
							  "ON (d.classid = t.tableoid "
							  "AND d.objid = t.oid "
							  "AND d.deptype = 'i') "
							  "LEFT JOIN pg_catalog.pg_constraint c "
							  "ON (d.refclassid = c.tableoid "
							  "AND d.refobjid = c.oid) "
							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70300)
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
				 "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
							  "t.relnatts as indnkeys, "
							  "i.indkey, i.indisclustered, "
							  "c.contype, c.conname, "
							  "c.tableoid as contableoid, "
							  "c.oid as conoid, "
							  "NULL as tablespace "
2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791
							  "FROM pg_catalog.pg_index i "
				  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
							  "LEFT JOIN pg_catalog.pg_depend d "
							  "ON (d.classid = t.tableoid "
							  "AND d.objid = t.oid "
							  "AND d.deptype = 'i') "
							  "LEFT JOIN pg_catalog.pg_constraint c "
							  "ON (d.refclassid = c.tableoid "
							  "AND d.refobjid = c.oid) "
							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70100)
		{
			appendPQExpBuffer(query,
							  "SELECT t.tableoid, t.oid, "
							  "t.relname as indexname, "
Bruce Momjian's avatar
Bruce Momjian committed
2792
							"pg_get_indexdef(i.indexrelid) as indexdef, "
2793 2794 2795 2796 2797 2798
							  "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, "
2799 2800
							  "t.oid as conoid, "
							  "NULL as tablespace "
2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813
							  "FROM pg_index i, pg_class t "
							  "WHERE t.oid = i.indexrelid "
							  "AND i.indrelid = '%u'::oid "
							  "ORDER BY indexname",
							  tbinfo->dobj.catId.oid);
		}
		else
		{
			appendPQExpBuffer(query,
							  "SELECT "
							  "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
							  "t.oid, "
							  "t.relname as indexname, "
Bruce Momjian's avatar
Bruce Momjian committed
2814
							"pg_get_indexdef(i.indexrelid) as indexdef, "
2815 2816 2817 2818 2819 2820
							  "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, "
2821 2822
							  "t.oid as conoid, "
							  "NULL as tablespace "
2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845
							  "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");
2846
		i_tablespace = PQfnumber(res, "tablespace");
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858

		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);
2859 2860
			indxinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_indexname));
			indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
2861 2862 2863
			indxinfo[j].indextable = tbinfo;
			indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
			indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
2864
			indxinfo[j].tablespace = strdup(PQgetvalue(res, j, i_tablespace));
2865

2866 2867 2868
			/*
			 * In pre-7.4 releases, indkeys may contain more entries than
			 * indnkeys says (since indnkeys will be 1 for a functional
Bruce Momjian's avatar
Bruce Momjian committed
2869 2870 2871 2872 2873
			 * 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.
2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893
			 */
			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);
2894 2895
				constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
				constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
2896 2897 2898 2899 2900 2901 2902
				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;
2903

2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960
				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",
2961
					  tbinfo->dobj.name);
2962 2963 2964 2965 2966

		/*
		 * select table schema to ensure constraint expr is qualified if
		 * needed
		 */
2967
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
2968 2969 2970 2971

		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
						  "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
2972
						"pg_catalog.pg_get_constraintdef(oid) as condef "
2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994
						  "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);
2995 2996
			constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
			constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
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
			constrinfo[j].contable = tbinfo;
			constrinfo[j].condomain = NULL;
			constrinfo[j].contype = 'f';
			constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
			constrinfo[j].conindex = 0;
			constrinfo[j].coninherited = false;
			constrinfo[j].separate = true;
		}

		PQclear(res);
	}

	destroyPQExpBuffer(query);
}

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

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

	/*
Bruce Momjian's avatar
Bruce Momjian committed
3035 3036
	 * select appropriate schema to ensure names in constraint are
	 * properly qualified
3037
	 */
3038
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
3039 3040 3041 3042 3043

	query = createPQExpBuffer();

	if (g_fout->remoteVersion >= 70400)
		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
3044
						"pg_catalog.pg_get_constraintdef(oid) AS consrc "
3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077
						  "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);
3078 3079
		constrinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
		constrinfo[i].dobj.namespace = tinfo->dobj.namespace;
3080 3081 3082 3083 3084 3085 3086
		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
3087

3088
		/*
Bruce Momjian's avatar
Bruce Momjian committed
3089 3090
		 * Make the domain depend on the constraint, ensuring it won't be
		 * output till any constraint dependencies are OK.
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 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160
		 */
		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
3161
		Oid			ruletableoid;
3162 3163 3164 3165 3166

		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);
3167
		ruleinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_rulename));
3168 3169
		ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
		ruleinfo[i].ruletable = findTableByOid(ruletableoid);
3170
		ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
3171 3172 3173 3174 3175
		ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
		ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
		if (ruleinfo[i].ruletable)
		{
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3176 3177 3178 3179
			 * 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.
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
			 */
			if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
				ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
				addObjectDependency(&ruleinfo[i].ruletable->dobj,
									ruleinfo[i].dobj.dumpId);
			else
				addObjectDependency(&ruleinfo[i].dobj,
									ruleinfo[i].ruletable->dobj.dumpId);
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return ruleinfo;
}

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

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

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

		if (g_verbose)
			write_msg(NULL, "reading triggers for table \"%s\"\n",
3237
					  tbinfo->dobj.name);
3238 3239 3240 3241 3242

		/*
		 * select table schema to ensure regproc name is qualified if
		 * needed
		 */
3243
		selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256

		resetPQExpBuffer(query);
		if (g_fout->remoteVersion >= 70300)
		{
			/*
			 * We ignore triggers that are tied to a foreign-key
			 * constraint
			 */
			appendPQExpBuffer(query,
							  "SELECT tgname, "
							  "tgfoid::pg_catalog.regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
Bruce Momjian's avatar
Bruce Momjian committed
3257
						 "tgconstrrelid, tginitdeferred, tableoid, oid, "
3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273
				 "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
							  "from pg_catalog.pg_trigger t "
							  "where tgrelid = '%u'::pg_catalog.oid "
							  "and (not tgisconstraint "
							  " OR NOT EXISTS"
							  "  (SELECT 1 FROM pg_catalog.pg_depend d "
							  "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
							  "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
							  tbinfo->dobj.catId.oid);
		}
		else if (g_fout->remoteVersion >= 70100)
		{
			appendPQExpBuffer(query,
							"SELECT tgname, tgfoid::regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
Bruce Momjian's avatar
Bruce Momjian committed
3274
						 "tgconstrrelid, tginitdeferred, tableoid, oid, "
3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308
			  "(select relname from pg_class where oid = tgconstrrelid) "
							  "		as tgconstrrelname "
							  "from pg_trigger "
							  "where tgrelid = '%u'::oid",
							  tbinfo->dobj.catId.oid);
		}
		else
		{
			appendPQExpBuffer(query,
							"SELECT tgname, tgfoid::regproc as tgfname, "
							  "tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
							  "tgconstrrelid, tginitdeferred, "
							  "(SELECT oid FROM pg_class WHERE relname = 'pg_trigger') AS tableoid, "

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

		ntups = PQntuples(res);

		/*
		 * We may have less triggers than recorded due to having ignored
		 * foreign-key triggers
		 */
		if (ntups > tbinfo->ntrig)
		{
			write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
3309
					  tbinfo->ntrig, tbinfo->dobj.name, ntups);
3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333
			exit_nicely();
		}
		i_tableoid = PQfnumber(res, "tableoid");
		i_oid = PQfnumber(res, "oid");
		i_tgname = PQfnumber(res, "tgname");
		i_tgfname = PQfnumber(res, "tgfname");
		i_tgtype = PQfnumber(res, "tgtype");
		i_tgnargs = PQfnumber(res, "tgnargs");
		i_tgargs = PQfnumber(res, "tgargs");
		i_tgisconstraint = PQfnumber(res, "tgisconstraint");
		i_tgconstrname = PQfnumber(res, "tgconstrname");
		i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
		i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
		i_tgdeferrable = PQfnumber(res, "tgdeferrable");
		i_tginitdeferred = PQfnumber(res, "tginitdeferred");

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

		for (j = 0; j < ntups; j++)
		{
			tginfo[j].dobj.objType = DO_TRIGGER;
			tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
			tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
			AssignDumpId(&tginfo[j].dobj);
3334 3335
			tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
			tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353
			tginfo[j].tgtable = tbinfo;
			tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
			tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
			tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
			tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
			tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
			tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
			tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';

			if (tginfo[j].tgisconstraint)
			{
				tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
				tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
				if (OidIsValid(tginfo[j].tgconstrrelid))
				{
					if (PQgetisnull(res, j, i_tgconstrrelname))
					{
						write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
3354
								  tginfo[j].dobj.name, tbinfo->dobj.name,
3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446
								  tginfo[j].tgconstrrelid);
						exit_nicely();
					}
					tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
				}
				else
					tginfo[j].tgconstrrelname = NULL;
			}
			else
			{
				tginfo[j].tgconstrname = NULL;
				tginfo[j].tgconstrrelid = InvalidOid;
				tginfo[j].tgconstrrelname = NULL;
			}
		}

		PQclear(res);
	}

	destroyPQExpBuffer(query);
}

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

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

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

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

	ntups = PQntuples(res);

	*numProcLangs = ntups;

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

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

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

3447
		planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
		planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
		planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
		if (g_fout->remoteVersion >= 70300)
		{
			planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
			planginfo[i].lanacl = strdup(PQgetvalue(res, i, i_lanacl));
		}
		else
		{
			FuncInfo   *funcInfo;

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

3462
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3463 3464
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
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 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518
			 * mechanism will handle this for us.)
			 */
			funcInfo = findFuncByOid(planginfo[i].lanplcallfoid);
			if (funcInfo)
				addObjectDependency(&planginfo[i].dobj,
									funcInfo->dobj.dumpId);
		}
	}

	PQclear(res);

	destroyPQExpBuffer(query);

	return planginfo;
}

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

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

	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query, "SELECT tableoid, oid, "
						  "castsource, casttarget, castfunc, castcontext "
						  "FROM pg_cast ORDER BY 3,4");
	}
	else
	{
		appendPQExpBuffer(query, "SELECT 0 as tableoid, p.oid, "
						  "t1.oid as castsource, t2.oid as casttarget, "
						  "p.oid as castfunc, 'e' as castcontext "
						  "FROM pg_type t1, pg_type t2, pg_proc p "
						  "WHERE p.pronargs = 1 AND "
						  "p.proargtypes[0] = t1.oid AND "
Bruce Momjian's avatar
Bruce Momjian committed
3519
					  "p.prorettype = t2.oid AND p.proname = t2.typname "
3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540
						  "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
3541
		PQExpBufferData namebuf;
3542 3543 3544
		TypeInfo   *sTypeInfo;
		TypeInfo   *tTypeInfo;

3545 3546 3547 3548 3549 3550 3551 3552 3553
		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));

3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566
		/*
		 * Try to name cast as concatenation of typnames.  This is only
		 * used for purposes of sorting.  If we fail to find either type,
		 * the name will be an empty string.
		 */
		initPQExpBuffer(&namebuf);
		sTypeInfo = findTypeByOid(castinfo[i].castsource);
		tTypeInfo = findTypeByOid(castinfo[i].casttarget);
		if (sTypeInfo && tTypeInfo)
			appendPQExpBuffer(&namebuf, "%s %s",
							  sTypeInfo->dobj.name, tTypeInfo->dobj.name);
		castinfo[i].dobj.name = namebuf.data;

3567 3568 3569
		if (OidIsValid(castinfo[i].castfunc))
		{
			/*
Bruce Momjian's avatar
Bruce Momjian committed
3570 3571
			 * We need to make a dependency to ensure the function will be
			 * dumped first.  (In 7.3 and later the regular dependency
3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 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
			 * 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;
3617
	int			i_attnotnull;
3618
	int			i_atthasdef;
3619
	int			i_attisdropped;
3620
	int			i_attislocal;
3621
	PGresult   *res;
3622
	int			ntups;
3623
	bool		hasdefaults;
3624 3625 3626

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

3629
		/* Don't bother to collect info for sequences */
3630
		if (tbinfo->relkind == RELKIND_SEQUENCE)
3631 3632
			continue;

3633
		/* Don't bother with uninteresting tables, either */
3634
		if (!tbinfo->interesting)
3635 3636 3637 3638 3639 3640
			continue;

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

3643 3644 3645
		/* find all the user attributes and their types */

		/*
3646
		 * we must read the attribute names in attribute number order!
3647
		 * because we will use the attnum to index into the attnames array
3648 3649 3650 3651
		 * 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.
3652 3653
		 */
		if (g_verbose)
3654
			write_msg(NULL, "finding the columns and types of table \"%s\"\n",
3655
					  tbinfo->dobj.name);
3656

Bruce Momjian's avatar
Bruce Momjian committed
3657
		resetPQExpBuffer(q);
3658

3659 3660
		if (g_fout->remoteVersion >= 70300)
		{
3661
			/* need left join here to not fail on dropped columns ... */
3662
			appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
Bruce Momjian's avatar
Bruce Momjian committed
3663 3664
			  "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
			   "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname "
3665 3666
							  "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
							  "on a.atttypid = t.oid "
3667
							  "where a.attrelid = '%u'::pg_catalog.oid "
3668 3669
							  "and a.attnum > 0::pg_catalog.int2 "
							  "order by a.attrelid, a.attnum",
3670
							  tbinfo->dobj.catId.oid);
3671 3672
		}
		else if (g_fout->remoteVersion >= 70100)
3673
		{
3674 3675
			/*
			 * attstattarget doesn't exist in 7.1.  It does exist in 7.2,
Bruce Momjian's avatar
Bruce Momjian committed
3676 3677
			 * but we don't dump it because we can't tell whether it's
			 * been explicitly set or was just a default.
3678
			 */
3679
			appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, -1 as attstattarget, a.attstorage, t.typstorage, "
3680
							  "a.attnotnull, a.atthasdef, false as attisdropped, false as attislocal, "
Bruce Momjian's avatar
Bruce Momjian committed
3681
						  "format_type(t.oid,a.atttypmod) as atttypname "
3682 3683
							  "from pg_attribute a left join pg_type t "
							  "on a.atttypid = t.oid "
3684
							  "where a.attrelid = '%u'::oid "
3685 3686
							  "and a.attnum > 0::int2 "
							  "order by a.attrelid, a.attnum",
3687
							  tbinfo->dobj.catId.oid);
3688 3689 3690
		}
		else
		{
3691
			/* format_type not available before 7.1 */
3692
			appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, attstorage, attstorage as typstorage, "
3693
							  "attnotnull, atthasdef, false as attisdropped, false as attislocal, "
3694 3695
							  "(select typname from pg_type where oid = atttypid) as atttypname "
							  "from pg_attribute a "
3696
							  "where attrelid = '%u'::oid "
3697 3698
							  "and attnum > 0::int2 "
							  "order by attrelid, attnum",
3699
							  tbinfo->dobj.catId.oid);
3700 3701
		}

Bruce Momjian's avatar
Bruce Momjian committed
3702
		res = PQexec(g_conn, q->data);
3703
		check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
3704 3705 3706

		ntups = PQntuples(res);

3707
		i_attnum = PQfnumber(res, "attnum");
3708
		i_attname = PQfnumber(res, "attname");
3709
		i_atttypname = PQfnumber(res, "atttypname");
3710
		i_atttypmod = PQfnumber(res, "atttypmod");
3711
		i_attstattarget = PQfnumber(res, "attstattarget");
3712 3713
		i_attstorage = PQfnumber(res, "attstorage");
		i_typstorage = PQfnumber(res, "typstorage");
3714
		i_attnotnull = PQfnumber(res, "attnotnull");
3715
		i_atthasdef = PQfnumber(res, "atthasdef");
3716
		i_attisdropped = PQfnumber(res, "attisdropped");
3717
		i_attislocal = PQfnumber(res, "attislocal");
3718

3719 3720 3721 3722 3723
		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));
3724 3725
		tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
		tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
3726
		tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
3727
		tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
3728 3729
		tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
3730
		tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
3731 3732 3733
		tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
		tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
3734 3735
		hasdefaults = false;

3736 3737
		for (j = 0; j < ntups; j++)
		{
Bruce Momjian's avatar
Bruce Momjian committed
3738
			if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
3739
			{
3740
				write_msg(NULL, "invalid column numbering in table \"%s\"\n",
3741
						  tbinfo->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
3742
				exit_nicely();
3743
			}
3744 3745 3746 3747
			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));
3748 3749
			tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
			tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
3750
			tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
3751
			tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3752
			tbinfo->attisserial[j] = false;		/* fix below */
3753
			tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
Bruce Momjian's avatar
Bruce Momjian committed
3754
			tbinfo->attrdefs[j] = NULL; /* fix below */
3755
			if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
3756 3757
				hasdefaults = true;
			/* these flags will be set in flagInhAttrs() */
3758 3759 3760
			tbinfo->inhAttrs[j] = false;
			tbinfo->inhAttrDef[j] = false;
			tbinfo->inhNotNull[j] = false;
3761
		}
3762

3763
		PQclear(res);
3764

3765 3766 3767
		/*
		 * Get info about column defaults
		 */
3768 3769
		if (hasdefaults)
		{
3770
			AttrDefInfo *attrdefs;
3771 3772 3773
			int			numDefaults;

			if (g_verbose)
3774
				write_msg(NULL, "finding default expressions of table \"%s\"\n",
3775
						  tbinfo->dobj.name);
3776 3777

			resetPQExpBuffer(q);
3778 3779
			if (g_fout->remoteVersion >= 70300)
			{
3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820
				appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
					   "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
								  "FROM pg_catalog.pg_attrdef "
								  "WHERE adrelid = '%u'::pg_catalog.oid",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70200)
			{
				/* 7.2 did not have OIDs in pg_attrdef */
				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, adnum, "
								  "pg_get_expr(adbin, adrelid) AS adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			else if (g_fout->remoteVersion >= 70100)
			{
				/* no pg_get_expr, so must rely on adsrc */
				appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			else
			{
				/* no pg_get_expr, no tableoid either */
				appendPQExpBuffer(q, "SELECT "
								  "(SELECT oid FROM pg_class WHERE relname = 'pg_attrdef') AS tableoid, "
								  "oid, adnum, adsrc "
								  "FROM pg_attrdef "
								  "WHERE adrelid = '%u'::oid",
								  tbinfo->dobj.catId.oid);
			}
			res = PQexec(g_conn, q->data);
			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);

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

			for (j = 0; j < numDefaults; j++)
			{
Bruce Momjian's avatar
Bruce Momjian committed
3821
				int			adnum;
3822 3823 3824 3825 3826 3827 3828 3829 3830

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

3831 3832 3833
				attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
				attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;

3834 3835 3836
				/*
				 * Defaults on a VIEW must always be dumped as separate
				 * ALTER TABLE commands.  Defaults on regular tables are
Bruce Momjian's avatar
Bruce Momjian committed
3837 3838 3839
				 * 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.
3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857
				 */
				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",
3858
							  adnum, tbinfo->dobj.name);
3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875
					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",
3876
						  tbinfo->dobj.name);
3877 3878 3879 3880 3881

			resetPQExpBuffer(q);
			if (g_fout->remoteVersion >= 70400)
			{
				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
Bruce Momjian's avatar
Bruce Momjian committed
3882
						"pg_catalog.pg_get_constraintdef(oid) AS consrc "
3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898
								  "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);
3899 3900
			}
			else if (g_fout->remoteVersion >= 70200)
3901
			{
3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919
				/* 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);
3920 3921 3922
			}
			else
			{
3923 3924 3925 3926 3927 3928 3929 3930 3931
				/* 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);
3932 3933
			}
			res = PQexec(g_conn, q->data);
3934 3935 3936 3937
			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);

			numConstrs = PQntuples(res);
			if (numConstrs != tbinfo->ncheck)
3938
			{
3939
				write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
3940
						  tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
3941
				write_msg(NULL, "(The system catalogs might be corrupted.)\n");
3942 3943
				exit_nicely();
			}
3944

3945 3946
			constrs = (ConstraintInfo *) malloc(numConstrs * sizeof(ConstraintInfo));
			tbinfo->checkexprs = constrs;
3947

3948 3949 3950 3951 3952 3953
			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);
3954 3955
				constrs[j].dobj.name = strdup(PQgetvalue(res, j, 2));
				constrs[j].dobj.namespace = tbinfo->dobj.namespace;
3956 3957 3958 3959 3960 3961 3962 3963 3964
				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;
				addObjectDependency(&tbinfo->dobj,
									constrs[j].dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
3965

3966 3967 3968 3969 3970
				/*
				 * 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.
				 */
3971
			}
3972
			PQclear(res);
3973
		}
3974 3975

		/*
Bruce Momjian's avatar
Bruce Momjian committed
3976 3977 3978 3979 3980
		 * 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.
3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991
		 */
		for (j = 0; j < ntups; j++)
		{
			/*
			 * Note assumption that format_type will show these types as
			 * exactly "integer" and "bigint" regardless of schema path.
			 * This is correct in 7.3 but needs to be watched.
			 */
			if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
				strcmp(tbinfo->atttypnames[j], "bigint") != 0)
				continue;
3992
			if (tbinfo->attrdefs[j] == NULL)
3993 3994 3995
				continue;
			for (k = 0; k < numTables; k++)
			{
Bruce Momjian's avatar
Bruce Momjian committed
3996
				TableInfo  *seqinfo = &tblinfo[k];
3997

3998 3999
				if (OidIsValid(seqinfo->owning_tab) &&
					seqinfo->owning_tab == tbinfo->dobj.catId.oid &&
Bruce Momjian's avatar
Bruce Momjian committed
4000
					seqinfo->owning_col == j + 1)
4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012
				{
					/*
					 * 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;
				}
			}
		}
4013
	}
4014 4015

	destroyPQExpBuffer(q);
4016 4017 4018 4019
}


/*
4020
 * dumpComment --
Bruce Momjian's avatar
Bruce Momjian committed
4021
 *
4022
 * This routine is used to dump any comments associated with the
4023
 * object handed to this routine. The routine takes a constant character
4024
 * string for the target part of the comment-creation command, plus
4025
 * the namespace and owner of the object (for labeling the ArchiveEntry),
4026 4027
 * plus catalog ID and subid which are the lookup key for pg_description,
 * plus the dump ID for the object (for setting a dependency).
4028
 * If a matching pg_description entry is found, it is dumped.
4029
 */
4030
static void
4031 4032
dumpComment(Archive *fout, const char *target,
			const char *namespace, const char *owner,
4033
			CatalogId catalogId, int subid, DumpId dumpId)
4034
{
4035 4036
	CommentItem *comments;
	int			ncomments;
Bruce Momjian's avatar
Bruce Momjian committed
4037

4038 4039 4040 4041
	/* Comments are SCHEMA not data */
	if (dataOnly)
		return;

4042 4043 4044
	/* Search for comments associated with catalogId, using table */
	ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
							 &comments);
4045

4046 4047
	/* Is there one matching the subid? */
	while (ncomments > 0)
4048
	{
4049 4050 4051 4052
		if (comments->objsubid == subid)
			break;
		comments++;
		ncomments--;
4053
	}
Bruce Momjian's avatar
Bruce Momjian committed
4054

Bruce Momjian's avatar
Bruce Momjian committed
4055
	/* If a comment exists, build COMMENT ON statement */
4056
	if (ncomments > 0)
4057
	{
4058 4059
		PQExpBuffer query = createPQExpBuffer();

4060
		appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
4061
		appendStringLiteral(query, comments->descr, false);
4062
		appendPQExpBuffer(query, ";\n");
4063

4064
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
4065
					 target, namespace, owner, false,
4066 4067 4068
					 "COMMENT", query->data, "", NULL,
					 &(dumpId), 1,
					 NULL, NULL);
4069

4070 4071
		destroyPQExpBuffer(query);
	}
4072 4073 4074 4075 4076
}

/*
 * dumpTableComment --
 *
4077
 * As above, but dump comments for both the specified table (or view)
4078
 * and its columns.
4079 4080 4081
 */
static void
dumpTableComment(Archive *fout, TableInfo *tbinfo,
4082
				 const char *reltypename)
4083
{
4084 4085
	CommentItem *comments;
	int			ncomments;
4086 4087 4088 4089 4090 4091 4092
	PQExpBuffer query;
	PQExpBuffer target;

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

4093 4094 4095 4096 4097
	/* Search for comments associated with relation, using table */
	ncomments = findComments(fout,
							 tbinfo->dobj.catId.tableoid,
							 tbinfo->dobj.catId.oid,
							 &comments);
4098

4099 4100 4101
	/* If comments exist, build COMMENT ON statements */
	if (ncomments <= 0)
		return;
4102 4103 4104 4105

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

4106
	while (ncomments > 0)
4107
	{
4108 4109
		const char *descr = comments->descr;
		int			objsubid = comments->objsubid;
4110 4111 4112 4113 4114

		if (objsubid == 0)
		{
			resetPQExpBuffer(target);
			appendPQExpBuffer(target, "%s %s", reltypename,
4115
							  fmtId(tbinfo->dobj.name));
4116 4117 4118

			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4119
			appendStringLiteral(query, descr, false);
4120 4121
			appendPQExpBuffer(query, ";\n");

4122 4123
			ArchiveEntry(fout, nilCatalogId, createDumpId(),
						 target->data,
Bruce Momjian's avatar
Bruce Momjian committed
4124
					  tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
4125
						 false, "COMMENT", query->data, "", NULL,
4126 4127
						 &(tbinfo->dobj.dumpId), 1,
						 NULL, NULL);
4128 4129 4130 4131 4132
		}
		else if (objsubid > 0 && objsubid <= tbinfo->numatts)
		{
			resetPQExpBuffer(target);
			appendPQExpBuffer(target, "COLUMN %s.",
4133
							  fmtId(tbinfo->dobj.name));
4134
			appendPQExpBuffer(target, "%s",
Bruce Momjian's avatar
Bruce Momjian committed
4135
							  fmtId(tbinfo->attnames[objsubid - 1]));
Bruce Momjian's avatar
Bruce Momjian committed
4136

4137 4138
			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
4139
			appendStringLiteral(query, descr, false);
4140 4141
			appendPQExpBuffer(query, ";\n");

4142 4143
			ArchiveEntry(fout, nilCatalogId, createDumpId(),
						 target->data,
Bruce Momjian's avatar
Bruce Momjian committed
4144
					  tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
4145
						 false, "COMMENT", query->data, "", NULL,
4146 4147
						 &(tbinfo->dobj.dumpId), 1,
						 NULL, NULL);
4148
		}
4149 4150 4151

		comments++;
		ncomments--;
4152
	}
Bruce Momjian's avatar
Bruce Momjian committed
4153

4154
	destroyPQExpBuffer(query);
4155
	destroyPQExpBuffer(target);
Bruce Momjian's avatar
Bruce Momjian committed
4156 4157
}

4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182
/*
 * findComments --
 *
 * Find the comment(s), if any, associated with the given object.  All the
 * objsubid values associated with the given classoid/objoid are found with
 * one search.
 */
static int
findComments(Archive *fout, Oid classoid, Oid objoid,
			 CommentItem **items)
{
	/* static storage for table of comments */
	static CommentItem *comments = NULL;
	static int	ncomments = -1;

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

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

	/*
Bruce Momjian's avatar
Bruce Momjian committed
4183 4184 4185
	 * 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.
4186 4187 4188 4189 4190 4191 4192 4193
	 */
	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
4194
	high = &comments[ncomments - 1];
4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 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 4322 4323 4324 4325
	while (low <= high)
	{
		middle = low + (high - low) / 2;

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

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

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

	*items = middle;

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

	return nmatch;
}

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

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

	query = createPQExpBuffer();

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

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

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

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

	ntups = PQntuples(res);

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

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

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

	*items = comments;
	return ntups;
}

4326
/*
4327
 * dumpDumpableObject
Bruce Momjian's avatar
Bruce Momjian committed
4328
 *
4329 4330
 * This routine and its subsidiaries are responsible for creating
 * ArchiveEntries (TOC objects) for each object to be dumped.
4331
 */
4332 4333
static void
dumpDumpableObject(Archive *fout, DumpableObject *dobj)
4334
{
4335
	switch (dobj->objType)
4336
	{
4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387
		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;
4388 4389 4390 4391 4392 4393
		case DO_TABLE_TYPE:
			/* table rowtypes are never dumped separately */
			break;
		case DO_BLOBS:
			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
						 dobj->name, NULL, "",
4394
						 false, "BLOBS", "", "", NULL,
4395 4396 4397
						 NULL, 0,
						 dumpBlobs, NULL);
			break;
4398 4399 4400 4401
	}
}

/*
4402 4403
 * dumpNamespace
 *	  writes out to fout the queries to recreate a user-defined namespace
4404
 */
4405 4406
static void
dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
4407
{
4408 4409
	PQExpBuffer q;
	PQExpBuffer delq;
4410
	char	   *qnspname;
4411

4412 4413 4414
	/* skip if not to be dumped */
	if (!nspinfo->dump || dataOnly)
		return;
4415

4416
	/* don't dump dummy namespace from pre-7.3 source */
4417
	if (strlen(nspinfo->dobj.name) == 0)
4418
		return;
4419

4420 4421
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
4422

4423
	qnspname = strdup(fmtId(nspinfo->dobj.name));
4424

4425
	/*
Bruce Momjian's avatar
Bruce Momjian committed
4426 4427 4428 4429 4430 4431
	 * Note that ownership is shown in the AUTHORIZATION clause, while the
	 * archive entry is listed with empty owner (causing it to be emitted
	 * with SET SESSION AUTHORIZATION DEFAULT). This seems the best way of
	 * dealing with schemas owned by users without CREATE SCHEMA
	 * privilege.  Further hacking has to be applied for --no-owner mode,
	 * though!
4432
	 */
4433
	appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
4434

4435 4436
	appendPQExpBuffer(q, "CREATE SCHEMA %s AUTHORIZATION %s",
					  qnspname, fmtId(nspinfo->usename));
4437

4438
	/* Add tablespace qualifier, if not default for database */
4439 4440 4441
	if (strlen(nspinfo->nsptablespace) != 0)
		appendPQExpBuffer(q, " TABLESPACE %s",
						  fmtId(nspinfo->nsptablespace));
4442

4443
	appendPQExpBuffer(q, ";\n");
4444

4445 4446 4447 4448 4449 4450
	ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
				 nspinfo->dobj.name,
				 NULL, strcmp(nspinfo->dobj.name, "public") == 0 ? nspinfo->usename : "",
				 false, "SCHEMA", q->data, delq->data, NULL,
				 nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
				 NULL, NULL);
4451

4452 4453 4454 4455 4456 4457
	/* Dump Schema Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "SCHEMA %s", qnspname);
	dumpComment(fout, q->data,
				NULL, nspinfo->usename,
				nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
4458

4459
	dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
4460
			qnspname, nspinfo->dobj.name, NULL,
4461
			nspinfo->usename, nspinfo->nspacl);
4462

4463
	free(qnspname);
4464 4465 4466 4467 4468 4469

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

/*
4470 4471 4472 4473 4474 4475 4476
 * 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 */
4477
	if (!tinfo->dobj.namespace->dump || dataOnly)
4478 4479 4480
		return;

	/* skip complex types, except for standalone composite types */
4481
	/* (note: this test should now be unnecessary) */
4482 4483 4484 4485 4486 4487 4488 4489
	if (OidIsValid(tinfo->typrelid) && tinfo->typrelkind != 'c')
		return;

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

	/* skip all array types that start w/ underscore */
4490
	if ((tinfo->dobj.name[0] == '_') &&
4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504
		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
4505
 *	  writes out to fout the queries to recreate a user-defined base type
4506 4507
 */
static void
4508
dumpBaseType(Archive *fout, TypeInfo *tinfo)
4509 4510 4511 4512 4513 4514 4515 4516 4517
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
	char	   *typlen;
	char	   *typinput;
	char	   *typoutput;
4518 4519
	char	   *typreceive;
	char	   *typsend;
4520
	char	   *typanalyze;
4521 4522 4523 4524
	Oid			typinputoid;
	Oid			typoutputoid;
	Oid			typreceiveoid;
	Oid			typsendoid;
4525
	Oid			typanalyzeoid;
4526 4527 4528 4529 4530 4531 4532
	char	   *typdelim;
	char	   *typdefault;
	char	   *typbyval;
	char	   *typalign;
	char	   *typstorage;

	/* Set proper schema search path so regproc references list correctly */
4533
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4534 4535

	/* Fetch type-specific details */
4536
	if (fout->remoteVersion >= 80000)
4537 4538 4539
	{
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, typreceive, typsend, "
4540
						  "typanalyze, "
4541 4542 4543 4544
						  "typinput::pg_catalog.oid as typinputoid, "
						  "typoutput::pg_catalog.oid as typoutputoid, "
						  "typreceive::pg_catalog.oid as typreceiveoid, "
						  "typsend::pg_catalog.oid as typsendoid, "
4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561
						  "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, "
4562 4563 4564
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_catalog.pg_type "
4565 4566
						  "WHERE oid = '%u'::pg_catalog.oid",
						  tinfo->dobj.catId.oid);
4567 4568
	}
	else if (fout->remoteVersion >= 70300)
4569
	{
4570 4571
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4572
						  "'-' as typreceive, '-' as typsend, "
4573
						  "'-' as typanalyze, "
4574 4575
						  "typinput::pg_catalog.oid as typinputoid, "
						  "typoutput::pg_catalog.oid as typoutputoid, "
4576
						  "0 as typreceiveoid, 0 as typsendoid, "
4577
						  "0 as typanalyzeoid, "
4578 4579 4580
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_catalog.pg_type "
4581 4582
						  "WHERE oid = '%u'::pg_catalog.oid",
						  tinfo->dobj.catId.oid);
4583 4584
	}
	else if (fout->remoteVersion >= 70100)
4585
	{
4586 4587 4588 4589
		/*
		 * Note: although pre-7.3 catalogs contain typreceive and typsend,
		 * ignore them because they are not right.
		 */
4590 4591
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4592
						  "'-' as typreceive, '-' as typsend, "
4593
						  "'-' as typanalyze, "
4594 4595
						  "typinput::oid as typinputoid, "
						  "typoutput::oid as typoutputoid, "
4596
						  "0 as typreceiveoid, 0 as typsendoid, "
4597
						  "0 as typanalyzeoid, "
4598 4599 4600
						  "typdelim, typdefault, typbyval, typalign, "
						  "typstorage "
						  "FROM pg_type "
4601 4602
						  "WHERE oid = '%u'::oid",
						  tinfo->dobj.catId.oid);
4603 4604 4605
	}
	else
	{
4606 4607
		appendPQExpBuffer(query, "SELECT typlen, "
						  "typinput, typoutput, "
4608
						  "'-' as typreceive, '-' as typsend, "
4609
						  "'-' as typanalyze, "
4610 4611
						  "typinput::oid as typinputoid, "
						  "typoutput::oid as typoutputoid, "
4612
						  "0 as typreceiveoid, 0 as typsendoid, "
4613
						  "0 as typanalyzeoid, "
4614 4615 4616
						  "typdelim, typdefault, typbyval, typalign, "
						  "'p'::char as typstorage "
						  "FROM pg_type "
4617 4618
						  "WHERE oid = '%u'::oid",
						  tinfo->dobj.catId.oid);
4619 4620 4621
	}

	res = PQexec(g_conn, query->data);
4622
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635

	/* 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"));
4636 4637
	typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
	typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
4638
	typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
4639 4640 4641 4642
	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")));
4643
	typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
4644 4645 4646 4647
	typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4648
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4649 4650 4651 4652
	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
4653 4654 4655 4656
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
4657
	appendPQExpBuffer(delq, "DROP TYPE %s.",
4658
					  fmtId(tinfo->dobj.namespace->dobj.name));
4659
	appendPQExpBuffer(delq, "%s CASCADE;\n",
4660
					  fmtId(tinfo->dobj.name));
4661 4662

	appendPQExpBuffer(q,
4663
					  "CREATE TYPE %s (\n"
4664
					  "    INTERNALLENGTH = %s",
4665
					  fmtId(tinfo->dobj.name),
4666
					  (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
4667 4668 4669

	if (fout->remoteVersion >= 70300)
	{
4670
		/* regproc result is correctly quoted as of 7.3 */
4671 4672
		appendPQExpBuffer(q, ",\n    INPUT = %s", typinput);
		appendPQExpBuffer(q, ",\n    OUTPUT = %s", typoutput);
4673
		if (OidIsValid(typreceiveoid))
4674
			appendPQExpBuffer(q, ",\n    RECEIVE = %s", typreceive);
4675
		if (OidIsValid(typsendoid))
4676
			appendPQExpBuffer(q, ",\n    SEND = %s", typsend);
4677 4678
		if (OidIsValid(typanalyzeoid))
			appendPQExpBuffer(q, ",\n    ANALYZE = %s", typanalyze);
4679 4680 4681 4682 4683
	}
	else
	{
		/* regproc delivers an unquoted name before 7.3 */
		/* cannot combine these because fmtId uses static result area */
4684 4685
		appendPQExpBuffer(q, ",\n    INPUT = %s", fmtId(typinput));
		appendPQExpBuffer(q, ",\n    OUTPUT = %s", fmtId(typoutput));
4686
		/* no chance that receive/send/analyze need be printed */
Bruce Momjian's avatar
Bruce Momjian committed
4687 4688
	}

4689 4690
	if (typdefault != NULL)
	{
4691 4692
		appendPQExpBuffer(q, ",\n    DEFAULT = ");
		appendStringLiteral(q, typdefault, true);
4693 4694 4695 4696 4697 4698 4699
	}

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

		/* reselect schema in case changed by function dump */
4700
		selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4701
		elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque);
4702
		appendPQExpBuffer(q, ",\n    ELEMENT = %s", elemType);
4703 4704 4705
		free(elemType);
	}

4706 4707 4708 4709 4710 4711
	if (typdelim && strcmp(typdelim, ",") != 0)
	{
		appendPQExpBuffer(q, ",\n    DELIMITER = ");
		appendStringLiteral(q, typdelim, true);
	}

4712
	if (strcmp(typalign, "c") == 0)
4713
		appendPQExpBuffer(q, ",\n    ALIGNMENT = char");
4714
	else if (strcmp(typalign, "s") == 0)
4715
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int2");
4716
	else if (strcmp(typalign, "i") == 0)
4717
		appendPQExpBuffer(q, ",\n    ALIGNMENT = int4");
4718
	else if (strcmp(typalign, "d") == 0)
4719
		appendPQExpBuffer(q, ",\n    ALIGNMENT = double");
4720 4721

	if (strcmp(typstorage, "p") == 0)
4722
		appendPQExpBuffer(q, ",\n    STORAGE = plain");
4723
	else if (strcmp(typstorage, "e") == 0)
4724
		appendPQExpBuffer(q, ",\n    STORAGE = external");
4725
	else if (strcmp(typstorage, "x") == 0)
4726
		appendPQExpBuffer(q, ",\n    STORAGE = extended");
4727
	else if (strcmp(typstorage, "m") == 0)
4728
		appendPQExpBuffer(q, ",\n    STORAGE = main");
4729 4730

	if (strcmp(typbyval, "t") == 0)
4731 4732 4733
		appendPQExpBuffer(q, ",\n    PASSEDBYVALUE");

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

4735
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4736 4737
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4738
				 tinfo->usename, false,
4739 4740 4741
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4742

Bruce Momjian's avatar
Bruce Momjian committed
4743
	/* Dump Type Comments */
4744 4745
	resetPQExpBuffer(q);

4746
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
4747
	dumpComment(fout, q->data,
4748
				tinfo->dobj.namespace->dobj.name, tinfo->usename,
4749
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
Bruce Momjian's avatar
Bruce Momjian committed
4750 4751

	PQclear(res);
4752 4753
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
4754
	destroyPQExpBuffer(query);
Bruce Momjian's avatar
Bruce Momjian committed
4755 4756
}

4757
/*
4758
 * dumpDomain
Bruce Momjian's avatar
Bruce Momjian committed
4759
 *	  writes out to fout the queries to recreate a user-defined domain
4760
 */
4761
static void
4762
dumpDomain(Archive *fout, TypeInfo *tinfo)
4763 4764 4765 4766
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
4767
	PGresult   *res;
4768
	int			ntups;
4769
	int			i;
4770 4771 4772
	char	   *typnotnull;
	char	   *typdefn;
	char	   *typdefault;
4773

4774
	/* Set proper schema search path so type references list correctly */
4775
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
4776

4777
	/* Fetch domain specific details */
4778
	/* We assume here that remoteVersion must be at least 70300 */
4779
	appendPQExpBuffer(query, "SELECT typnotnull, "
Bruce Momjian's avatar
Bruce Momjian committed
4780
			"pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
4781
					  "typdefault "
4782
					  "FROM pg_catalog.pg_type "
4783 4784
					  "WHERE oid = '%u'::pg_catalog.oid",
					  tinfo->dobj.catId.oid);
4785 4786

	res = PQexec(g_conn, query->data);
4787
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4788 4789 4790 4791

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

4798 4799 4800 4801 4802
	typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
	typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
		typdefault = NULL;
	else
4803
		typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
4804 4805 4806

	appendPQExpBuffer(q,
					  "CREATE DOMAIN %s AS %s",
4807
					  fmtId(tinfo->dobj.name),
4808
					  typdefn);
4809

4810
	if (typnotnull[0] == 't')
4811 4812
		appendPQExpBuffer(q, " NOT NULL");

4813
	if (typdefault)
4814
		appendPQExpBuffer(q, " DEFAULT %s", typdefault);
4815

4816 4817 4818
	PQclear(res);

	/*
4819
	 * Add any CHECK constraints for the domain
4820
	 */
4821
	for (i = 0; i < tinfo->nDomChecks; i++)
4822
	{
4823
		ConstraintInfo *domcheck = &(tinfo->domChecks[i]);
4824

4825 4826
		if (!domcheck->separate)
			appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
Bruce Momjian's avatar
Bruce Momjian committed
4827
						   fmtId(domcheck->dobj.name), domcheck->condef);
4828
	}
Bruce Momjian's avatar
Bruce Momjian committed
4829

4830 4831
	appendPQExpBuffer(q, ";\n");

4832 4833 4834 4835 4836
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delq, "DROP DOMAIN %s.",
4837
					  fmtId(tinfo->dobj.namespace->dobj.name));
4838
	appendPQExpBuffer(delq, "%s;\n",
4839
					  fmtId(tinfo->dobj.name));
4840

4841
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4842 4843
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4844
				 tinfo->usename, false,
4845 4846 4847
				 "DOMAIN", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4848

Bruce Momjian's avatar
Bruce Momjian committed
4849
	/* Dump Domain Comments */
4850 4851
	resetPQExpBuffer(q);

4852
	appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->dobj.name));
4853
	dumpComment(fout, q->data,
4854
				tinfo->dobj.namespace->dobj.name, tinfo->usename,
4855
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
4856 4857 4858 4859

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
4860 4861
}

Bruce Momjian's avatar
Bruce Momjian committed
4862
/*
4863
 * dumpCompositeType
Bruce Momjian's avatar
Bruce Momjian committed
4864
 *	  writes out to fout the queries to recreate a user-defined stand-alone
4865
 *	  composite type
Bruce Momjian's avatar
Bruce Momjian committed
4866 4867
 */
static void
4868
dumpCompositeType(Archive *fout, TypeInfo *tinfo)
Bruce Momjian's avatar
Bruce Momjian committed
4869 4870 4871 4872 4873 4874
{
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer query = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
Bruce Momjian's avatar
Bruce Momjian committed
4875 4876
	int			i_attname;
	int			i_atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
4877 4878 4879
	int			i;

	/* Set proper schema search path so type references list correctly */
4880
	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
Bruce Momjian's avatar
Bruce Momjian committed
4881 4882 4883 4884 4885

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

	appendPQExpBuffer(query, "SELECT a.attname, "
Bruce Momjian's avatar
Bruce Momjian committed
4886 4887
		 "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
				  "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
4888
					  "WHERE t.oid = '%u'::pg_catalog.oid "
4889 4890 4891
					  "AND a.attrelid = t.typrelid "
					  "AND NOT a.attisdropped "
					  "ORDER BY a.attnum ",
4892
					  tinfo->dobj.catId.oid);
Bruce Momjian's avatar
Bruce Momjian committed
4893 4894

	res = PQexec(g_conn, query->data);
4895
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
Bruce Momjian's avatar
Bruce Momjian committed
4896 4897 4898 4899 4900

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

4905 4906
	i_attname = PQfnumber(res, "attname");
	i_atttypdefn = PQfnumber(res, "atttypdefn");
Bruce Momjian's avatar
Bruce Momjian committed
4907

4908
	appendPQExpBuffer(q, "CREATE TYPE %s AS (",
4909
					  fmtId(tinfo->dobj.name));
Bruce Momjian's avatar
Bruce Momjian committed
4910 4911 4912

	for (i = 0; i < ntups; i++)
	{
4913 4914
		char	   *attname;
		char	   *atttypdefn;
Bruce Momjian's avatar
Bruce Momjian committed
4915

4916 4917
		attname = PQgetvalue(res, i, i_attname);
		atttypdefn = PQgetvalue(res, i, i_atttypdefn);
Bruce Momjian's avatar
Bruce Momjian committed
4918

4919 4920 4921
		appendPQExpBuffer(q, "\n\t%s %s", fmtId(attname), atttypdefn);
		if (i < ntups - 1)
			appendPQExpBuffer(q, ",");
Bruce Momjian's avatar
Bruce Momjian committed
4922
	}
4923
	appendPQExpBuffer(q, "\n);\n");
Bruce Momjian's avatar
Bruce Momjian committed
4924

Bruce Momjian's avatar
Bruce Momjian committed
4925 4926 4927 4928
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
4929
	appendPQExpBuffer(delq, "DROP TYPE %s.",
4930
					  fmtId(tinfo->dobj.namespace->dobj.name));
4931
	appendPQExpBuffer(delq, "%s;\n",
4932
					  fmtId(tinfo->dobj.name));
4933

4934
	ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
4935 4936
				 tinfo->dobj.name,
				 tinfo->dobj.namespace->dobj.name,
4937
				 tinfo->usename, false,
4938 4939 4940
				 "TYPE", q->data, delq->data, NULL,
				 tinfo->dobj.dependencies, tinfo->dobj.nDeps,
				 NULL, NULL);
4941

4942

4943 4944
	/* Dump Type Comments */
	resetPQExpBuffer(q);
4945

4946
	appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
4947
	dumpComment(fout, q->data,
4948
				tinfo->dobj.namespace->dobj.name, tinfo->usename,
4949
				tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
4950

4951 4952 4953 4954 4955
	PQclear(res);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(query);
}
4956

4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969
/*
 * dumpProcLang
 *		  writes out to fout the queries to recreate a user-defined
 *		  procedural language
 */
static void
dumpProcLang(Archive *fout, ProcLangInfo *plang)
{
	PQExpBuffer defqry;
	PQExpBuffer delqry;
	char	   *qlanname;
	FuncInfo   *funcInfo;
	FuncInfo   *validatorInfo = NULL;
4970

4971 4972
	if (dataOnly)
		return;
4973

4974
	/*
Bruce Momjian's avatar
Bruce Momjian committed
4975 4976 4977 4978 4979
	 * Current theory is to dump PLs iff their underlying functions will
	 * be dumped (are in a dumpable namespace, or have a non-system OID in
	 * pre-7.3 databases).	Actually, we treat the PL itself as being in
	 * the underlying function's namespace, though it isn't really.  This
	 * avoids searchpath problems for the HANDLER clause.
4980
	 *
Bruce Momjian's avatar
Bruce Momjian committed
4981 4982 4983 4984
	 * If the underlying function is in the pg_catalog namespace, we won't
	 * have loaded it into finfo[] at all; therefore, treat failure to
	 * find it in finfo[] as indicating we shouldn't dump it, not as an
	 * error condition.  Ditto for the validator.
4985
	 */
4986

4987 4988 4989
	funcInfo = findFuncByOid(plang->lanplcallfoid);
	if (funcInfo == NULL)
		return;
4990

4991
	if (!funcInfo->dobj.namespace->dump)
4992
		return;
4993

4994 4995 4996 4997 4998 4999
	if (OidIsValid(plang->lanvalidator))
	{
		validatorInfo = findFuncByOid(plang->lanvalidator);
		if (validatorInfo == NULL)
			return;
	}
5000

5001 5002
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
5003

5004
	qlanname = strdup(fmtId(plang->dobj.name));
5005

5006 5007
	appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
					  qlanname);
5008

5009 5010 5011 5012
	appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
					  plang->lanpltrusted ? "TRUSTED " : "",
					  qlanname);
	appendPQExpBuffer(defqry, " HANDLER %s",
5013
					  fmtId(funcInfo->dobj.name));
5014 5015 5016 5017
	if (OidIsValid(plang->lanvalidator))
	{
		appendPQExpBuffer(defqry, " VALIDATOR ");
		/* Cope with possibility that validator is in different schema */
5018
		if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
5019
			appendPQExpBuffer(defqry, "%s.",
Bruce Momjian's avatar
Bruce Momjian committed
5020
						fmtId(validatorInfo->dobj.namespace->dobj.name));
5021
		appendPQExpBuffer(defqry, "%s",
5022
						  fmtId(validatorInfo->dobj.name));
5023
	}
5024
	appendPQExpBuffer(defqry, ";\n");
5025

5026
	ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
5027 5028
				 plang->dobj.name,
				 funcInfo->dobj.namespace->dobj.name, "",
5029
				 false, "PROCEDURAL LANGUAGE",
5030 5031 5032
				 defqry->data, delqry->data, NULL,
				 plang->dobj.dependencies, plang->dobj.nDeps,
				 NULL, NULL);
5033

5034 5035
	/* Dump Proc Lang Comments */
	resetPQExpBuffer(defqry);
5036

5037 5038 5039 5040
	appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
	dumpComment(fout, defqry->data,
				NULL, "",
				plang->dobj.catId, 0, plang->dobj.dumpId);
5041

5042 5043
	if (plang->lanpltrusted)
		dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
5044 5045
				qlanname, plang->dobj.name,
				funcInfo->dobj.namespace->dobj.name,
5046
				NULL, plang->lanacl);
5047

5048 5049 5050 5051
	free(qlanname);

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5052 5053
}

5054 5055 5056 5057 5058
/*
 * format_function_signature: generate function name and argument list
 *
 * The argument type names are qualified if needed.  The function name
 * is never qualified.
5059 5060
 *
 * argnames may be NULL if no names are available.
5061
 */
5062
static char *
5063 5064
format_function_signature(FuncInfo *finfo, char **argnames,
						  bool honor_quotes)
5065 5066 5067 5068 5069
{
	PQExpBufferData fn;
	int			j;

	initPQExpBuffer(&fn);
5070
	if (honor_quotes)
5071
		appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
5072
	else
5073
		appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
5074 5075 5076
	for (j = 0; j < finfo->nargs; j++)
	{
		char	   *typname;
5077
		char	   *argname;
5078 5079

		typname = getFormattedTypeName(finfo->argtypes[j], zeroAsOpaque);
5080 5081 5082 5083 5084 5085

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

		appendPQExpBuffer(&fn, "%s%s%s%s",
5086
						  (j > 0) ? ", " : "",
5087 5088
						  argname ? fmtId(argname) : "",
						  argname ? " " : "",
5089 5090 5091 5092 5093 5094 5095 5096
						  typname);
		free(typname);
	}
	appendPQExpBuffer(&fn, ")");
	return fn.data;
}


5097
/*
5098 5099
 * dumpFunc:
 *	  dump out one function
5100
 */
5101
static void
5102
dumpFunc(Archive *fout, FuncInfo *finfo)
5103
{
5104 5105 5106 5107 5108 5109 5110
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delqry;
	PQExpBuffer asPart;
	PGresult   *res;
	char	   *funcsig;
	char	   *funcsig_tag;
5111 5112 5113 5114
	int			ntups;
	char	   *proretset;
	char	   *prosrc;
	char	   *probin;
5115
	char	   *proargnames;
5116 5117
	char	   *provolatile;
	char	   *proisstrict;
5118
	char	   *prosecdef;
5119
	char	   *lanname;
5120
	char	   *rettypename;
5121
	char	  **argnamearray = NULL;
5122

5123
	/* Dump only funcs in dumpable namespaces */
5124
	if (!finfo->dobj.namespace->dump || dataOnly)
5125
		return;
5126

5127 5128 5129 5130
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delqry = createPQExpBuffer();
	asPart = createPQExpBuffer();
5131

5132
	/* Set proper schema search path so type references list correctly */
5133
	selectSourceSchema(finfo->dobj.namespace->dobj.name);
5134 5135

	/* Fetch function-specific details */
5136
	if (g_fout->remoteVersion >= 80000)
5137 5138 5139
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151
						  "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, "
						  "null::text as proargnames, "
5152
						  "provolatile, proisstrict, prosecdef, "
5153
						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
5154
						  "FROM pg_catalog.pg_proc "
5155 5156
						  "WHERE oid = '%u'::pg_catalog.oid",
						  finfo->dobj.catId.oid);
5157 5158 5159 5160 5161
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5162
						  "null::text as proargnames, "
Bruce Momjian's avatar
Bruce Momjian committed
5163
		 "case when proiscachable then 'i' else 'v' end as provolatile, "
5164
						  "proisstrict, "
5165
						  "'f'::boolean as prosecdef, "
5166
						  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5167
						  "FROM pg_proc "
5168 5169
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5170 5171 5172 5173 5174
	}
	else
	{
		appendPQExpBuffer(query,
						  "SELECT proretset, prosrc, probin, "
5175
						  "null::text as proargnames, "
Bruce Momjian's avatar
Bruce Momjian committed
5176
		 "case when proiscachable then 'i' else 'v' end as provolatile, "
5177
						  "'f'::boolean as proisstrict, "
5178
						  "'f'::boolean as prosecdef, "
5179
						  "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
5180
						  "FROM pg_proc "
5181 5182
						  "WHERE oid = '%u'::oid",
						  finfo->dobj.catId.oid);
5183
	}
5184

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

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

5197 5198 5199
	proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
	prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
	probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
5200
	proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
5201 5202
	provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
	proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
5203
	prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
5204
	lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
5205

5206
	/*
5207 5208
	 * See backend/commands/define.c for details of how the 'AS' clause is
	 * used.
5209
	 */
5210
	if (strcmp(probin, "-") != 0)
5211
	{
5212
		appendPQExpBuffer(asPart, "AS ");
5213
		appendStringLiteral(asPart, probin, true);
5214
		if (strcmp(prosrc, "-") != 0)
5215 5216
		{
			appendPQExpBuffer(asPart, ", ");
Bruce Momjian's avatar
Bruce Momjian committed
5217

5218
			/*
5219 5220 5221 5222 5223 5224 5225
			 * 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);
5226
		}
5227 5228 5229
	}
	else
	{
5230
		if (strcmp(prosrc, "-") != 0)
5231 5232
		{
			appendPQExpBuffer(asPart, "AS ");
5233 5234 5235 5236 5237
			/* with no bin, dollar quote src unconditionally if allowed */
			if (disable_dollar_quoting)
				appendStringLiteral(asPart, prosrc, false);
			else
				appendStringLiteralDQ(asPart, prosrc, NULL);
5238
		}
5239 5240
	}

5241 5242
	if (proargnames && *proargnames)
	{
Bruce Momjian's avatar
Bruce Momjian committed
5243
		int			nitems = 0;
5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256

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

	funcsig = format_function_signature(finfo, argnamearray, true);
	funcsig_tag = format_function_signature(finfo, NULL, false);
5257

Bruce Momjian's avatar
Bruce Momjian committed
5258 5259 5260 5261
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5262
	appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
5263
					  fmtId(finfo->dobj.namespace->dobj.name),
5264
					  funcsig);
5265

5266
	rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
5267

5268
	appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
5269
	appendPQExpBuffer(q, "RETURNS %s%s\n    %s\n    LANGUAGE %s",
5270
					  (proretset[0] == 't') ? "SETOF " : "",
5271
					  rettypename,
5272
					  asPart->data,
5273
					  fmtId(lanname));
5274 5275

	free(rettypename);
5276

5277
	if (provolatile[0] != PROVOLATILE_VOLATILE)
5278
	{
5279
		if (provolatile[0] == PROVOLATILE_IMMUTABLE)
5280
			appendPQExpBuffer(q, " IMMUTABLE");
5281
		else if (provolatile[0] == PROVOLATILE_STABLE)
5282
			appendPQExpBuffer(q, " STABLE");
5283
		else if (provolatile[0] != PROVOLATILE_VOLATILE)
5284
		{
5285
			write_msg(NULL, "unrecognized provolatile value for function \"%s\"\n",
5286
					  finfo->dobj.name);
5287 5288
			exit_nicely();
		}
5289
	}
5290

5291 5292
	if (proisstrict[0] == 't')
		appendPQExpBuffer(q, " STRICT");
5293

5294 5295 5296
	if (prosecdef[0] == 't')
		appendPQExpBuffer(q, " SECURITY DEFINER");

5297 5298
	appendPQExpBuffer(q, ";\n");

5299 5300
	ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
				 funcsig_tag,
5301
				 finfo->dobj.namespace->dobj.name,
5302
				 finfo->usename, false,
5303 5304 5305
				 "FUNCTION", q->data, delqry->data, NULL,
				 finfo->dobj.dependencies, finfo->dobj.nDeps,
				 NULL, NULL);
5306

Bruce Momjian's avatar
Bruce Momjian committed
5307
	/* Dump Function Comments */
Bruce Momjian's avatar
Bruce Momjian committed
5308
	resetPQExpBuffer(q);
5309
	appendPQExpBuffer(q, "FUNCTION %s", funcsig);
5310
	dumpComment(fout, q->data,
5311
				finfo->dobj.namespace->dobj.name, finfo->usename,
5312 5313 5314 5315
				finfo->dobj.catId, 0, finfo->dobj.dumpId);

	dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
			funcsig, funcsig_tag,
5316
			finfo->dobj.namespace->dobj.name,
5317
			finfo->usename, finfo->proacl);
5318

5319 5320 5321
	PQclear(res);

	destroyPQExpBuffer(query);
5322 5323 5324
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delqry);
	destroyPQExpBuffer(asPart);
5325
	free(funcsig);
5326
	free(funcsig_tag);
5327 5328
	if (argnamearray)
		free(argnamearray);
5329 5330
}

5331 5332

/*
5333
 * Dump a user-defined cast
5334
 */
5335 5336
static void
dumpCast(Archive *fout, CastInfo *cast)
5337
{
5338 5339 5340 5341 5342 5343
	PQExpBuffer defqry;
	PQExpBuffer delqry;
	PQExpBuffer castsig;
	FuncInfo   *funcInfo = NULL;
	TypeInfo   *sourceInfo;
	TypeInfo   *targetInfo;
5344

5345 5346
	if (dataOnly)
		return;
5347

5348
	if (OidIsValid(cast->castfunc))
5349
	{
5350 5351 5352
		funcInfo = findFuncByOid(cast->castfunc);
		if (funcInfo == NULL)
			return;
5353 5354
	}

5355 5356 5357 5358
	/*
	 * As per discussion we dump casts if one or more of the underlying
	 * objects (the conversion function and the two data types) are not
	 * builtin AND if all of the non-builtin objects namespaces are
Bruce Momjian's avatar
Bruce Momjian committed
5359 5360
	 * included in the dump. Builtin meaning, the namespace name does not
	 * start with "pg_".
5361 5362 5363
	 */
	sourceInfo = findTypeByOid(cast->castsource);
	targetInfo = findTypeByOid(cast->casttarget);
5364

5365 5366
	if (sourceInfo == NULL || targetInfo == NULL)
		return;
5367

5368 5369 5370
	/*
	 * Skip this cast if all objects are from pg_
	 */
5371 5372 5373 5374
	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)
5375
		return;
5376

5377
	/*
Bruce Momjian's avatar
Bruce Momjian committed
5378 5379
	 * Skip cast if function isn't from pg_ and that namespace is not
	 * dumped.
5380
	 */
5381
	if (funcInfo &&
5382 5383
		strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!funcInfo->dobj.namespace->dump)
5384
		return;
5385

5386 5387 5388
	/*
	 * Same for the Source type
	 */
5389 5390
	if (strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!sourceInfo->dobj.namespace->dump)
5391
		return;
5392

5393 5394 5395
	/*
	 * and the target type.
	 */
5396 5397
	if (strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
		!targetInfo->dobj.namespace->dump)
5398
		return;
5399

5400 5401
	/* Make sure we are in proper schema (needed for getFormattedTypeName) */
	selectSourceSchema("pg_catalog");
5402

5403 5404 5405
	defqry = createPQExpBuffer();
	delqry = createPQExpBuffer();
	castsig = createPQExpBuffer();
5406

5407 5408 5409
	appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5410

5411 5412 5413
	appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
					  getFormattedTypeName(cast->castsource, zeroAsNone),
					  getFormattedTypeName(cast->casttarget, zeroAsNone));
5414

5415 5416 5417
	if (!OidIsValid(cast->castfunc))
		appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
	else
5418 5419
	{
		/*
Bruce Momjian's avatar
Bruce Momjian committed
5420 5421
		 * Always qualify the function name, in case it is not in
		 * pg_catalog schema (format_function_signature won't qualify it).
5422 5423
		 */
		appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
5424
						  fmtId(funcInfo->dobj.namespace->dobj.name));
5425
		appendPQExpBuffer(defqry, "%s",
Bruce Momjian's avatar
Bruce Momjian committed
5426
						format_function_signature(funcInfo, NULL, true));
5427
	}
5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440

	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,
5441
				 "pg_catalog", "",
5442
				 false, "CAST", defqry->data, delqry->data, NULL,
5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453
				 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);
5454 5455 5456

	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
5457
	destroyPQExpBuffer(castsig);
5458 5459
}

5460
/*
5461
 * dumpOpr
5462 5463 5464
 *	  write out a single operator definition
 */
static void
5465
dumpOpr(Archive *fout, OprInfo *oprinfo)
5466
{
5467 5468 5469 5470 5471
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer oprid;
	PQExpBuffer details;
5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501
	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;

5502
	/* Dump only operators in dumpable namespaces */
5503
	if (!oprinfo->dobj.namespace->dump || dataOnly)
5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518
		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();

5519
	/* Make sure we are in proper schema so regoperator works correctly */
5520
	selectSourceSchema(oprinfo->dobj.namespace->dobj.name);
5521 5522 5523

	if (g_fout->remoteVersion >= 70300)
	{
5524 5525 5526 5527 5528 5529 5530 5531
		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, "
5532
						  "oprcanhash, "
5533 5534 5535 5536 5537
						  "oprlsortop::pg_catalog.regoperator, "
						  "oprrsortop::pg_catalog.regoperator, "
						  "oprltcmpop::pg_catalog.regoperator, "
						  "oprgtcmpop::pg_catalog.regoperator "
						  "from pg_catalog.pg_operator "
5538 5539
						  "where oid = '%u'::pg_catalog.oid",
						  oprinfo->dobj.catId.oid);
5540 5541 5542 5543 5544
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
						  "CASE WHEN oprleft = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
5545
					   "ELSE format_type(oprleft, NULL) END as oprleft, "
5546
						  "CASE WHEN oprright = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
5547
					 "ELSE format_type(oprright, NULL) END as oprright, "
5548 5549 5550 5551
						  "oprcom, oprnegate, oprrest, oprjoin, "
						  "oprcanhash, oprlsortop, oprrsortop, "
						  "0 as oprltcmpop, 0 as oprgtcmpop "
						  "from pg_operator "
5552 5553
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565
	}
	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 "
5566 5567
						  "where oid = '%u'::oid",
						  oprinfo->dobj.catId.oid);
5568
	}
5569

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

5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609
	/* 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);

5610
	appendPQExpBuffer(details, "    PROCEDURE = %s",
5611 5612 5613
					  convertRegProcReference(oprcode));

	appendPQExpBuffer(oprid, "%s (",
5614
					  oprinfo->dobj.name);
5615 5616

	/*
Bruce Momjian's avatar
Bruce Momjian committed
5617 5618
	 * right unary means there's a left arg and left unary means there's a
	 * right arg
5619 5620 5621 5622 5623 5624 5625
	 */
	if (strcmp(oprkind, "r") == 0 ||
		strcmp(oprkind, "b") == 0)
	{
		if (g_fout->remoteVersion >= 70100)
			name = oprleft;
		else
5626 5627
			name = fmtId(oprleft);
		appendPQExpBuffer(details, ",\n    LEFTARG = %s", name);
5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638
		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
5639 5640
			name = fmtId(oprright);
		appendPQExpBuffer(details, ",\n    RIGHTARG = %s", name);
5641 5642 5643 5644 5645
		appendPQExpBuffer(oprid, ", %s)", name);
	}
	else
		appendPQExpBuffer(oprid, ", NONE)");

5646
	name = convertOperatorReference(oprcom);
5647
	if (name)
5648
		appendPQExpBuffer(details, ",\n    COMMUTATOR = %s", name);
5649

5650
	name = convertOperatorReference(oprnegate);
5651
	if (name)
5652
		appendPQExpBuffer(details, ",\n    NEGATOR = %s", name);
5653 5654

	if (strcmp(oprcanhash, "t") == 0)
5655
		appendPQExpBuffer(details, ",\n    HASHES");
5656 5657 5658

	name = convertRegProcReference(oprrest);
	if (name)
5659
		appendPQExpBuffer(details, ",\n    RESTRICT = %s", name);
5660 5661 5662

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

5665
	name = convertOperatorReference(oprlsortop);
5666
	if (name)
5667
		appendPQExpBuffer(details, ",\n    SORT1 = %s", name);
5668

5669
	name = convertOperatorReference(oprrsortop);
5670
	if (name)
5671
		appendPQExpBuffer(details, ",\n    SORT2 = %s", name);
5672

5673
	name = convertOperatorReference(oprltcmpop);
5674
	if (name)
5675
		appendPQExpBuffer(details, ",\n    LTCMP = %s", name);
5676

5677
	name = convertOperatorReference(oprgtcmpop);
5678
	if (name)
5679
		appendPQExpBuffer(details, ",\n    GTCMP = %s", name);
5680

Bruce Momjian's avatar
Bruce Momjian committed
5681 5682 5683 5684
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5685
	appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
5686
					  fmtId(oprinfo->dobj.namespace->dobj.name),
5687 5688
					  oprid->data);

5689
	appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n",
5690
					  oprinfo->dobj.name, details->data);
5691

5692
	ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
5693 5694
				 oprinfo->dobj.name,
				 oprinfo->dobj.namespace->dobj.name, oprinfo->usename,
5695
				 false, "OPERATOR", q->data, delq->data, NULL,
5696 5697
				 oprinfo->dobj.dependencies, oprinfo->dobj.nDeps,
				 NULL, NULL);
5698

Bruce Momjian's avatar
Bruce Momjian committed
5699
	/* Dump Operator Comments */
5700 5701 5702
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
	dumpComment(fout, q->data,
5703
				oprinfo->dobj.namespace->dobj.name, oprinfo->usename,
5704
				oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731

	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
5732 5733 5734
		char	   *name;
		char	   *paren;
		bool		inquote;
5735 5736 5737 5738 5739

		name = strdup(proc);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
5740
		{
5741
			if (*paren == '(' && !inquote)
5742
			{
5743 5744
				*paren = '\0';
				break;
5745
			}
5746 5747
			if (*paren == '"')
				inquote = !inquote;
5748
		}
5749 5750 5751 5752
		return name;
	}

	/* REGPROC before 7.3 does not quote its result */
5753
	return fmtId(proc);
5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765
}

/*
 * 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 *
5766
convertOperatorReference(const char *opr)
5767
{
Bruce Momjian's avatar
Bruce Momjian committed
5768
	OprInfo    *oprInfo;
5769 5770 5771 5772 5773 5774 5775

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

	if (g_fout->remoteVersion >= 70300)
	{
5776
		char	   *name;
Bruce Momjian's avatar
Bruce Momjian committed
5777 5778
		char	   *paren;
		bool		inquote;
5779

5780 5781 5782 5783
		name = strdup(opr);
		/* find non-double-quoted left paren */
		inquote = false;
		for (paren = name; *paren; paren++)
5784
		{
5785
			if (*paren == '(' && !inquote)
5786
			{
5787 5788
				*paren = '\0';
				break;
5789
			}
5790 5791
			if (*paren == '"')
				inquote = !inquote;
5792
		}
5793
		return name;
5794
	}
5795

5796 5797 5798
	oprInfo = findOprByOid(atooid(opr));
	if (oprInfo == NULL)
	{
5799
		write_msg(NULL, "WARNING: could not find operator with OID %s\n",
5800
				  opr);
5801
		return NULL;
5802
	}
5803
	return oprInfo->dobj.name;
5804 5805 5806
}

/*
5807
 * dumpOpclass
5808 5809 5810
 *	  write out a single operator class definition
 */
static void
5811
dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
5812
{
5813 5814 5815
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838
	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;

5839
	/* Dump only opclasses in dumpable namespaces */
5840
	if (!opcinfo->dobj.namespace->dump || dataOnly)
5841 5842
		return;

5843 5844 5845 5846 5847 5848 5849 5850
	/*
	 * 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;

5851 5852 5853 5854
	query = createPQExpBuffer();
	q = createPQExpBuffer();
	delq = createPQExpBuffer();

5855
	/* Make sure we are in proper schema so regoperator works correctly */
5856
	selectSourceSchema(opcinfo->dobj.namespace->dobj.name);
5857 5858 5859 5860 5861

	/* Get additional fields from the pg_opclass row */
	appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
					  "opckeytype::pg_catalog.regtype, "
					  "opcdefault, "
Bruce Momjian's avatar
Bruce Momjian committed
5862
	"(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
5863
					  "FROM pg_catalog.pg_opclass "
5864 5865
					  "WHERE oid = '%u'::pg_catalog.oid",
					  opcinfo->dobj.catId.oid);
5866 5867

	res = PQexec(g_conn, query->data);
5868
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886

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

Bruce Momjian's avatar
Bruce Momjian committed
5890 5891 5892 5893
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
5894
	appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
5895
					  fmtId(opcinfo->dobj.namespace->dobj.name));
5896
	appendPQExpBuffer(delq, ".%s",
5897
					  fmtId(opcinfo->dobj.name));
5898
	appendPQExpBuffer(delq, " USING %s;\n",
5899
					  fmtId(amname));
5900 5901

	/* Build the fixed portion of the CREATE command */
5902
	appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n    ",
5903
					  fmtId(opcinfo->dobj.name));
5904 5905
	if (strcmp(opcdefault, "t") == 0)
		appendPQExpBuffer(q, "DEFAULT ");
5906
	appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n    ",
5907
					  opcintype,
5908
					  fmtId(amname));
5909 5910 5911 5912 5913

	needComma = false;

	if (strcmp(opckeytype, "-") != 0)
	{
5914
		appendPQExpBuffer(q, "STORAGE %s",
5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928
						  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 "
5929
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
5930
					  "ORDER BY amopstrategy",
5931
					  opcinfo->dobj.catId.oid);
5932 5933

	res = PQexec(g_conn, query->data);
5934
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948

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

5951
		appendPQExpBuffer(q, "OPERATOR %s %s",
5952 5953
						  amopstrategy, amopopr);
		if (strcmp(amopreqcheck, "t") == 0)
5954
			appendPQExpBuffer(q, " RECHECK");
5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968

		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 "
5969
					  "WHERE amopclaid = '%u'::pg_catalog.oid "
5970
					  "ORDER BY amprocnum",
5971
					  opcinfo->dobj.catId.oid);
5972 5973

	res = PQexec(g_conn, query->data);
5974
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986

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

5989
		appendPQExpBuffer(q, "FUNCTION %s %s",
5990 5991 5992 5993 5994 5995 5996
						  amprocnum, amproc);

		needComma = true;
	}

	PQclear(res);

5997
	appendPQExpBuffer(q, ";\n");
5998

5999
	ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
6000 6001
				 opcinfo->dobj.name,
				 opcinfo->dobj.namespace->dobj.name, opcinfo->usename,
6002
				 false, "OPERATOR CLASS", q->data, delq->data, NULL,
6003 6004
				 opcinfo->dobj.dependencies, opcinfo->dobj.nDeps,
				 NULL, NULL);
6005

6006 6007 6008
	/* Dump Operator Class Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "OPERATOR CLASS %s",
6009
					  fmtId(opcinfo->dobj.name));
6010 6011 6012 6013
	appendPQExpBuffer(q, " USING %s",
					  fmtId(amname));
	dumpComment(fout, q->data,
				NULL, opcinfo->usename,
6014
				opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
6015 6016

	free(amname);
6017 6018 6019 6020 6021
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

6022
/*
6023
 * dumpConversion
6024 6025 6026
 *	  write out a single conversion definition
 */
static void
6027
dumpConversion(Archive *fout, ConvInfo *convinfo)
6028
{
6029 6030 6031 6032
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045
	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;

6046
	/* Dump only conversions in dumpable namespaces */
6047
	if (!convinfo->dobj.namespace->dump || dataOnly)
6048 6049 6050 6051 6052 6053 6054
		return;

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

6055
	/* Make sure we are in proper schema */
6056
	selectSourceSchema(convinfo->dobj.namespace->dobj.name);
6057 6058

	/* Get conversion-specific details */
6059
	appendPQExpBuffer(query, "SELECT conname, "
Bruce Momjian's avatar
Bruce Momjian committed
6060 6061
	 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
	   "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
6062 6063
					  "conproc, condefault "
					  "FROM pg_catalog.pg_conversion c "
6064 6065
					  "WHERE c.oid = '%u'::pg_catalog.oid",
					  convinfo->dobj.catId.oid);
6066 6067

	res = PQexec(g_conn, query->data);
6068
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095

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

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

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

	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delq, "DROP CONVERSION %s",
6096
					  fmtId(convinfo->dobj.namespace->dobj.name));
6097
	appendPQExpBuffer(delq, ".%s;\n",
6098
					  fmtId(convinfo->dobj.name));
6099 6100

	appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
Bruce Momjian's avatar
Bruce Momjian committed
6101 6102
					  (condefault) ? "DEFAULT " : "",
					  fmtId(convinfo->dobj.name));
6103 6104 6105 6106 6107
	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);
6108

6109
	ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
6110 6111
				 convinfo->dobj.name,
				 convinfo->dobj.namespace->dobj.name, convinfo->usename,
6112
				 false, "CONVERSION", q->data, delq->data, NULL,
6113 6114
				 convinfo->dobj.dependencies, convinfo->dobj.nDeps,
				 NULL, NULL);
6115 6116 6117

	/* Dump Conversion Comments */
	resetPQExpBuffer(q);
6118
	appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->dobj.name));
6119
	dumpComment(fout, q->data,
6120
				convinfo->dobj.namespace->dobj.name, convinfo->usename,
6121
				convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
6122

6123
	PQclear(res);
6124

6125 6126 6127 6128
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6129 6130
}

6131 6132 6133 6134 6135 6136
/*
 * format_aggregate_signature: generate aggregate name and argument list
 *
 * The argument type names are qualified if needed.  The aggregate name
 * is never qualified.
 */
6137
static char *
6138
format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
6139 6140 6141 6142
{
	PQExpBufferData buf;

	initPQExpBuffer(&buf);
6143 6144
	if (honor_quotes)
		appendPQExpBuffer(&buf, "%s",
6145
						  fmtId(agginfo->aggfn.dobj.name));
6146
	else
6147
		appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name);
6148

6149
	/* If using regtype or format_type, fmtbasetype is already quoted */
6150 6151
	if (fout->remoteVersion >= 70100)
	{
6152
		if (agginfo->anybasetype)
6153 6154
			appendPQExpBuffer(&buf, "(*)");
		else
6155
			appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
6156
	}
6157 6158
	else
	{
6159
		if (agginfo->anybasetype)
6160 6161 6162
			appendPQExpBuffer(&buf, "(*)");
		else
			appendPQExpBuffer(&buf, "(%s)",
6163
							  fmtId(agginfo->fmtbasetype));
6164 6165 6166
	}

	return buf.data;
6167 6168 6169
}

/*
6170
 * dumpAgg
6171 6172 6173
 *	  write out a single aggregate definition
 */
static void
6174
dumpAgg(Archive *fout, AggInfo *agginfo)
6175
{
6176 6177 6178 6179
	PQExpBuffer query;
	PQExpBuffer q;
	PQExpBuffer delq;
	PQExpBuffer details;
6180
	char	   *aggsig;
6181
	char	   *aggsig_tag;
6182 6183 6184 6185 6186 6187
	PGresult   *res;
	int			ntups;
	int			i_aggtransfn;
	int			i_aggfinalfn;
	int			i_aggtranstype;
	int			i_agginitval;
6188
	int			i_anybasetype;
6189
	int			i_fmtbasetype;
6190 6191 6192 6193 6194 6195 6196
	int			i_convertok;
	const char *aggtransfn;
	const char *aggfinalfn;
	const char *aggtranstype;
	const char *agginitval;
	bool		convertok;

6197
	/* Dump only aggs in dumpable namespaces */
6198
	if (!agginfo->aggfn.dobj.namespace->dump || dataOnly)
6199 6200 6201 6202 6203 6204 6205
		return;

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

6206
	/* Make sure we are in proper schema */
6207
	selectSourceSchema(agginfo->aggfn.dobj.namespace->dobj.name);
6208 6209 6210 6211 6212

	/* Get aggregate-specific details */
	if (g_fout->remoteVersion >= 70300)
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, "
6213
						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
6214
						  "agginitval, "
6215
						  "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
Bruce Momjian's avatar
Bruce Momjian committed
6216
					"proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
6217
						  "'t'::boolean as convertok "
Bruce Momjian's avatar
Bruce Momjian committed
6218
				  "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
6219
						  "where a.aggfnoid = p.oid "
6220 6221
						  "and p.oid = '%u'::pg_catalog.oid",
						  agginfo->aggfn.dobj.catId.oid);
6222 6223 6224 6225
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
Bruce Momjian's avatar
Bruce Momjian committed
6226
					  "format_type(aggtranstype, NULL) as aggtranstype, "
6227
						  "agginitval, "
6228
						  "aggbasetype = 0 as anybasetype, "
6229
						  "CASE WHEN aggbasetype = 0 THEN '-' "
Bruce Momjian's avatar
Bruce Momjian committed
6230
			   "ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
6231
						  "'t'::boolean as convertok "
6232
						  "from pg_aggregate "
6233 6234
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6235 6236 6237 6238 6239 6240 6241
	}
	else
	{
		appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
						  "aggfinalfn, "
						  "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
						  "agginitval1 as agginitval, "
6242
						  "aggbasetype = 0 as anybasetype, "
6243
						  "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
6244 6245
						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
						  "from pg_aggregate "
6246 6247
						  "where oid = '%u'::oid",
						  agginfo->aggfn.dobj.catId.oid);
6248
	}
6249

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

6253 6254 6255 6256 6257 6258 6259 6260
	/* 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
6261

6262 6263 6264 6265
	i_aggtransfn = PQfnumber(res, "aggtransfn");
	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
	i_aggtranstype = PQfnumber(res, "aggtranstype");
	i_agginitval = PQfnumber(res, "agginitval");
6266
	i_anybasetype = PQfnumber(res, "anybasetype");
6267
	i_fmtbasetype = PQfnumber(res, "fmtbasetype");
6268
	i_convertok = PQfnumber(res, "convertok");
6269

6270 6271 6272 6273
	aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
	agginitval = PQgetvalue(res, 0, i_agginitval);
6274
	/* we save anybasetype for format_aggregate_signature */
6275
	agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
6276
	/* we save fmtbasetype for format_aggregate_signature */
6277
	agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
6278
	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
6279

6280 6281
	aggsig = format_aggregate_signature(agginfo, fout, true);
	aggsig_tag = format_aggregate_signature(agginfo, fout, false);
6282

6283 6284 6285
	if (!convertok)
	{
		write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
6286
				  aggsig);
6287 6288
		return;
	}
6289

6290 6291 6292
	if (g_fout->remoteVersion >= 70300)
	{
		/* If using 7.3's regproc or regtype, data is already quoted */
6293
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6294 6295
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6296 6297 6298 6299 6300 6301
						  aggtransfn,
						  aggtranstype);
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* format_type quotes, regproc does not */
6302
		appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
6303 6304
						  agginfo->anybasetype ? "'any'" :
						  agginfo->fmtbasetype,
6305
						  fmtId(aggtransfn),
6306 6307 6308 6309 6310
						  aggtranstype);
	}
	else
	{
		/* need quotes all around */
6311
		appendPQExpBuffer(details, "    BASETYPE = %s,\n",
6312
						  agginfo->anybasetype ? "'any'" :
6313 6314 6315 6316 6317
						  fmtId(agginfo->fmtbasetype));
		appendPQExpBuffer(details, "    SFUNC = %s,\n",
						  fmtId(aggtransfn));
		appendPQExpBuffer(details, "    STYPE = %s",
						  fmtId(aggtranstype));
6318
	}
6319

6320 6321
	if (!PQgetisnull(res, 0, i_agginitval))
	{
6322 6323
		appendPQExpBuffer(details, ",\n    INITCOND = ");
		appendStringLiteral(details, agginitval, true);
6324
	}
6325

6326 6327
	if (strcmp(aggfinalfn, "-") != 0)
	{
6328
		appendPQExpBuffer(details, ",\n    FINALFUNC = %s",
6329 6330
						  aggfinalfn);
	}
6331

Bruce Momjian's avatar
Bruce Momjian committed
6332 6333 6334 6335
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
6336
	appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
6337
					  fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
6338
					  aggsig);
6339

6340
	appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
6341
					  fmtId(agginfo->aggfn.dobj.name),
6342
					  details->data);
6343

6344 6345
	ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
				 aggsig_tag,
Bruce Momjian's avatar
Bruce Momjian committed
6346
		agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.usename,
6347
				 false, "AGGREGATE", q->data, delq->data, NULL,
Bruce Momjian's avatar
Bruce Momjian committed
6348
			 agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps,
6349
				 NULL, NULL);
6350

Bruce Momjian's avatar
Bruce Momjian committed
6351
	/* Dump Aggregate Comments */
6352
	resetPQExpBuffer(q);
6353
	appendPQExpBuffer(q, "AGGREGATE %s", aggsig);
6354
	dumpComment(fout, q->data,
Bruce Momjian's avatar
Bruce Momjian committed
6355
		agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.usename,
6356 6357 6358 6359
				agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);

	/*
	 * Since there is no GRANT ON AGGREGATE syntax, we have to make the
Bruce Momjian's avatar
Bruce Momjian committed
6360 6361
	 * ACL command look like a function's GRANT; in particular this
	 * affects the syntax for aggregates on ANY.
6362 6363 6364 6365
	 */
	free(aggsig);
	free(aggsig_tag);

6366 6367
	aggsig = format_function_signature(&agginfo->aggfn, NULL, true);
	aggsig_tag = format_function_signature(&agginfo->aggfn, NULL, false);
6368 6369 6370 6371

	dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
			"FUNCTION",
			aggsig, aggsig_tag,
6372
			agginfo->aggfn.dobj.namespace->dobj.name,
6373 6374 6375 6376
			agginfo->aggfn.usename, agginfo->aggfn.proacl);

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

6378
	PQclear(res);
6379

6380
	destroyPQExpBuffer(query);
6381 6382 6383
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(details);
6384 6385
}

6386

6387
/*----------
6388 6389
 * Write out grant/revoke information
 *
6390 6391
 * 'objCatId' is the catalog ID of the underlying object.
 * 'objDumpId' is the dump ID of the underlying object.
6392
 * 'type' must be TABLE, FUNCTION, LANGUAGE, or SCHEMA.
Bruce Momjian's avatar
Bruce Momjian committed
6393
 * 'name' is the formatted name of the object.	Must be quoted etc. already.
6394
 * 'tag' is the tag for the archive entry (typ. unquoted name of object).
6395
 * 'nspname' is the namespace the object is in (NULL if none).
6396
 * 'owner' is the owner, NULL if there is no owner (for languages).
6397 6398
 * 'acls' is the string read out of the fooacl system catalog field;
 * it will be parsed here.
6399
 *----------
Bruce Momjian's avatar
Bruce Momjian committed
6400
 */
6401
static void
6402 6403
dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
		const char *type, const char *name,
6404
		const char *tag, const char *nspname, const char *owner,
6405
		const char *acls)
Bruce Momjian's avatar
Bruce Momjian committed
6406
{
6407
	PQExpBuffer sql;
6408

6409 6410 6411
	/* Do nothing if ACL dump is not enabled */
	if (dataOnly || aclsSkip)
		return;
6412

6413
	sql = createPQExpBuffer();
6414

6415
	if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql))
6416
	{
6417
		write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
6418 6419
				  acls, name, type);
		exit_nicely();
Bruce Momjian's avatar
Bruce Momjian committed
6420
	}
6421

6422
	if (sql->len > 0)
6423 6424
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
					 tag, nspname,
6425
					 owner ? owner : "",
6426
					 false, "ACL", sql->data, "", NULL,
6427 6428
					 &(objDumpId), 1,
					 NULL, NULL);
6429 6430

	destroyPQExpBuffer(sql);
Bruce Momjian's avatar
Bruce Momjian committed
6431 6432
}

6433
/*
6434 6435
 * dumpTable
 *	  write out to fout the declarations (not data) of a user-defined table
6436
 */
6437 6438
static void
dumpTable(Archive *fout, TableInfo *tbinfo)
6439
{
6440
	char	   *namecopy;
6441

6442
	if (tbinfo->dump)
6443
	{
6444 6445
		if (tbinfo->relkind == RELKIND_SEQUENCE)
			dumpSequence(fout, tbinfo);
6446
		else if (!dataOnly)
6447 6448 6449
			dumpTableSchema(fout, tbinfo);

		/* Handle the ACL here */
6450
		namecopy = strdup(fmtId(tbinfo->dobj.name));
6451
		dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
6452 6453
				namecopy, tbinfo->dobj.name,
				tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
6454 6455
				tbinfo->relacl);
		free(namecopy);
6456
	}
6457
}
6458

6459
/*
6460
 * dumpTableSchema
6461
 *	  write the declaration (not data) of one user-defined table or view
6462
 */
6463
static void
6464
dumpTableSchema(Archive *fout, TableInfo *tbinfo)
6465
{
6466
	PQExpBuffer query = createPQExpBuffer();
6467
	PQExpBuffer q = createPQExpBuffer();
6468
	PQExpBuffer delq = createPQExpBuffer();
6469
	PGresult   *res;
6470
	int			numParents;
6471
	TableInfo **parents;
Bruce Momjian's avatar
Bruce Momjian committed
6472
	int			actual_atts;	/* number of attrs in this CREATE statment */
6473
	char	   *reltypename;
6474
	char	   *storage;
6475 6476
	int			j,
				k;
6477

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

6481 6482
	/* Is it a table or a view? */
	if (tbinfo->relkind == RELKIND_VIEW)
6483
	{
6484
		char	   *viewdef;
6485

6486
		reltypename = "VIEW";
6487

6488 6489 6490
		/* Fetch the view definition */
		if (g_fout->remoteVersion >= 70300)
		{
6491 6492 6493 6494
			/* 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);
6495 6496 6497
		}
		else
		{
6498
			appendPQExpBuffer(query, "SELECT definition as viewdef "
6499
							  " from pg_views where viewname = ");
6500
			appendStringLiteral(query, tbinfo->dobj.name, true);
6501 6502
			appendPQExpBuffer(query, ";");
		}
6503

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

6507 6508 6509 6510
		if (PQntuples(res) != 1)
		{
			if (PQntuples(res) < 1)
				write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
6511
						  tbinfo->dobj.name);
6512
			else
6513
				write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
6514
						  tbinfo->dobj.name);
6515 6516
			exit_nicely();
		}
Bruce Momjian's avatar
Bruce Momjian committed
6517

6518
		viewdef = PQgetvalue(res, 0, 0);
6519

6520 6521 6522
		if (strlen(viewdef) == 0)
		{
			write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
6523
					  tbinfo->dobj.name);
6524 6525
			exit_nicely();
		}
6526

Bruce Momjian's avatar
Bruce Momjian committed
6527 6528 6529 6530
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6531
		appendPQExpBuffer(delq, "DROP VIEW %s.",
6532
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6533
		appendPQExpBuffer(delq, "%s;\n",
6534
						  fmtId(tbinfo->dobj.name));
6535

6536
		appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
6537
						  fmtId(tbinfo->dobj.name), viewdef);
6538

6539 6540 6541 6542 6543 6544
		PQclear(res);
	}
	else
	{
		reltypename = "TABLE";
		numParents = tbinfo->numParents;
6545
		parents = tbinfo->parents;
6546

Bruce Momjian's avatar
Bruce Momjian committed
6547 6548 6549 6550
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
6551
		appendPQExpBuffer(delq, "DROP TABLE %s.",
6552
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6553
		appendPQExpBuffer(delq, "%s;\n",
6554
						  fmtId(tbinfo->dobj.name));
6555

6556
		appendPQExpBuffer(q, "CREATE TABLE %s (",
6557
						  fmtId(tbinfo->dobj.name));
6558 6559 6560
		actual_atts = 0;
		for (j = 0; j < tbinfo->numatts; j++)
		{
6561 6562
			/* Is this one of the table's own attrs, and not dropped ? */
			if (!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j])
6563 6564 6565
			{
				/* Format properly if not first attr */
				if (actual_atts > 0)
6566 6567
					appendPQExpBuffer(q, ",");
				appendPQExpBuffer(q, "\n    ");
6568

6569
				/* Attribute name */
6570
				appendPQExpBuffer(q, "%s ",
6571
								  fmtId(tbinfo->attnames[j]));
6572

6573
				/* Attribute type */
6574
				if (g_fout->remoteVersion >= 70100)
6575
				{
Bruce Momjian's avatar
Bruce Momjian committed
6576
					char	   *typname = tbinfo->atttypnames[j];
6577 6578 6579 6580 6581 6582 6583 6584 6585 6586

					if (tbinfo->attisserial[j])
					{
						if (strcmp(typname, "integer") == 0)
							typname = "serial";
						else if (strcmp(typname, "bigint") == 0)
							typname = "bigserial";
					}
					appendPQExpBuffer(q, "%s", typname);
				}
6587
				else
6588 6589
				{
					/* If no format_type, fake it */
6590 6591 6592
					appendPQExpBuffer(q, "%s",
									  myFormatType(tbinfo->atttypnames[j],
												   tbinfo->atttypmod[j]));
6593
				}
6594

6595
				/*
Bruce Momjian's avatar
Bruce Momjian committed
6596 6597
				 * Default value --- suppress if inherited, serial, or to
				 * be printed separately.
6598 6599
				 */
				if (tbinfo->attrdefs[j] != NULL &&
6600
					!tbinfo->inhAttrDef[j] &&
6601 6602
					!tbinfo->attisserial[j] &&
					!tbinfo->attrdefs[j]->separate)
6603
					appendPQExpBuffer(q, " DEFAULT %s",
6604
									  tbinfo->attrdefs[j]->adef_expr);
Bruce Momjian's avatar
Bruce Momjian committed
6605

6606 6607 6608 6609 6610 6611 6612 6613 6614
				/*
				 * Not Null constraint --- suppress if inherited
				 *
				 * Note: we could suppress this for serial columns since
				 * SERIAL implies NOT NULL.  We choose not to for forward
				 * compatibility, since there has been some talk of making
				 * SERIAL not imply NOT NULL, in which case the explicit
				 * specification would be needed.
				 */
6615 6616
				if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
					appendPQExpBuffer(q, " NOT NULL");
6617

6618
				actual_atts++;
6619
			}
6620
		}
6621

6622
		/*
6623
		 * Add non-inherited CHECK constraints, if any.
6624
		 */
6625
		for (j = 0; j < tbinfo->ncheck; j++)
6626
		{
6627
			ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
6628

6629 6630
			if (constr->coninherited || constr->separate)
				continue;
6631

6632 6633
			if (actual_atts > 0)
				appendPQExpBuffer(q, ",\n    ");
6634

6635
			appendPQExpBuffer(q, "CONSTRAINT %s ",
6636
							  fmtId(constr->dobj.name));
6637
			appendPQExpBuffer(q, "%s", constr->condef);
6638

6639
			actual_atts++;
6640
		}
6641

6642
		appendPQExpBuffer(q, "\n)");
6643

6644 6645 6646 6647 6648
		if (numParents > 0)
		{
			appendPQExpBuffer(q, "\nINHERITS (");
			for (k = 0; k < numParents; k++)
			{
6649
				TableInfo  *parentRel = parents[k];
6650

6651 6652
				if (k > 0)
					appendPQExpBuffer(q, ", ");
6653
				if (parentRel->dobj.namespace != tbinfo->dobj.namespace)
6654
					appendPQExpBuffer(q, "%s.",
Bruce Momjian's avatar
Bruce Momjian committed
6655
							fmtId(parentRel->dobj.namespace->dobj.name));
6656
				appendPQExpBuffer(q, "%s",
6657
								  fmtId(parentRel->dobj.name));
6658 6659 6660
			}
			appendPQExpBuffer(q, ")");
		}
6661

6662 6663
		/* Output tablespace clause if different from parent schema's */
		if (strcmp(tbinfo->reltablespace,
6664 6665
				   tbinfo->dobj.namespace->nsptablespace) != 0)
		{
6666 6667 6668 6669 6670 6671
			if (strlen(tbinfo->reltablespace) != 0)
				appendPQExpBuffer(q, " TABLESPACE %s",
								  fmtId(tbinfo->reltablespace));
			else if (strlen(dbDefaultTableSpace) != 0)
				appendPQExpBuffer(q, " TABLESPACE %s",
								  fmtId(dbDefaultTableSpace));
6672 6673
		}

6674
		appendPQExpBuffer(q, ";\n");
6675

6676
		/* Loop dumping statistics and storage statements */
Bruce Momjian's avatar
Bruce Momjian committed
6677
		for (j = 0; j < tbinfo->numatts; j++)
6678
		{
6679
			/*
Bruce Momjian's avatar
Bruce Momjian committed
6680 6681 6682
			 * 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)
6683
			 */
6684 6685
			if (tbinfo->attstattarget[j] >= 0 &&
				!tbinfo->attisdropped[j])
6686
			{
6687
				appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
6688
								  fmtId(tbinfo->dobj.name));
6689
				appendPQExpBuffer(q, "ALTER COLUMN %s ",
6690
								  fmtId(tbinfo->attnames[j]));
6691 6692 6693
				appendPQExpBuffer(q, "SET STATISTICS %d;\n",
								  tbinfo->attstattarget[j]);
			}
6694 6695

			/*
Bruce Momjian's avatar
Bruce Momjian committed
6696 6697 6698
			 * Dump per-column storage information.  The statement is only
			 * dumped if the storage has been changed from the type's
			 * default.
6699
			 */
Bruce Momjian's avatar
Bruce Momjian committed
6700
			if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
6701
			{
Bruce Momjian's avatar
Bruce Momjian committed
6702 6703
				switch (tbinfo->attstorage[j])
				{
6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718
					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
6719 6720 6721 6722 6723 6724 6725

				/*
				 * Only dump the statement if it's a storage type we
				 * recognize
				 */
				if (storage != NULL)
				{
6726
					appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
6727
									  fmtId(tbinfo->dobj.name));
6728 6729 6730 6731 6732 6733
					appendPQExpBuffer(q, "ALTER COLUMN %s ",
									  fmtId(tbinfo->attnames[j]));
					appendPQExpBuffer(q, "SET STORAGE %s;\n",
									  storage);
				}
			}
6734
		}
6735 6736
	}

6737
	ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
6738 6739
				 tbinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
Bruce Momjian's avatar
Bruce Momjian committed
6740
		   (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
6741 6742 6743
				 reltypename, q->data, delq->data, NULL,
				 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
				 NULL, NULL);
6744

6745
	/* Dump Table Comments */
6746
	dumpTableComment(fout, tbinfo, reltypename);
6747

6748 6749 6750
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
6751 6752
}

6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768
/*
 * 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
6769
	if (tbinfo->inhAttrDef[adnum - 1] || tbinfo->attisserial[adnum - 1])
6770 6771 6772 6773 6774 6775
		return;

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

	appendPQExpBuffer(q, "ALTER TABLE %s ",
6776
					  fmtId(tbinfo->dobj.name));
6777 6778 6779 6780 6781
	appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
					  fmtId(tbinfo->attnames[adnum - 1]),
					  adinfo->adef_expr);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
6782 6783
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
6784 6785
	 */
	appendPQExpBuffer(delq, "ALTER TABLE %s.",
6786
					  fmtId(tbinfo->dobj.namespace->dobj.name));
6787
	appendPQExpBuffer(delq, "%s ",
6788
					  fmtId(tbinfo->dobj.name));
6789 6790 6791 6792 6793
	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],
6794
				 tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
6795
				 false, "DEFAULT", q->data, delq->data, NULL,
6796 6797 6798 6799 6800 6801 6802
				 adinfo->dobj.dependencies, adinfo->dobj.nDeps,
				 NULL, NULL);

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
}

6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813
/*
 * 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)
6814
		return tblInfo->attnames[attrnum - 1];
6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831
	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";
	}
6832
	write_msg(NULL, "invalid column number %d for table \"%s\"\n",
6833
			  attrnum, tblInfo->dobj.name);
6834
	exit_nicely();
6835 6836 6837
	return NULL;				/* keep compiler quiet */
}

6838
/*
6839 6840
 * dumpIndex
 *	  write out to fout a user-defined index
6841
 */
6842 6843
static void
dumpIndex(Archive *fout, IndxInfo *indxinfo)
6844
{
6845 6846 6847
	TableInfo  *tbinfo = indxinfo->indextable;
	PQExpBuffer q;
	PQExpBuffer delq;
6848

6849 6850
	if (dataOnly)
		return;
6851

6852 6853
	q = createPQExpBuffer();
	delq = createPQExpBuffer();
6854

6855 6856 6857 6858 6859 6860 6861 6862
	/*
	 * If there's an associated constraint, don't dump the index per se,
	 * but do dump any comment for it.
	 */
	if (indxinfo->indexconstraint == 0)
	{
		/* Plain secondary index */
		appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
6863

6864 6865 6866 6867
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
6868
							  fmtId(tbinfo->dobj.name));
6869
			appendPQExpBuffer(q, " ON %s;\n",
6870
							  fmtId(indxinfo->dobj.name));
6871
		}
6872

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

6882
		ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
6883 6884
					 indxinfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
6885
					 tbinfo->usename, false,
6886 6887 6888 6889
					 "INDEX", q->data, delq->data, NULL,
					 indxinfo->dobj.dependencies, indxinfo->dobj.nDeps,
					 NULL, NULL);
	}
6890

6891 6892 6893
	/* Dump Index Comments */
	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "INDEX %s",
6894
					  fmtId(indxinfo->dobj.name));
6895
	dumpComment(fout, q->data,
6896
				tbinfo->dobj.namespace->dobj.name,
6897 6898
				tbinfo->usename,
				indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId);
6899

6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933
	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)
		{
			write_msg(NULL, "missing index for constraint %s\n",
6934
					  coninfo->dobj.name);
6935 6936
			exit_nicely();
		}
6937

6938
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
6939
						  fmtId(tbinfo->dobj.name));
6940
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s (",
6941
						  fmtId(coninfo->dobj.name),
Bruce Momjian's avatar
Bruce Momjian committed
6942
					 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
6943 6944

		for (k = 0; k < indxinfo->indnkeys; k++)
6945
		{
6946 6947
			int			indkey = (int) indxinfo->indkeys[k];
			const char *attname;
6948

6949 6950 6951
			if (indkey == InvalidAttrNumber)
				break;
			attname = getAttrName(indkey, tbinfo);
6952

6953 6954 6955 6956
			appendPQExpBuffer(q, "%s%s",
							  (k == 0) ? "" : ", ",
							  fmtId(attname));
		}
6957

6958 6959
		appendPQExpBuffer(q, ")");

6960 6961
		/* Output tablespace clause if different from parent table's */
		if (strcmp(indxinfo->tablespace,
6962 6963
				   indxinfo->indextable->reltablespace) != 0)
		{
6964 6965 6966 6967 6968 6969
			if (strlen(indxinfo->tablespace) != 0)
				appendPQExpBuffer(q, " USING INDEX TABLESPACE %s",
								  fmtId(indxinfo->tablespace));
			else if (strlen(dbDefaultTableSpace) != 0)
				appendPQExpBuffer(q, " USING INDEX TABLESPACE %s",
								  fmtId(dbDefaultTableSpace));
6970 6971 6972
		}

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

6974 6975 6976 6977
		/* If the index is clustered, we need to record that. */
		if (indxinfo->indisclustered)
		{
			appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
6978
							  fmtId(tbinfo->dobj.name));
6979
			appendPQExpBuffer(q, " ON %s;\n",
6980
							  fmtId(indxinfo->dobj.name));
6981
		}
6982

6983
		/*
Bruce Momjian's avatar
Bruce Momjian committed
6984 6985
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
6986 6987
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
6988
						  fmtId(tbinfo->dobj.namespace->dobj.name));
6989
		appendPQExpBuffer(delq, "%s ",
6990
						  fmtId(tbinfo->dobj.name));
6991
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
6992
						  fmtId(coninfo->dobj.name));
6993

6994
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
6995 6996
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
6997
					 tbinfo->usename, false,
6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008
					 "CONSTRAINT", q->data, delq->data, NULL,
					 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
					 NULL, NULL);
	}
	else if (coninfo->contype == 'f')
	{
		/*
		 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that
		 * the current table data is not processed
		 */
		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
7009
						  fmtId(tbinfo->dobj.name));
7010
		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7011
						  fmtId(coninfo->dobj.name),
7012
						  coninfo->condef);
7013

7014 7015 7016 7017 7018
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
		appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
7019
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7020
		appendPQExpBuffer(delq, "%s ",
7021
						  fmtId(tbinfo->dobj.name));
7022
		appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7023
						  fmtId(coninfo->dobj.name));
7024

7025
		ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7026 7027
					 coninfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name,
7028
					 tbinfo->usename, false,
7029 7030 7031 7032 7033 7034 7035
					 "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 */
7036

7037 7038 7039 7040 7041
		/* 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",
7042
							  fmtId(tbinfo->dobj.name));
7043
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7044
							  fmtId(coninfo->dobj.name),
7045
							  coninfo->condef);
7046

7047 7048 7049 7050 7051
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER TABLE %s.",
7052
							  fmtId(tbinfo->dobj.namespace->dobj.name));
7053
			appendPQExpBuffer(delq, "%s ",
7054
							  fmtId(tbinfo->dobj.name));
7055
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7056
							  fmtId(coninfo->dobj.name));
7057

7058
			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7059 7060
						 coninfo->dobj.name,
						 tbinfo->dobj.namespace->dobj.name,
7061
						 tbinfo->usename, false,
7062 7063 7064 7065 7066 7067 7068 7069 7070
						 "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
7071

7072
		/* Ignore if not to be dumped separately, or if not dumping domain */
7073
		if (coninfo->separate && tinfo->dobj.namespace->dump)
7074 7075
		{
			appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
7076
							  fmtId(tinfo->dobj.name));
7077
			appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s;\n",
7078
							  fmtId(coninfo->dobj.name),
7079
							  coninfo->condef);
7080

7081 7082 7083 7084 7085
			/*
			 * DROP must be fully qualified in case same name appears in
			 * pg_catalog
			 */
			appendPQExpBuffer(delq, "ALTER DOMAIN %s.",
7086
							  fmtId(tinfo->dobj.namespace->dobj.name));
7087
			appendPQExpBuffer(delq, "%s ",
7088
							  fmtId(tinfo->dobj.name));
7089
			appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
7090
							  fmtId(coninfo->dobj.name));
7091 7092

			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
7093 7094
						 coninfo->dobj.name,
						 tinfo->dobj.namespace->dobj.name,
7095
						 tinfo->usename, false,
7096 7097 7098
						 "CHECK CONSTRAINT", q->data, delq->data, NULL,
						 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
						 NULL, NULL);
7099
		}
7100 7101 7102 7103 7104 7105
	}
	else
	{
		write_msg(NULL, "unexpected constraint type\n");
		exit_nicely();
	}
7106

7107 7108 7109 7110 7111
	/* Dump Constraint Comments --- only works for table constraints */
	if (tbinfo)
	{
		resetPQExpBuffer(q);
		appendPQExpBuffer(q, "CONSTRAINT %s ",
7112
						  fmtId(coninfo->dobj.name));
7113
		appendPQExpBuffer(q, "ON %s",
7114
						  fmtId(tbinfo->dobj.name));
7115
		dumpComment(fout, q->data,
7116
					tbinfo->dobj.namespace->dobj.name,
7117 7118
					tbinfo->usename,
					coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
7119
	}
7120 7121 7122

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
7123 7124
}

7125 7126 7127 7128
/*
 * setMaxOid -
 * find the maximum oid and generate a COPY statement to set it
*/
7129

7130
static void
7131
setMaxOid(Archive *fout)
7132
{
7133 7134
	PGresult   *res;
	Oid			max_oid;
7135
	char		sql[1024];
7136

7137 7138
	do_sql_command(g_conn,
				   "CREATE TEMPORARY TABLE pgdump_oid (dummy integer)");
7139
	res = PQexec(g_conn, "INSERT INTO pgdump_oid VALUES (0)");
7140 7141
	check_sql_result(res, g_conn, "INSERT INTO pgdump_oid VALUES (0)",
					 PGRES_COMMAND_OK);
7142
	max_oid = PQoidValue(res);
7143 7144
	if (max_oid == 0)
	{
7145
		write_msg(NULL, "inserted invalid OID\n");
7146
		exit_nicely();
7147 7148
	}
	PQclear(res);
7149
	do_sql_command(g_conn, "DROP TABLE pgdump_oid;");
7150
	if (g_verbose)
7151
		write_msg(NULL, "maximum system OID is %u\n", max_oid);
7152
	snprintf(sql, sizeof(sql),
7153
			 "CREATE TEMPORARY TABLE pgdump_oid (dummy integer);\n"
7154
			 "COPY pgdump_oid WITH OIDS FROM stdin;\n"
7155
			 "%u\t0\n"
7156 7157 7158
			 "\\.\n"
			 "DROP TABLE pgdump_oid;\n",
			 max_oid);
7159

7160 7161
	ArchiveEntry(fout, nilCatalogId, createDumpId(),
				 "Max OID", NULL, "",
7162
				 false, "<Init>", sql, "", NULL,
7163 7164
				 NULL, 0,
				 NULL, NULL);
7165
}
7166 7167 7168

/*
 * findLastBuiltInOid -
7169
 * find the last built in oid
7170 7171 7172
 *
 * For 7.1 and 7.2, we do this by retrieving datlastsysoid from the
 * pg_database entry for the current database
7173
 */
7174
static Oid
7175
findLastBuiltinOid_V71(const char *dbname)
7176
{
Bruce Momjian's avatar
Bruce Momjian committed
7177
	PGresult   *res;
7178
	int			ntups;
7179
	Oid			last_oid;
7180 7181 7182
	PQExpBuffer query = createPQExpBuffer();

	resetPQExpBuffer(query);
7183
	appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
7184
	appendStringLiteral(query, dbname, true);
7185

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

7189
	ntups = PQntuples(res);
7190
	if (ntups < 1)
7191
	{
7192
		write_msg(NULL, "missing pg_database entry for this database\n");
7193
		exit_nicely();
7194 7195 7196
	}
	if (ntups > 1)
	{
7197
		write_msg(NULL, "found more than one pg_database entry for this database\n");
7198
		exit_nicely();
7199
	}
7200
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
7201
	PQclear(res);
7202
	destroyPQExpBuffer(query);
7203
	return last_oid;
7204 7205
}

7206 7207 7208
/*
 * findLastBuiltInOid -
 * find the last built in oid
7209 7210
 *
 * For 7.0, we do this by assuming that the last thing that initdb does is to
Bruce Momjian's avatar
Bruce Momjian committed
7211
 * create the pg_indexes view.	This sucks in general, but seeing that 7.0.x
7212 7213
 * initdb won't be changing anymore, it'll do.
 */
7214
static Oid
7215 7216 7217 7218 7219 7220 7221
findLastBuiltinOid_V70(void)
{
	PGresult   *res;
	int			ntups;
	int			last_oid;

	res = PQexec(g_conn,
7222
				 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'");
7223
	check_sql_result(res, g_conn,
Bruce Momjian's avatar
Bruce Momjian committed
7224
				 "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'",
7225
					 PGRES_TUPLES_OK);
7226 7227 7228
	ntups = PQntuples(res);
	if (ntups < 1)
	{
7229
		write_msg(NULL, "could not find entry for pg_indexes in pg_class\n");
7230
		exit_nicely();
7231 7232 7233
	}
	if (ntups > 1)
	{
7234
		write_msg(NULL, "found more than one entry for pg_indexes in pg_class\n");
7235
		exit_nicely();
7236 7237 7238 7239 7240
	}
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
	PQclear(res);
	return last_oid;
}
7241

7242
static void
7243
dumpSequence(Archive *fout, TableInfo *tbinfo)
7244
{
7245
	PGresult   *res;
7246 7247
	char	   *last,
			   *incby,
7248 7249
			   *maxv = NULL,
			   *minv = NULL,
7250
			   *cache;
7251 7252
	char		bufm[100],
				bufx[100];
7253
	bool		cycled,
Bruce Momjian's avatar
Bruce Momjian committed
7254
				called;
7255
	PQExpBuffer query = createPQExpBuffer();
7256
	PQExpBuffer delqry = createPQExpBuffer();
7257

7258
	/* Make sure we are in proper schema */
7259
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7260

7261 7262 7263
	snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
	snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);

Bruce Momjian's avatar
Bruce Momjian committed
7264
	appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7265 7266 7267 7268 7269 7270 7271 7272 7273 7274
					  "SELECT sequence_name, last_value, increment_by, "
			   "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
			   "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
					  "     ELSE max_value "
					  "END AS max_value, "
				"CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
			   "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
					  "     ELSE min_value "
					  "END AS min_value, "
					  "cache_value, is_cycled, is_called from %s",
7275
					  bufx, bufm,
7276
					  fmtId(tbinfo->dobj.name));
7277

Bruce Momjian's avatar
Bruce Momjian committed
7278
	res = PQexec(g_conn, query->data);
7279
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
7280 7281 7282

	if (PQntuples(res) != 1)
	{
7283
		write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
7284
				  tbinfo->dobj.name, PQntuples(res));
7285
		exit_nicely();
7286 7287
	}

7288 7289
	/* Disable this check: it fails if sequence has been renamed */
#ifdef NOT_USED
7290
	if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
7291
	{
7292
		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
7293
				  tbinfo->dobj.name, PQgetvalue(res, 0, 0));
7294
		exit_nicely();
7295
	}
7296
#endif
7297

7298 7299
	last = PQgetvalue(res, 0, 1);
	incby = PQgetvalue(res, 0, 2);
7300 7301 7302 7303
	if (!PQgetisnull(res, 0, 3))
		maxv = PQgetvalue(res, 0, 3);
	if (!PQgetisnull(res, 0, 4))
		minv = PQgetvalue(res, 0, 4);
7304 7305 7306
	cache = PQgetvalue(res, 0, 5);
	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
	called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
7307

7308
	/*
7309 7310 7311 7312 7313
	 * The logic we use for restoring sequences is as follows:
	 *
	 * Add a basic CREATE SEQUENCE statement (use last_val for start if
	 * called is false, else use min_val for start_val).  Skip this if the
	 * sequence came from a SERIAL column.
7314
	 *
7315
	 * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
7316
	 * data.  We do this for serial sequences too.
7317
	 */
7318

7319
	if (!dataOnly && !OidIsValid(tbinfo->owning_tab))
7320 7321
	{
		resetPQExpBuffer(delqry);
7322

Bruce Momjian's avatar
Bruce Momjian committed
7323 7324 7325 7326
		/*
		 * DROP must be fully qualified in case same name appears in
		 * pg_catalog
		 */
7327
		appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
7328
						  fmtId(tbinfo->dobj.namespace->dobj.name));
7329
		appendPQExpBuffer(delqry, "%s;\n",
7330
						  fmtId(tbinfo->dobj.name));
7331

7332 7333
		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
Bruce Momjian's avatar
Bruce Momjian committed
7334
						  "CREATE SEQUENCE %s\n",
7335
						  fmtId(tbinfo->dobj.name));
7336 7337 7338 7339 7340

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

		appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);
7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352

		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,
7353
						  "    CACHE %s%s",
7354
						  cache, (cycled ? "\n    CYCLE" : ""));
7355

7356 7357
		appendPQExpBuffer(query, ";\n");

7358
		ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
7359 7360
					 tbinfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
7361
					 false, "SEQUENCE", query->data, delqry->data, NULL,
7362 7363
					 tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
					 NULL, NULL);
7364
	}
7365

7366 7367
	if (!schemaOnly)
	{
Bruce Momjian's avatar
Bruce Momjian committed
7368
		TableInfo  *owning_tab;
7369

7370
		resetPQExpBuffer(query);
7371
		appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
Bruce Momjian's avatar
Bruce Momjian committed
7372

7373
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7374 7375 7376 7377 7378
		 * 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.
7379 7380 7381 7382 7383 7384 7385
		 */
		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
7386
			appendStringLiteral(query, owning_tab->attnames[tbinfo->owning_col - 1], true);
7387 7388 7389 7390
			appendPQExpBuffer(query, ")");
		}
		else
			appendStringLiteral(query, fmtId(tbinfo->dobj.name), true);
7391 7392
		appendPQExpBuffer(query, ", %s, %s);\n",
						  last, (called ? "true" : "false"));
7393

7394
		ArchiveEntry(fout, nilCatalogId, createDumpId(),
7395 7396
					 tbinfo->dobj.name,
					 tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
7397
					 false, "SEQUENCE SET", query->data, "", NULL,
7398 7399
					 &(tbinfo->dobj.dumpId), 1,
					 NULL, NULL);
7400
	}
Bruce Momjian's avatar
Bruce Momjian committed
7401

7402 7403 7404 7405
	if (!dataOnly)
	{
		/* Dump Sequence Comments */
		resetPQExpBuffer(query);
7406
		appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
7407
		dumpComment(fout, query->data,
7408
					tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
7409
					tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
7410
	}
7411

7412 7413
	PQclear(res);

7414 7415
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
7416
}
7417

7418
static void
7419
dumpTrigger(Archive *fout, TriggerInfo *tginfo)
7420
{
7421
	TableInfo  *tbinfo = tginfo->tgtable;
7422 7423
	PQExpBuffer query;
	PQExpBuffer delqry;
7424 7425
	const char *p;
	int			findx;
7426

7427
	if (dataOnly)
7428 7429
		return;

7430 7431
	query = createPQExpBuffer();
	delqry = createPQExpBuffer();
Bruce Momjian's avatar
Bruce Momjian committed
7432

7433 7434 7435 7436 7437
	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
7438
					  fmtId(tginfo->dobj.name));
7439
	appendPQExpBuffer(delqry, "ON %s.",
7440
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7441
	appendPQExpBuffer(delqry, "%s;\n",
7442
					  fmtId(tbinfo->dobj.name));
7443

7444 7445 7446 7447 7448 7449 7450 7451
	if (tginfo->tgisconstraint)
	{
		appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
		appendPQExpBuffer(query, fmtId(tginfo->tgconstrname));
	}
	else
	{
		appendPQExpBuffer(query, "CREATE TRIGGER ");
7452
		appendPQExpBuffer(query, fmtId(tginfo->dobj.name));
7453 7454
	}
	appendPQExpBuffer(query, "\n    ");
7455

7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482
	/* 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",
7483
					  fmtId(tbinfo->dobj.name));
7484

7485 7486 7487 7488 7489 7490 7491 7492
	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);
7493
			else
7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504
				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");
	}
7505

7506 7507 7508 7509
	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
7510

7511 7512 7513 7514 7515 7516 7517
	/* 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));
7518

7519 7520 7521 7522 7523 7524 7525 7526 7527
	p = tginfo->tgargs;
	for (findx = 0; findx < tginfo->tgnargs; findx++)
	{
		const char *s = p;

		for (;;)
		{
			p = strchr(p, '\\');
			if (p == NULL)
7528
			{
7529 7530
				write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
						  tginfo->tgargs,
7531 7532
						  tginfo->dobj.name,
						  tbinfo->dobj.name);
7533
				exit_nicely();
7534
			}
7535 7536
			p++;
			if (*p == '\\')
7537
			{
7538 7539
				p++;
				continue;
7540
			}
7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557
			if (p[0] == '0' && p[1] == '0' && p[2] == '0')
				break;
		}
		p--;
		appendPQExpBufferChar(query, '\'');
		while (s < p)
		{
			if (*s == '\'')
				appendPQExpBufferChar(query, '\\');
			appendPQExpBufferChar(query, *s++);
		}
		appendPQExpBufferChar(query, '\'');
		appendPQExpBuffer(query,
						  (findx < tginfo->tgnargs - 1) ? ", " : "");
		p = p + 4;
	}
	appendPQExpBuffer(query, ");\n");
7558

7559
	ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
7560 7561
				 tginfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7562
				 tbinfo->usename, false,
7563 7564 7565
				 "TRIGGER", query->data, delqry->data, NULL,
				 tginfo->dobj.dependencies, tginfo->dobj.nDeps,
				 NULL, NULL);
7566

7567 7568
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "TRIGGER %s ",
7569
					  fmtId(tginfo->dobj.name));
7570
	appendPQExpBuffer(query, "ON %s",
7571
					  fmtId(tbinfo->dobj.name));
7572

7573
	dumpComment(fout, query->data,
7574
				tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
7575
				tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
7576

7577 7578 7579
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
}
7580

7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592
/*
 * dumpRule
 *		Dump a rule
 */
static void
dumpRule(Archive *fout, RuleInfo *rinfo)
{
	TableInfo  *tbinfo = rinfo->ruletable;
	PQExpBuffer query;
	PQExpBuffer cmd;
	PQExpBuffer delcmd;
	PGresult   *res;
7593

7594 7595 7596 7597 7598
	/*
	 * Ignore rules for not-to-be-dumped tables
	 */
	if (tbinfo == NULL || !tbinfo->dump || dataOnly)
		return;
7599

7600
	/*
Bruce Momjian's avatar
Bruce Momjian committed
7601 7602
	 * If it is an ON SELECT rule, we do not need to dump it because it
	 * will be handled via CREATE VIEW for the table.
7603 7604 7605
	 */
	if (rinfo->ev_type == '1' && rinfo->is_instead)
		return;
7606

7607 7608 7609
	/*
	 * Make sure we are in proper schema.
	 */
7610
	selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
7611

7612 7613 7614
	query = createPQExpBuffer();
	cmd = createPQExpBuffer();
	delcmd = createPQExpBuffer();
7615

7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626
	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",
7627
						  rinfo->dobj.name);
7628
	}
7629

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

7633 7634 7635
	if (PQntuples(res) != 1)
	{
		write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
7636
				  rinfo->dobj.name, tbinfo->dobj.name);
7637
		exit_nicely();
7638
	}
7639

7640 7641 7642 7643 7644 7645 7646
	printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

	/*
	 * DROP must be fully qualified in case same name appears in
	 * pg_catalog
	 */
	appendPQExpBuffer(delcmd, "DROP RULE %s ",
7647
					  fmtId(rinfo->dobj.name));
7648
	appendPQExpBuffer(delcmd, "ON %s.",
7649
					  fmtId(tbinfo->dobj.namespace->dobj.name));
7650
	appendPQExpBuffer(delcmd, "%s;\n",
7651
					  fmtId(tbinfo->dobj.name));
7652 7653

	ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
7654 7655
				 rinfo->dobj.name,
				 tbinfo->dobj.namespace->dobj.name,
7656
				 tbinfo->usename, false,
7657 7658 7659 7660 7661 7662 7663
				 "RULE", cmd->data, delcmd->data, NULL,
				 rinfo->dobj.dependencies, rinfo->dobj.nDeps,
				 NULL, NULL);

	/* Dump rule comments */
	resetPQExpBuffer(query);
	appendPQExpBuffer(query, "RULE %s",
7664
					  fmtId(rinfo->dobj.name));
7665
	appendPQExpBuffer(query, " ON %s",
7666
					  fmtId(tbinfo->dobj.name));
7667
	dumpComment(fout, query->data,
7668
				tbinfo->dobj.namespace->dobj.name,
7669 7670 7671 7672 7673
				tbinfo->usename,
				rinfo->dobj.catId, 0, rinfo->dobj.dumpId);

	PQclear(res);

7674
	destroyPQExpBuffer(query);
7675 7676
	destroyPQExpBuffer(cmd);
	destroyPQExpBuffer(delcmd);
7677
}
7678

7679 7680 7681
/*
 * getDependencies --- obtain available dependency data
 */
7682
static void
7683
getDependencies(void)
7684
{
7685
	PQExpBuffer query;
Bruce Momjian's avatar
Bruce Momjian committed
7686
	PGresult   *res;
7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699
	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;
7700 7701

	if (g_verbose)
7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724
		write_msg(NULL, "fetching dependency data\n");

	/* 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");
7725 7726

	/*
7727 7728 7729
	 * Since we ordered the SELECT by referencing ID, we can expect that
	 * multiple entries for the same object will appear together; this
	 * saves on searches.
7730
	 */
7731 7732 7733
	dobj = NULL;

	for (i = 0; i < ntups; i++)
7734
	{
7735 7736 7737
		CatalogId	objId;
		CatalogId	refobjId;
		char		deptype;
7738

7739 7740 7741 7742 7743
		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));
7744

7745 7746 7747 7748
		if (dobj == NULL ||
			dobj->catId.tableoid != objId.tableoid ||
			dobj->catId.oid != objId.oid)
			dobj = findObjectByCatalogId(objId);
7749

7750
		/*
Bruce Momjian's avatar
Bruce Momjian committed
7751 7752 7753
		 * Failure to find objects mentioned in pg_depend is not
		 * unexpected, since for example we don't collect info about TOAST
		 * tables.
7754
		 */
7755
		if (dobj == NULL)
7756
		{
7757 7758 7759 7760 7761
#ifdef NOT_USED
			fprintf(stderr, "no referencing object %u %u\n",
					objId.tableoid, objId.oid);
#endif
			continue;
7762 7763
		}

7764
		refdobj = findObjectByCatalogId(refobjId);
Bruce Momjian's avatar
Bruce Momjian committed
7765

7766
		if (refdobj == NULL)
7767
		{
7768 7769 7770 7771 7772
#ifdef NOT_USED
			fprintf(stderr, "no referenced object %u %u\n",
					refobjId.tableoid, refobjId.oid);
#endif
			continue;
Bruce Momjian's avatar
Bruce Momjian committed
7773 7774
		}

7775
		addObjectDependency(dobj, refdobj->dumpId);
7776
	}
7777

7778 7779
	PQclear(res);

7780
	destroyPQExpBuffer(query);
7781
}
7782

7783

7784 7785 7786 7787
/*
 * selectSourceSchema - make the specified schema the active search path
 * in the source database.
 *
7788 7789 7790 7791 7792 7793 7794
 * 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!
7795 7796 7797 7798
 */
static void
selectSourceSchema(const char *schemaName)
{
Bruce Momjian's avatar
Bruce Momjian committed
7799
	static char *curSchemaName = NULL;
7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813
	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",
7814
					  fmtId(schemaName));
7815 7816
	if (strcmp(schemaName, "pg_catalog") != 0)
		appendPQExpBuffer(query, ", pg_catalog");
7817

7818 7819 7820
	do_sql_command(g_conn, query->data);

	destroyPQExpBuffer(query);
7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833
	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 *
7834
getFormattedTypeName(Oid oid, OidOptions opts)
7835 7836 7837 7838 7839 7840
{
	char	   *result;
	PQExpBuffer query;
	PGresult   *res;
	int			ntups;

7841
	if (oid == 0)
7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853
	{
		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();
7854 7855
	if (g_fout->remoteVersion >= 70300)
	{
7856
		appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
7857 7858 7859
						  oid);
	}
	else if (g_fout->remoteVersion >= 70100)
7860
	{
7861
		appendPQExpBuffer(query, "SELECT format_type('%u'::oid, NULL)",
7862 7863 7864 7865 7866 7867
						  oid);
	}
	else
	{
		appendPQExpBuffer(query, "SELECT typname "
						  "FROM pg_type "
7868
						  "WHERE oid = '%u'::oid",
7869 7870 7871 7872
						  oid);
	}

	res = PQexec(g_conn, query->data);
7873
	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
7874 7875 7876 7877 7878

	/* Expecting a single result only */
	ntups = PQntuples(res);
	if (ntups != 1)
	{
7879
		write_msg(NULL, "query yielded %d rows instead of one: %s\n",
7880 7881 7882 7883
				  ntups, query->data);
		exit_nicely();
	}

7884 7885 7886 7887 7888 7889 7890 7891
	if (g_fout->remoteVersion >= 70100)
	{
		/* already quoted */
		result = strdup(PQgetvalue(res, 0, 0));
	}
	else
	{
		/* may need to quote it */
7892
		result = strdup(fmtId(PQgetvalue(res, 0, 0)));
7893
	}
7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907

	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
7908
	bool		isarray = false;
7909 7910
	PQExpBuffer buf = createPQExpBuffer();

7911 7912 7913 7914 7915 7916 7917
	/* Handle array types */
	if (typname[0] == '_')
	{
		isarray = true;
		typname++;
	}

7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950
	/* 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
7951

7952 7953 7954 7955
	/*
	 * char is an internal single-byte data type; Let's make sure we force
	 * it through with quotes. - thomas 1998-12-13
	 */
Bruce Momjian's avatar
Bruce Momjian committed
7956
	else if (strcmp(typname, "char") == 0)
7957
		appendPQExpBuffer(buf, "\"char\"");
7958
	else
7959
		appendPQExpBuffer(buf, "%s", fmtId(typname));
7960

7961 7962 7963 7964
	/* Append array qualifier for array types */
	if (isarray)
		appendPQExpBuffer(buf, "[]");

7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990
	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.",
7991
						  fmtId(schema));
7992 7993
	}
	appendPQExpBuffer(id_return, "%s",
7994
					  fmtId(id));
7995 7996 7997

	return id_return->data;
}
7998 7999

/*
8000 8001 8002
 * Return a column list clause for the given relation.
 *
 * Special case: if there are no undropped columns in the relation, return
8003
 * "", not an invalid "()" column list.
8004
 */
8005 8006
static const char *
fmtCopyColumnList(const TableInfo *ti)
8007 8008 8009
{
	static PQExpBuffer q = NULL;
	int			numatts = ti->numatts;
Bruce Momjian's avatar
Bruce Momjian committed
8010 8011 8012 8013
	char	  **attnames = ti->attnames;
	bool	   *attisdropped = ti->attisdropped;
	bool		needComma;
	int			i;
8014

Bruce Momjian's avatar
Bruce Momjian committed
8015
	if (q)						/* first time through? */
8016 8017 8018 8019
		resetPQExpBuffer(q);
	else
		q = createPQExpBuffer();

8020 8021
	appendPQExpBuffer(q, "(");
	needComma = false;
8022 8023
	for (i = 0; i < numatts; i++)
	{
8024 8025 8026
		if (attisdropped[i])
			continue;
		if (needComma)
8027 8028
			appendPQExpBuffer(q, ", ");
		appendPQExpBuffer(q, "%s", fmtId(attnames[i]));
8029
		needComma = true;
8030
	}
8031 8032

	if (!needComma)
8033
		return "";				/* no undropped columns */
8034

8035 8036 8037
	appendPQExpBuffer(q, ")");
	return q->data;
}
8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074

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