pg_dump.c 132.0 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * pg_dump.c
4
 *	  pg_dump is a utility for dumping out a postgres database
B
Bruce Momjian 已提交
5
 *	  into a script file.
6 7 8
 *
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10 11 12 13 14 15
 *	pg_dump will read the system catalogs in a database and
 *	dump out a script that reproduces
 *	the schema of the database in terms of
 *		  user-defined types
 *		  user-defined functions
 *		  tables
16
 *		  indexes
17 18
 *		  aggregates
 *		  operators
19
 *		  privileges
20
 *
21
 * the output script is SQL that is understood by PostgreSQL
22 23 24
 *
 *
 * IDENTIFICATION
25
 *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.242 2002/02/27 20:59:05 tgl Exp $
26
 *
27
 *-------------------------------------------------------------------------
28 29
 */

30 31 32 33 34 35 36
/*
 * 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"

37
#include <unistd.h>				/* for getopt() */
38
#include <ctype.h>
39 40 41
#ifdef ENABLE_NLS
#include <locale.h>
#endif
42 43 44 45 46 47 48
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

49 50 51 52
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

53
#include "access/attnum.h"
54
#include "access/htup.h"
55
#include "catalog/pg_class.h"
V
Vadim B. Mikheev 已提交
56
#include "catalog/pg_trigger.h"
57
#include "catalog/pg_type.h"
58

59
#include "libpq-fe.h"
60
#include "libpq/libpq-fs.h"
61 62

#include "pg_dump.h"
B
Bruce Momjian 已提交
63
#include "pg_backup.h"
64
#include "pg_backup_archiver.h"
65

B
Bruce Momjian 已提交
66 67
typedef enum _formatLiteralOptions
{
68 69
	CONV_ALL = 0,
	PASS_LFTAB = 3				/* NOTE: 1 and 2 are reserved in case we
B
Bruce Momjian 已提交
70 71 72 73
								 * want to make a mask. */
	/* We could make this a bit mask for control chars, but I don't */
	/* see any value in making it more complex...the current code */
	/* only checks for 'opts == CONV_ALL' anyway. */
74 75
} formatLiteralOptions;

76
static void dumpComment(Archive *fout, const char *target, const char *oid,
77 78
			const char *classname, int subid,
			const char *((*deps)[]));
79
static void dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly);
B
Bruce Momjian 已提交
80 81
static void dumpACL(Archive *fout, TableInfo tbinfo);
static void dumpTriggers(Archive *fout, const char *tablename,
82
			 TableInfo *tblinfo, int numTables);
B
Bruce Momjian 已提交
83
static void dumpRules(Archive *fout, const char *tablename,
B
Bruce Momjian 已提交
84
		  TableInfo *tblinfo, int numTables);
85
static void formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions opts);
86
static void clearTableInfo(TableInfo *, int);
B
Bruce Momjian 已提交
87
static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
88
			TypeInfo *tinfo, int numTypes);
89 90
static Oid	findLastBuiltinOid_V71(const char *);
static Oid	findLastBuiltinOid_V70(void);
B
Bruce Momjian 已提交
91
static void setMaxOid(Archive *fout);
92

93
static void AddAcl(char *aclbuf, const char *keyword);
94
static char *GetPrivileges(Archive *AH, const char *s);
V
Vadim B. Mikheev 已提交
95

B
Bruce Momjian 已提交
96 97
static int	dumpBlobs(Archive *AH, char *, void *);
static int	dumpDatabase(Archive *AH);
98
static PQExpBuffer getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo);
99
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
100

B
Bruce Momjian 已提交
101
extern char *optarg;
102
extern int	optind,
B
Bruce Momjian 已提交
103
			opterr;
104 105

/* global decls */
106
bool		g_verbose;			/* User wants verbose narration of our
B
Bruce Momjian 已提交
107
								 * activities. */
108
Oid			g_last_builtin_oid; /* value of the last builtin oid */
B
Bruce Momjian 已提交
109
Archive    *g_fout;				/* the script file */
B
Bruce Momjian 已提交
110 111 112
PGconn	   *g_conn;				/* the database connection */

bool		force_quotes;		/* User wants to suppress double-quotes */
113 114 115 116
bool		dumpData;			/* dump data using proper insert strings */
bool		attrNames;			/* put attr names into insert strings */
bool		schemaOnly;
bool		dataOnly;
117
bool		aclsSkip;
118

B
Bruce Momjian 已提交
119
char		g_opaque_type[10];	/* name for the opaque type */
120 121

/* placeholders for the delimiters for comments */
122 123
char		g_comment_start[10];
char		g_comment_end[10];
124 125


B
Bruce Momjian 已提交
126 127 128 129
typedef struct _dumpContext
{
	TableInfo  *tblinfo;
	int			tblidx;
B
Bruce Momjian 已提交
130 131 132
	bool		oids;
} DumpContext;

133
static void
134
help(const char *progname)
135
{
136 137 138 139
	printf(gettext("%s dumps a database as a text file or to other formats.\n\n"), progname);
	puts(gettext("Usage:"));
	printf(gettext("  %s [options] dbname\n\n"), progname);
	puts(gettext("Options:"));
B
Hi,  
Bruce Momjian 已提交
140 141

#ifdef HAVE_GETOPT_LONG
142 143
	puts(gettext(
		"  -a, --data-only          dump only the data, not the schema\n"
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
			 "  -b, --blobs              include large objects in dump\n"
	   "  -c, --clean              clean (drop) schema prior to create\n"
				 "  -C, --create             include commands to create database in dump\n"
				 "  -d, --inserts            dump data as INSERT, rather than COPY, commands\n"
				 "  -D, --column-inserts     dump data as INSERT commands with column names\n"
				 "  -f, --file=FILENAME      output file name\n"
				 "  -F, --format {c|t|p}     output file format (custom, tar, plain text)\n"
				 "  -h, --host=HOSTNAME      database server host name\n"
				 "  -i, --ignore-version     proceed even when server version mismatches\n"
				 "                           pg_dump version\n"
	"  -n, --no-quotes          suppress most quotes around identifiers\n"
	 "  -N, --quotes             enable most quotes around identifiers\n"
				 "  -o, --oids               include oids in dump\n"
				 "  -O, --no-owner           do not output \\connect commands in plain\n"
				 "                           text format\n"
			   "  -p, --port=PORT          database server port number\n"
				 "  -R, --no-reconnect       disable ALL reconnections to the database in\n"
				 "                           plain text format\n"
			 "  -s, --schema-only        dump only the schema, no data\n"
				 "  -S, --superuser=NAME     specify the superuser user name to use in\n"
				 "                           plain text format\n"
		  "  -t, --table=TABLE        dump this table only (* for all)\n"
166
		"  -U, --username=NAME      connect as specified database user\n"
167 168 169 170 171 172 173 174
				 "  -v, --verbose            verbose mode\n"
				 "  -W, --password           force password prompt (should happen automatically)\n"
	 "  -x, --no-privileges      do not dump privileges (grant/revoke)\n"
				 "  -X use-set-session-authorization, --use-set-session-authorization\n"
				 "                           output SET SESSION AUTHORIZATION commands rather\n"
				 "                           than \\connect commands\n"
				 "  -Z, --compress {0-9}     compression level for compressed formats\n"
				 ));
B
Hi,  
Bruce Momjian 已提交
175
#else
176 177
	puts(gettext(
		"  -a                       dump only the data, not the schema\n"
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
			 "  -b                       include large objects in dump\n"
	   "  -c                       clean (drop) schema prior to create\n"
				 "  -C                       include commands to create database in dump\n"
				 "  -d                       dump data as INSERT, rather than COPY, commands\n"
				 "  -D                       dump data as INSERT commands with column names\n"
				 "  -f FILENAME              output file name\n"
				 "  -F {c|t|p}               output file format (custom, tar, plain text)\n"
				 "  -h HOSTNAME              database server host name\n"
				 "  -i                       proceed even when server version mismatches\n"
				 "                           pg_dump version\n"
	"  -n                       suppress most quotes around identifiers\n"
	 "  -N                       enable most quotes around identifiers\n"
				 "  -o                       include oids in dump\n"
				 "  -O                       do not output \\connect commands in plain\n"
				 "                           text format\n"
			   "  -p PORT                  database server port number\n"
				 "  -R                       disable ALL reconnections to the database in\n"
				 "                           plain text format\n"
			 "  -s                       dump only the schema, no data\n"
				 "  -S NAME                  specify the superuser user name to use in\n"
				 "                           plain text format\n"
		  "  -t TABLE                 dump this table only (* for all)\n"
200
		"  -U NAME                  connect as specified database user\n"
201 202 203 204 205 206 207 208
				 "  -v                       verbose mode\n"
				 "  -W                       force password prompt (should happen automatically)\n"
	 "  -x                       do not dump privileges (grant/revoke)\n"
				 "  -X use-set-session-authorization\n"
				 "                           output SET SESSION AUTHORIZATION commands rather\n"
				 "                           than \\connect commands\n"
				 "  -Z {0-9}                 compression level for compressed formats\n"
				 ));
B
Hi,  
Bruce Momjian 已提交
209
#endif
210 211 212
	puts(gettext("If no database name is not supplied, then the PGDATABASE environment\n"
				 "variable value is used.\n\n"
				 "Report bugs to <pgsql-bugs@postgresql.org>."));
213 214
}

215

216 217
void
exit_nicely(void)
218
{
219 220 221
	PQfinish(g_conn);
	if (g_verbose)
		write_msg(NULL, "*** aborted because of error\n");
222
	exit(1);
223 224 225
}


226 227
#define COPYBUFSIZ		8192

B
Bruce Momjian 已提交
228 229 230 231 232
/*
 *	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.
 */
233

B
Bruce Momjian 已提交
234 235
static int
dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
236
{
B
Bruce Momjian 已提交
237 238
	const DumpContext *dctx = (DumpContext *) dctxv;
	const char *classname = dctx->tblinfo[dctx->tblidx].relname;
239
	const bool	hasoids = dctx->tblinfo[dctx->tblidx].hasoids;
B
Bruce Momjian 已提交
240
	const bool	oids = dctx->oids;
241

242 243 244 245 246
	PGresult   *res;
	char		query[255];
	int			ret;
	bool		copydone;
	char		copybuf[COPYBUFSIZ];
247

B
Bruce Momjian 已提交
248
	if (g_verbose)
249
		write_msg(NULL, "dumping out the contents of table %s\n", classname);
250

251
	if (oids && hasoids)
252
	{
B
Bruce Momjian 已提交
253
		/*
254
		 * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
B
Bruce Momjian 已提交
255
		 * fmtId(classname, force_quotes));
256
		 *
B
Bruce Momjian 已提交
257
		 * - Not used as of V1.3 (needs to be in ArchiveEntry call)
258 259 260
		 *
		 */

261
		sprintf(query, "COPY %s WITH OIDS TO stdout;",
262
				fmtId(classname, force_quotes));
263 264 265
	}
	else
	{
B
Bruce Momjian 已提交
266 267 268
		/*
		 * archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname,
		 * force_quotes));
269 270 271 272 273
		 *
		 * - Not used as of V1.3 (needs to be in ArchiveEntry call)
		 *
		 */

274
		sprintf(query, "COPY %s TO stdout;", fmtId(classname, force_quotes));
275 276
	}
	res = PQexec(g_conn, query);
277 278
	if (!res ||
		PQresultStatus(res) == PGRES_FATAL_ERROR)
279
	{
280 281 282 283
		write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed\n",
				  classname);
		write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
		write_msg(NULL, "The command was: %s\n", query);
284
		exit_nicely();
285 286 287 288 289
	}
	else
	{
		if (PQresultStatus(res) != PGRES_COPY_OUT)
		{
290 291
			write_msg(NULL, "SQL command to dump the contents of table \"%s\" executed abnormally.\n",
					  classname);
292
			write_msg(NULL, "The server returned status %d when %d was expected.\n",
293 294
					  PQresultStatus(res), PGRES_COPY_OUT);
			write_msg(NULL, "The command was: %s\n", query);
295
			exit_nicely();
296 297 298 299
		}
		else
		{
			copydone = false;
300

301 302
			while (!copydone)
			{
303
				ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
304 305 306 307 308 309 310 311 312

				if (copybuf[0] == '\\' &&
					copybuf[1] == '.' &&
					copybuf[2] == '\0')
				{
					copydone = true;	/* don't print this... */
				}
				else
				{
B
Bruce Momjian 已提交
313
					archputs(copybuf, fout);
314 315
					switch (ret)
					{
316 317 318 319
						case EOF:
							copydone = true;
							/* FALLTHROUGH */
						case 0:
B
Bruce Momjian 已提交
320
							archputc('\n', fout);
321 322 323
							break;
						case 1:
							break;
324 325
					}
				}
326

B
Bruce Momjian 已提交
327
				/*
328 329
				 * THROTTLE:
				 *
B
Bruce Momjian 已提交
330 331 332 333 334
				 * 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.
335
				 *
B
Bruce Momjian 已提交
336 337 338 339 340
				 * 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:
341
				 *
B
Bruce Momjian 已提交
342 343 344
				 * 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
345 346
				 * EndIf
				 *
B
Bruce Momjian 已提交
347 348
				 * where the throttle value was the number of ms to sleep per
				 * ms of work. The calculation was done in each loop.
349
				 *
B
Bruce Momjian 已提交
350 351 352 353 354
				 * 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.
355 356 357
				 *
				 * Further discussion ensued, and the proposal was dropped.
				 *
B
Bruce Momjian 已提交
358 359 360 361 362 363
				 * 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.
364
				 *
B
Bruce Momjian 已提交
365
				 * select(0, NULL, NULL, NULL, &tvi);
366
				 *
B
Bruce Momjian 已提交
367 368 369 370
				 * This will return after the interval specified in the
				 * structure tvi. Fianally, call gettimeofday again to
				 * save the 'last sleep time'.
				 */
371
			}
B
Bruce Momjian 已提交
372
			archprintf(fout, "\\.\n");
373
		}
374
		ret = PQendcopy(g_conn);
375 376
		if (ret != 0)
		{
377 378 379
			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", query);
B
Bruce Momjian 已提交
380
			PQclear(res);
381
			exit_nicely();
382 383
		}
	}
384

B
Bruce Momjian 已提交
385
	return 1;
386 387
}

B
Bruce Momjian 已提交
388
static int
B
Bruce Momjian 已提交
389
dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
390
{
B
Bruce Momjian 已提交
391 392
	const DumpContext *dctx = (DumpContext *) dctxv;
	const char *classname = dctx->tblinfo[dctx->tblidx].relname;
B
Bruce Momjian 已提交
393

394 395
	PGresult   *res;
	PQExpBuffer q = createPQExpBuffer();
396 397
	int			tuple;
	int			field;
398

399
	if (fout->remoteVersion >= 70100)
400
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM ONLY %s", fmtId(classname, force_quotes));
401
	else
402
		appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM %s", fmtId(classname, force_quotes));
403

B
Hi, all  
Bruce Momjian 已提交
404
	res = PQexec(g_conn, q->data);
405
	if (!res ||
406
		PQresultStatus(res) != PGRES_COMMAND_OK)
407
	{
408 409 410
		write_msg(NULL, "dumpClasses(): SQL command failed\n");
		write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
		write_msg(NULL, "The command was: %s\n", q->data);
411
		exit_nicely();
412
	}
413 414 415

	do
	{
416 417 418 419 420
		PQclear(res);

		res = PQexec(g_conn, "FETCH 100 FROM _pg_dump_cursor");
		if (!res ||
			PQresultStatus(res) != PGRES_TUPLES_OK)
421
		{
422 423 424 425
			write_msg(NULL, "dumpClasses(): SQL command failed\n");
			write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
			write_msg(NULL, "The command was: FETCH 100 FROM _pg_dump_cursor\n");
			exit_nicely();
426
		}
427 428

		for (tuple = 0; tuple < PQntuples(res); tuple++)
429
		{
430 431
			archprintf(fout, "INSERT INTO %s ", fmtId(classname, force_quotes));
			if (attrNames == true)
432
			{
433 434 435 436 437 438 439 440 441 442
				resetPQExpBuffer(q);
				appendPQExpBuffer(q, "(");
				for (field = 0; field < PQnfields(res); field++)
				{
					if (field > 0)
						appendPQExpBuffer(q, ",");
					appendPQExpBuffer(q, fmtId(PQfname(res, field), force_quotes));
				}
				appendPQExpBuffer(q, ") ");
				archprintf(fout, "%s", q->data);
443
			}
444 445
			archprintf(fout, "VALUES (");
			for (field = 0; field < PQnfields(res); field++)
446
			{
447 448 449 450 451 452 453 454 455 456 457
				if (field > 0)
					archprintf(fout, ",");
				if (PQgetisnull(res, tuple, field))
				{
					archprintf(fout, "NULL");
					continue;
				}
				switch (PQftype(res, field))
				{
					case INT2OID:
					case INT4OID:
458
					case OIDOID:		/* int types */
459
					case FLOAT4OID:
460
					case FLOAT8OID:		/* float types */
461 462 463 464 465 466 467 468 469 470
						/* These types are printed without quotes */
						archprintf(fout, "%s",
								   PQgetvalue(res, tuple, field));
						break;
					case BITOID:
					case VARBITOID:
						archprintf(fout, "B'%s'",
								   PQgetvalue(res, tuple, field));
						break;
					default:
471

472 473
						/*
						 * All other types are printed as string literals,
474 475
						 * with appropriate escaping of special
						 * characters.
476 477 478 479 480 481
						 */
						resetPQExpBuffer(q);
						formatStringLiteral(q, PQgetvalue(res, tuple, field), CONV_ALL);
						archprintf(fout, "%s", q->data);
						break;
				}
482
			}
483
			archprintf(fout, ");\n");
484
		}
485

486
	} while (PQntuples(res) > 0);
487 488 489 490 491 492 493 494 495 496
	PQclear(res);

	res = PQexec(g_conn, "CLOSE _pg_dump_cursor");
	if (!res ||
		PQresultStatus(res) != PGRES_COMMAND_OK)
	{
		write_msg(NULL, "dumpClasses(): SQL command failed\n");
		write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
		write_msg(NULL, "The command was: CLOSE _pg_dump_cursor\n");
		exit_nicely();
497 498
	}
	PQclear(res);
499

500
	destroyPQExpBuffer(q);
B
Bruce Momjian 已提交
501
	return 1;
502 503
}

504 505 506 507 508 509 510 511
/*
 * Convert a string value to an SQL string literal,
 * with appropriate escaping of special characters.
 * Quote mark ' goes to '' per SQL standard, other
 * stuff goes to \ sequences.
 * The literal is appended to the given PQExpBuffer.
 */
static void
512
formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions opts)
513 514 515 516
{
	appendPQExpBufferChar(buf, '\'');
	while (*str)
	{
B
Bruce Momjian 已提交
517
		char		ch = *str++;
518 519 520

		if (ch == '\\' || ch == '\'')
		{
B
Bruce Momjian 已提交
521
			appendPQExpBufferChar(buf, ch);		/* double these */
522 523 524
			appendPQExpBufferChar(buf, ch);
		}
		else if ((unsigned char) ch < (unsigned char) ' ' &&
B
Bruce Momjian 已提交
525 526 527
				 (opts == CONV_ALL
				  || (ch != '\n' && ch != '\t')
				  ))
528
		{
B
Bruce Momjian 已提交
529 530 531 532
			/*
			 * generate octal escape for control chars other than
			 * whitespace
			 */
533 534 535 536 537 538 539 540 541 542 543
			appendPQExpBufferChar(buf, '\\');
			appendPQExpBufferChar(buf, ((ch >> 6) & 3) + '0');
			appendPQExpBufferChar(buf, ((ch >> 3) & 7) + '0');
			appendPQExpBufferChar(buf, (ch & 7) + '0');
		}
		else
			appendPQExpBufferChar(buf, ch);
	}
	appendPQExpBufferChar(buf, '\'');
}

544 545
/*
 * DumpClasses -
546
 *	  dump the contents of all the classes.
547 548
 */
static void
B
Bruce Momjian 已提交
549
dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
B
Bruce Momjian 已提交
550
		 const char *onlytable, const bool oids, const bool force_quotes)
551
{
B
Bruce Momjian 已提交
552 553 554 555 556 557
	int			i;
	DataDumperPtr dumpFn;
	DumpContext *dumpCtx;
	char		copyBuf[512];
	char	   *copyStmt;

558
	if (g_verbose)
559 560 561 562 563 564
	{
		if (onlytable == NULL || (strlen(onlytable) == 0))
			write_msg(NULL, "preparing to dump the contents of all %d tables/sequences\n", numTables);
		else
			write_msg(NULL, "preparing to dump the contents of only one table/sequence\n");
	}
565

566 567
	for (i = 0; i < numTables; i++)
	{
568
		const char *classname = tblinfo[i].relname;
569 570

		/* Skip VIEW relations */
571
		if (tblinfo[i].viewdef != NULL)
572
			continue;
573

574
		if (tblinfo[i].sequence)	/* already dumped */
V
Vadim B. Mikheev 已提交
575
			continue;
576

577
		if (!onlytable || (strcmp(classname, onlytable) == 0) || (strlen(onlytable) == 0))
578 579
		{
			if (g_verbose)
580
				write_msg(NULL, "preparing to dump the contents of table %s\n", classname);
581

582 583 584
#if 0
			becomeUser(fout, tblinfo[i].usename);
#endif
B
Bruce Momjian 已提交
585

B
Bruce Momjian 已提交
586 587
			dumpCtx = (DumpContext *) malloc(sizeof(DumpContext));
			dumpCtx->tblinfo = (TableInfo *) tblinfo;
B
Bruce Momjian 已提交
588 589
			dumpCtx->tblidx = i;
			dumpCtx->oids = oids;
590

591
			if (!dumpData)
592
			{
593
				/* Dump/restore using COPY */
B
Bruce Momjian 已提交
594
				dumpFn = dumpClasses_nodumpData;
595 596 597
				sprintf(copyBuf, "COPY %s %sFROM stdin;\n",
						fmtId(tblinfo[i].relname, force_quotes),
						(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
598 599
				copyStmt = copyBuf;
			}
B
Bruce Momjian 已提交
600
			else
601
			{
602
				/* Restore using INSERT */
B
Bruce Momjian 已提交
603
				dumpFn = dumpClasses_dumpData;
604 605
				copyStmt = NULL;
			}
B
Bruce Momjian 已提交
606

P
Philip Warner 已提交
607
			ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
608 609
						 "TABLE DATA", NULL, "", "",
						 copyStmt, tblinfo[i].usename,
B
Bruce Momjian 已提交
610
						 dumpFn, dumpCtx);
611
		}
612
	}
613 614
}

615 616 617


static int
618
parse_version(const char *versionString)
619 620
{
	int			cnt;
621 622 623
	int			vmaj,
				vmin,
				vrev;
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640

	cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);

	if (cnt < 2)
	{
		write_msg(NULL, "unable to parse version string \"%s\"\n", versionString);
		exit(1);
	}

	if (cnt == 2)
		vrev = 0;

	return (100 * vmaj + vmin) * 100 + vrev;
}



641
int
642
main(int argc, char **argv)
643
{
B
Bruce Momjian 已提交
644 645
	int			c;
	const char *filename = NULL;
B
Bruce Momjian 已提交
646
	const char *format = "p";
B
Bruce Momjian 已提交
647 648 649
	const char *dbname = NULL;
	const char *pghost = NULL;
	const char *pgport = NULL;
650
	const char *username = NULL;
B
Bruce Momjian 已提交
651
	char	   *tablename = NULL;
652
	bool		oids = false;
B
Bruce Momjian 已提交
653 654
	TableInfo  *tblinfo;
	int			numTables;
655
	bool		force_password = false;
B
Bruce Momjian 已提交
656
	int			compressLevel = -1;
657
	bool		ignore_version = false;
658 659
	int			plainText = 0;
	int			outputClean = 0;
660
	int			outputCreate = 0;
661
	int			outputBlobs = 0;
662 663
	int			outputNoOwner = 0;
	int			outputNoReconnect = 0;
664
	static int	use_setsessauth = 0;
B
Bruce Momjian 已提交
665
	char	   *outputSuperuser = NULL;
666

B
Bruce Momjian 已提交
667
	RestoreOptions *ropt;
668

B
Hi,  
Bruce Momjian 已提交
669 670 671
#ifdef HAVE_GETOPT_LONG
	static struct option long_options[] = {
		{"data-only", no_argument, NULL, 'a'},
B
Bruce Momjian 已提交
672
		{"blobs", no_argument, NULL, 'b'},
B
Hi,  
Bruce Momjian 已提交
673
		{"clean", no_argument, NULL, 'c'},
674
		{"create", no_argument, NULL, 'C'},
B
Bruce Momjian 已提交
675 676
		{"file", required_argument, NULL, 'f'},
		{"format", required_argument, NULL, 'F'},
677
		{"inserts", no_argument, NULL, 'd'},
678
		{"attribute-inserts", no_argument, NULL, 'D'},
679
		{"column-inserts", no_argument, NULL, 'D'},
B
Hi,  
Bruce Momjian 已提交
680
		{"host", required_argument, NULL, 'h'},
681
		{"ignore-version", no_argument, NULL, 'i'},
682
		{"no-reconnect", no_argument, NULL, 'R'},
B
Hi,  
Bruce Momjian 已提交
683 684
		{"no-quotes", no_argument, NULL, 'n'},
		{"quotes", no_argument, NULL, 'N'},
685
		{"oids", no_argument, NULL, 'o'},
686
		{"no-owner", no_argument, NULL, 'O'},
B
Hi,  
Bruce Momjian 已提交
687 688
		{"port", required_argument, NULL, 'p'},
		{"schema-only", no_argument, NULL, 's'},
689
		{"superuser", required_argument, NULL, 'S'},
B
Hi,  
Bruce Momjian 已提交
690
		{"table", required_argument, NULL, 't'},
691 692
		{"password", no_argument, NULL, 'W'},
		{"username", required_argument, NULL, 'U'},
B
Hi,  
Bruce Momjian 已提交
693
		{"verbose", no_argument, NULL, 'v'},
694
		{"no-privileges", no_argument, NULL, 'x'},
B
Hi,  
Bruce Momjian 已提交
695
		{"no-acl", no_argument, NULL, 'x'},
B
Bruce Momjian 已提交
696
		{"compress", required_argument, NULL, 'Z'},
B
Hi,  
Bruce Momjian 已提交
697
		{"help", no_argument, NULL, '?'},
698 699
		{"version", no_argument, NULL, 'V'},

700 701 702 703
		/*
		 * the following options don't have an equivalent short option
		 * letter, but are available as '-X long-name'
		 */
704
		{"use-set-session-authorization", no_argument, &use_setsessauth, 1}
705
	};
706
	int			optindex;
B
Hi,  
Bruce Momjian 已提交
707 708
#endif

709 710 711 712 713 714
#ifdef ENABLE_NLS
	setlocale(LC_ALL, "");
	bindtextdomain("pg_dump", LOCALEDIR);
	textdomain("pg_dump");
#endif

715
	g_verbose = false;
716
	force_quotes = true;
717 718 719 720 721

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

722
	dataOnly = schemaOnly = dumpData = attrNames = false;
723

724
	if (!strrchr(argv[0], '/'))
725 726
		progname = argv[0];
	else
727
		progname = strrchr(argv[0], '/') + 1;
728

729
	/* Set default options based on progname */
730 731 732
	if (strcmp(progname, "pg_backup") == 0)
	{
		format = "c";
P
Philip Warner 已提交
733
		outputBlobs = true;
734
	}
735

736 737
	if (argc > 1)
	{
B
Bruce Momjian 已提交
738
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
739 740 741 742
		{
			help(progname);
			exit(0);
		}
B
Bruce Momjian 已提交
743
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
744
		{
745
			puts("pg_dump (PostgreSQL) " PG_VERSION);
746 747 748 749
			exit(0);
		}
	}

B
Hi,  
Bruce Momjian 已提交
750
#ifdef HAVE_GETOPT_LONG
751
	while ((c = getopt_long(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?", long_options, &optindex)) != -1)
B
Hi,  
Bruce Momjian 已提交
752
#else
753
	while ((c = getopt(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?-")) != -1)
B
Hi,  
Bruce Momjian 已提交
754
#endif
755

756 757 758
	{
		switch (c)
		{
759
			case 'a':			/* Dump data only */
760
				dataOnly = true;
761
				break;
762

763 764 765 766
			case 'b':			/* Dump blobs */
				outputBlobs = true;
				break;

B
Bruce Momjian 已提交
767
			case 'c':			/* clean (i.e., drop) schema prior to
B
Bruce Momjian 已提交
768
								 * create */
B
Bruce Momjian 已提交
769
				outputClean = 1;
B
Bruce Momjian 已提交
770 771
				break;

772 773 774 775 776
			case 'C':			/* Create DB */

				outputCreate = 1;
				break;

777
			case 'd':			/* dump data as proper insert strings */
778
				dumpData = true;
779
				break;
780

781 782
			case 'D':			/* dump data as proper insert strings with
								 * attr names */
783 784
				dumpData = true;
				attrNames = true;
785
				break;
786

787
			case 'f':
788 789
				filename = optarg;
				break;
790

B
Bruce Momjian 已提交
791 792 793
			case 'F':
				format = optarg;
				break;
794

B
Bruce Momjian 已提交
795
			case 'h':			/* server host */
796 797
				pghost = optarg;
				break;
798

799 800 801
			case 'i':			/* ignore database version mismatch */
				ignore_version = true;
				break;
802

B
Bruce Momjian 已提交
803 804
			case 'n':			/* Do not force double-quotes on
								 * identifiers */
805
				force_quotes = false;
806
				break;
807

808
			case 'N':			/* Force double-quotes on identifiers */
809
				force_quotes = true;
810
				break;
811

812
			case 'o':			/* Dump oids */
813
				oids = true;
814
				break;
815 816


817 818 819
			case 'O':			/* Don't reconnect to match owner */
				outputNoOwner = 1;
				break;
820

821 822 823
			case 'p':			/* server port */
				pgport = optarg;
				break;
824

825 826 827
			case 'R':			/* No reconnect */
				outputNoReconnect = 1;
				break;
828

829
			case 's':			/* dump schema only */
830
				schemaOnly = true;
831
				break;
832

B
Bruce Momjian 已提交
833 834
			case 'S':			/* Username for superuser in plain text
								 * output */
835 836
				outputSuperuser = strdup(optarg);
				break;
837

838
			case 't':			/* Dump data for this table only */
839
				{
840
					int			i;
841 842

					tablename = strdup(optarg);
B
Bruce Momjian 已提交
843 844 845 846 847

					/*
					 * quoted string? Then strip quotes and preserve
					 * case...
					 */
848 849 850
					if (tablename[0] == '"')
					{
						strcpy(tablename, &tablename[1]);
B
Bruce Momjian 已提交
851 852
						if (*(tablename + strlen(tablename) - 1) == '"')
							*(tablename + strlen(tablename) - 1) = '\0';
853 854 855 856 857
					}
					/* otherwise, convert table name to lowercase... */
					else
					{
						for (i = 0; tablename[i]; i++)
858 859
							if (isupper((unsigned char) tablename[i]))
								tablename[i] = tolower((unsigned char) tablename[i]);
860

B
Bruce Momjian 已提交
861 862 863 864 865
						/*
						 * '*' is a special case meaning ALL tables, but
						 * only if unquoted
						 */
						if (strcmp(tablename, "*") == 0)
866 867
							tablename[0] = '\0';

868
					}
869
				}
870
				break;
871

872
			case 'u':
873
				force_password = true;
874
				username = simple_prompt("User name: ", 100, true);
875 876 877 878
				break;

			case 'U':
				username = optarg;
879
				break;
880

881 882
			case 'v':			/* verbose */
				g_verbose = true;
883
				break;
884

885 886 887 888
			case 'W':
				force_password = true;
				break;

889 890
			case 'x':			/* skip ACL dump */
				aclsSkip = true;
891
				break;
892

893
				/*
894
				 * Option letters were getting scarce, so I invented this
895 896 897 898
				 * 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'.
899 900
				 */
			case 'X':
901
				if (strcmp(optarg, "use-set-session-authorization") == 0)
902 903 904 905 906 907 908 909 910 911
					use_setsessauth = 1;
				else
				{
					fprintf(stderr,
							gettext("%s: invalid -X option -- %s\n"),
							progname, optarg);
					fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname);
					exit(1);
				}
				break;
B
Bruce Momjian 已提交
912 913 914
			case 'Z':			/* Compression Level */
				compressLevel = atoi(optarg);
				break;
915

916 917
#ifndef HAVE_GETOPT_LONG
			case '-':
918 919
				fprintf(stderr,
						gettext("%s was compiled without support for long options.\n"
920
						 "Use --help for help on invocation options.\n"),
921
						progname);
922 923
				exit(1);
				break;
924 925 926 927
#else
				/* This covers the long options equivalent to -X xxx. */
			case 0:
				break;
928
#endif
929
			default:
930
				fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname);
931
				exit(1);
932 933 934
		}
	}

B
Bruce Momjian 已提交
935 936
	if (optind < (argc - 1))
	{
P
Philip Warner 已提交
937
		fprintf(stderr,
938 939
			gettext("%s: too many command line options (first is '%s')\n"
					"Try '%s --help' for more information.\n"),
940
				progname, argv[optind + 1], progname);
P
Philip Warner 已提交
941 942 943
		exit(1);
	}

944 945 946 947 948 949 950
	/* Get the target database name */
	if (optind < argc)
		dbname = argv[optind];
	else
		dbname = getenv("PGDATABASE");
	if (!dbname)
	{
951
		write_msg(NULL, "no database name specified\n");
952 953 954
		exit(1);
	}

B
Bruce Momjian 已提交
955 956
	if (dataOnly && schemaOnly)
	{
957
		write_msg(NULL, "The options \"schema only\" (-s) and \"data only\" (-a) cannot be used together.\n");
B
Bruce Momjian 已提交
958 959 960
		exit(1);
	}

B
Bruce Momjian 已提交
961
	if (outputBlobs && tablename != NULL && strlen(tablename) > 0)
962
	{
963
		write_msg(NULL, "Large object output is not supported for a single table.\n");
964
		write_msg(NULL, "Use all tables or a full dump instead.\n");
965 966 967
		exit(1);
	}

968 969
	if (dumpData == true && oids == true)
	{
970 971
		write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together.\n");
		write_msg(NULL, "(The INSERT command cannot set oids.)\n");
972
		exit(1);
973
	}
974

B
Bruce Momjian 已提交
975
	if (outputBlobs == true && (format[0] == 'p' || format[0] == 'P'))
P
Philip Warner 已提交
976
	{
977
		write_msg(NULL, "large object output is not supported for plain text dump files.\n");
978
		write_msg(NULL, "(Use a different output format.)\n");
P
Philip Warner 已提交
979 980 981
		exit(1);
	}

982
	/* open the output file */
B
Bruce Momjian 已提交
983 984
	switch (format[0])
	{
B
Bruce Momjian 已提交
985 986 987 988 989 990 991 992 993 994 995 996 997 998

		case 'c':
		case 'C':
			g_fout = CreateArchive(filename, archCustom, compressLevel);
			break;

		case 'f':
		case 'F':
			g_fout = CreateArchive(filename, archFiles, compressLevel);
			break;

		case 'p':
		case 'P':
			plainText = 1;
999 1000 1001 1002 1003 1004
			g_fout = CreateArchive(filename, archNull, 0);
			break;

		case 't':
		case 'T':
			g_fout = CreateArchive(filename, archTar, compressLevel);
B
Bruce Momjian 已提交
1005 1006 1007
			break;

		default:
1008
			write_msg(NULL, "invalid output format '%s' specified\n", format);
B
Bruce Momjian 已提交
1009
			exit(1);
B
Bruce Momjian 已提交
1010 1011 1012 1013
	}

	if (g_fout == NULL)
	{
1014
		write_msg(NULL, "could not open output file %s for writing\n", filename);
B
Bruce Momjian 已提交
1015
		exit(1);
1016 1017
	}

1018 1019
	/* Let the archiver know how noisy to be */
	g_fout->verbose = g_verbose;
1020

B
Bruce Momjian 已提交
1021 1022
	/*
	 * Open the database using the Archiver, so it knows about it. Errors
1023
	 * mean death.
B
Bruce Momjian 已提交
1024
	 */
1025
	g_fout->minRemoteVersion = 70000;	/* we can handle back to 7.0 */
1026
	g_fout->maxRemoteVersion = parse_version(PG_VERSION);
1027
	g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, username, force_password, ignore_version);
1028

1029 1030 1031 1032 1033 1034 1035 1036
	/*
	 * Start serializable transaction to dump consistent data
	 */
	{
		PGresult   *res;

		res = PQexec(g_conn, "begin");
		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1037
			exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
B
Bruce Momjian 已提交
1038
						  PQerrorMessage(g_conn));
1039

1040 1041 1042
		PQclear(res);
		res = PQexec(g_conn, "set transaction isolation level serializable");
		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1043
			exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
B
Bruce Momjian 已提交
1044
						  PQerrorMessage(g_conn));
1045

1046 1047 1048
		PQclear(res);
	}

1049 1050 1051 1052
	if (g_fout->remoteVersion >= 70100)
		g_last_builtin_oid = findLastBuiltinOid_V71(dbname);
	else
		g_last_builtin_oid = findLastBuiltinOid_V70();
1053

1054 1055 1056 1057
	/* Dump the database definition */
	if (!dataOnly)
		dumpDatabase(g_fout);

1058
	if (oids == true)
1059
		setMaxOid(g_fout);
B
Bruce Momjian 已提交
1060 1061

	if (g_verbose)
1062
		write_msg(NULL, "last built-in oid is %u\n", g_last_builtin_oid);
B
Bruce Momjian 已提交
1063
	tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
1064

1065
	if (!schemaOnly)
B
Bruce Momjian 已提交
1066
		dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes);
1067 1068

	if (outputBlobs)
B
Bruce Momjian 已提交
1069
		ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0);
1070

1071 1072
	if (!dataOnly)				/* dump indexes and triggers at the end
								 * for performance */
V
Vadim B. Mikheev 已提交
1073 1074
	{
		dumpTriggers(g_fout, tablename, tblinfo, numTables);
1075
		dumpRules(g_fout, tablename, tblinfo, numTables);
V
Vadim B. Mikheev 已提交
1076
	}
1077

1078 1079
	/* Now sort the output nicely */
	SortTocByOID(g_fout);
1080
	MoveToStart(g_fout, "DATABASE");
1081 1082 1083
	MoveToEnd(g_fout, "TABLE DATA");
	MoveToEnd(g_fout, "BLOBS");
	MoveToEnd(g_fout, "INDEX");
1084
	MoveToEnd(g_fout, "CONSTRAINT");
1085 1086
	MoveToEnd(g_fout, "TRIGGER");
	MoveToEnd(g_fout, "RULE");
1087
	MoveToEnd(g_fout, "SEQUENCE SET");
1088

1089 1090 1091 1092 1093 1094
	/*
	 * Moving all comments to end is annoying, but must do it for comments
	 * on stuff we just moved, and we don't seem to have quite enough
	 * dependency structure to get it really right...
	 */
	MoveToEnd(g_fout, "COMMENT");
1095

B
Bruce Momjian 已提交
1096
	if (plainText)
B
Bruce Momjian 已提交
1097 1098
	{
		ropt = NewRestoreOptions();
B
Bruce Momjian 已提交
1099
		ropt->filename = (char *) filename;
B
Bruce Momjian 已提交
1100 1101
		ropt->dropSchema = outputClean;
		ropt->aclsSkip = aclsSkip;
1102 1103 1104 1105
		ropt->superuser = outputSuperuser;
		ropt->create = outputCreate;
		ropt->noOwner = outputNoOwner;
		ropt->noReconnect = outputNoReconnect;
1106
		ropt->use_setsessauth = use_setsessauth;
1107 1108 1109 1110 1111

		if (outputSuperuser)
			ropt->superuser = outputSuperuser;
		else
			ropt->superuser = PQuser(g_conn);
B
Bruce Momjian 已提交
1112 1113

		if (compressLevel == -1)
B
Bruce Momjian 已提交
1114
			ropt->compression = 0;
B
Bruce Momjian 已提交
1115
		else
B
Bruce Momjian 已提交
1116
			ropt->compression = compressLevel;
B
Bruce Momjian 已提交
1117

1118 1119
		ropt->suppressDumpWarnings = true;		/* We've already shown
												 * them */
1120

B
Bruce Momjian 已提交
1121 1122 1123 1124
		RestoreArchive(g_fout, ropt);
	}

	CloseArchive(g_fout);
1125

1126 1127 1128
	clearTableInfo(tblinfo, numTables);
	PQfinish(g_conn);
	exit(0);
1129 1130
}

1131 1132 1133 1134 1135
/*
 * dumpDatabase:
 *	dump the database definition
 *
 */
B
Bruce Momjian 已提交
1136
static int
1137 1138
dumpDatabase(Archive *AH)
{
B
Bruce Momjian 已提交
1139 1140 1141 1142 1143
	PQExpBuffer dbQry = createPQExpBuffer();
	PQExpBuffer delQry = createPQExpBuffer();
	PQExpBuffer creaQry = createPQExpBuffer();
	PGresult   *res;
	int			ntups;
1144 1145 1146 1147 1148 1149 1150 1151 1152
	int			i_dba,
				i_encoding,
				i_datpath;
	const char *datname,
			   *dba,
			   *encoding,
			   *datpath;

	datname = PQdb(g_conn);
1153 1154

	if (g_verbose)
1155
		write_msg(NULL, "saving database definition\n");
1156

1157 1158 1159
	/* Get the database owner and parameters from pg_database */
	appendPQExpBuffer(dbQry, "select (select usename from pg_user where usesysid = datdba) as dba,"
					  " encoding, datpath from pg_database"
B
Bruce Momjian 已提交
1160
					  " where datname = ");
1161
	formatStringLiteral(dbQry, datname, CONV_ALL);
1162 1163 1164 1165 1166

	res = PQexec(g_conn, dbQry->data);
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
1167 1168 1169
		write_msg(NULL, "SQL command failed\n");
		write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
		write_msg(NULL, "The command was: %s\n", dbQry->data);
1170
		exit_nicely();
1171 1172 1173 1174
	}

	ntups = PQntuples(res);

1175 1176
	if (ntups <= 0)
	{
1177 1178
		write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
				  datname);
1179
		exit_nicely();
1180 1181
	}

1182 1183
	if (ntups != 1)
	{
1184
		write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
1185
				  ntups, datname);
1186
		exit_nicely();
1187 1188 1189
	}

	i_dba = PQfnumber(res, "dba");
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
	i_encoding = PQfnumber(res, "encoding");
	i_datpath = PQfnumber(res, "datpath");
	dba = PQgetvalue(res, 0, i_dba);
	encoding = PQgetvalue(res, 0, i_encoding);
	datpath = PQgetvalue(res, 0, i_datpath);

	appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
					  fmtId(datname, force_quotes));
	if (strlen(encoding) > 0)
		appendPQExpBuffer(creaQry, " ENCODING = %s", encoding);
	if (strlen(datpath) > 0)
		appendPQExpBuffer(creaQry, " LOCATION = '%s'", datpath);
	appendPQExpBuffer(creaQry, ";\n");

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

	ArchiveEntry(AH, "0" /* OID */ , datname /* Name */ , "DATABASE", NULL,
B
Bruce Momjian 已提交
1208
				 creaQry->data /* Create */ , delQry->data /* Del */ ,
1209
				 "" /* Copy */ , dba /* Owner */ ,
B
Bruce Momjian 已提交
1210
				 NULL /* Dumper */ , NULL /* Dumper Arg */ );
1211 1212 1213

	PQclear(res);

1214 1215 1216 1217
	destroyPQExpBuffer(dbQry);
	destroyPQExpBuffer(delQry);
	destroyPQExpBuffer(creaQry);

1218 1219 1220 1221
	return 1;
}


1222 1223 1224 1225 1226 1227
/*
 * dumpBlobs:
 *	dump all blobs
 *
 */

B
Bruce Momjian 已提交
1228
#define loBufSize 16384
1229 1230
#define loFetchSize 1000

B
Bruce Momjian 已提交
1231 1232
static int
dumpBlobs(Archive *AH, char *junkOid, void *junkVal)
1233
{
B
Bruce Momjian 已提交
1234 1235 1236 1237 1238 1239 1240
	PQExpBuffer oidQry = createPQExpBuffer();
	PQExpBuffer oidFetchQry = createPQExpBuffer();
	PGresult   *res;
	int			i;
	int			loFd;
	char		buf[loBufSize];
	int			cnt;
1241
	Oid			blobOid;
1242 1243

	if (g_verbose)
1244
		write_msg(NULL, "saving large objects\n");
1245 1246

	/* Cursor to get all BLOB tables */
1247 1248
	if (AH->remoteVersion >= 70100)
		appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT DISTINCT loid FROM pg_largeobject");
1249
	else
1250
		appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
1251 1252 1253 1254

	res = PQexec(g_conn, oidQry->data);
	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
	{
1255
		write_msg(NULL, "dumpBlobs(): cursor declaration failed: %s", PQerrorMessage(g_conn));
1256
		exit_nicely();
1257 1258 1259 1260 1261
	}

	/* Fetch for cursor */
	appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);

B
Bruce Momjian 已提交
1262 1263
	do
	{
1264 1265 1266 1267 1268 1269
		/* Do a fetch */
		PQclear(res);
		res = PQexec(g_conn, oidFetchQry->data);

		if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
		{
1270 1271
			write_msg(NULL, "dumpBlobs(): fetch from cursor failed: %s",
					  PQerrorMessage(g_conn));
1272
			exit_nicely();
1273 1274 1275 1276 1277
		}

		/* Process the tuples, if any */
		for (i = 0; i < PQntuples(res); i++)
		{
1278
			blobOid = atooid(PQgetvalue(res, i, 0));
1279 1280 1281 1282
			/* Open the BLOB */
			loFd = lo_open(g_conn, blobOid, INV_READ);
			if (loFd == -1)
			{
1283 1284
				write_msg(NULL, "dumpBlobs(): could not open large object: %s",
						  PQerrorMessage(g_conn));
1285
				exit_nicely();
1286 1287 1288 1289 1290
			}

			StartBlob(AH, blobOid);

			/* Now read it in chunks, sending data to archive */
B
Bruce Momjian 已提交
1291 1292
			do
			{
1293
				cnt = lo_read(g_conn, loFd, buf, loBufSize);
B
Bruce Momjian 已提交
1294 1295
				if (cnt < 0)
				{
1296 1297
					write_msg(NULL, "dumpBlobs(): error reading large object: %s",
							  PQerrorMessage(g_conn));
1298
					exit_nicely();
1299 1300
				}

B
Bruce Momjian 已提交
1301
				WriteData(AH, buf, cnt);
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311

			} while (cnt > 0);

			lo_close(g_conn, loFd);

			EndBlob(AH, blobOid);

		}
	} while (PQntuples(res) > 0);

1312 1313 1314
	destroyPQExpBuffer(oidQry);
	destroyPQExpBuffer(oidFetchQry);

1315 1316 1317
	return 1;
}

1318
/*
1319 1320
 * getTypes:
 *	  read all base types in the system catalogs and return them in the
1321 1322
 * TypeInfo* structure
 *
1323
 *	numTypes is set to the number of types read in
1324 1325
 *
 */
1326
TypeInfo *
1327 1328
getTypes(int *numTypes)
{
B
Bruce Momjian 已提交
1329
	PGresult   *res;
1330 1331
	int			ntups;
	int			i;
1332
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
1333
	TypeInfo   *tinfo;
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347

	int			i_oid;
	int			i_typowner;
	int			i_typname;
	int			i_typlen;
	int			i_typprtlen;
	int			i_typinput;
	int			i_typoutput;
	int			i_typreceive;
	int			i_typsend;
	int			i_typelem;
	int			i_typdelim;
	int			i_typdefault;
	int			i_typrelid;
1348 1349
	int			i_typalign;
	int			i_typstorage;
1350
	int			i_typbyval;
1351
	int			i_typisdefined;
1352
	int			i_usename;
1353
	int			i_typedefn;
1354 1355 1356 1357 1358 1359

	/* find all base types */

	/*
	 * we include even the built-in types because those may be used as
	 * array elements by user-defined types
1360
	 *
1361 1362 1363
	 * we filter out the built-in types when we dump out the types
	 */

1364 1365 1366
	if (g_fout->remoteVersion < 70100)
	{
		appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, "
1367 1368 1369 1370 1371 1372 1373 1374
		  "typinput, typoutput, typreceive, typsend, typelem, typdelim, "
						  "typdefault, typrelid, typalign, 'p'::char as typstorage, typbyval, typisdefined, "
						  "(select usename from pg_user where typowner = usesysid) as usename, "
						  "typname as typedefn "
						  "from pg_type");
	}
	else
	{
1375
		appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, "
1376 1377 1378 1379 1380
		  "typinput, typoutput, typreceive, typsend, typelem, typdelim, "
						  "typdefault, typrelid, typalign, typstorage, typbyval, typisdefined, "
						  "(select usename from pg_user where typowner = usesysid) as usename, "
						  "format_type(pg_type.oid, NULL) as typedefn "
						  "from pg_type");
1381
	}
1382

B
Hi, all  
Bruce Momjian 已提交
1383
	res = PQexec(g_conn, query->data);
1384 1385 1386
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
1387
		write_msg(NULL, "query to obtain list of data types failed: %s", PQerrorMessage(g_conn));
1388
		exit_nicely();
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
	}

	ntups = PQntuples(res);

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

	i_oid = PQfnumber(res, "oid");
	i_typowner = PQfnumber(res, "typowner");
	i_typname = PQfnumber(res, "typname");
	i_typlen = PQfnumber(res, "typlen");
	i_typprtlen = PQfnumber(res, "typprtlen");
	i_typinput = PQfnumber(res, "typinput");
	i_typoutput = PQfnumber(res, "typoutput");
	i_typreceive = PQfnumber(res, "typreceive");
	i_typsend = PQfnumber(res, "typsend");
	i_typelem = PQfnumber(res, "typelem");
	i_typdelim = PQfnumber(res, "typdelim");
	i_typdefault = PQfnumber(res, "typdefault");
	i_typrelid = PQfnumber(res, "typrelid");
1408 1409
	i_typalign = PQfnumber(res, "typalign");
	i_typstorage = PQfnumber(res, "typstorage");
1410
	i_typbyval = PQfnumber(res, "typbyval");
1411
	i_typisdefined = PQfnumber(res, "typisdefined");
1412
	i_usename = PQfnumber(res, "usename");
1413
	i_typedefn = PQfnumber(res, "typedefn");
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427

	for (i = 0; i < ntups; i++)
	{
		tinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
		tinfo[i].typowner = strdup(PQgetvalue(res, i, i_typowner));
		tinfo[i].typname = strdup(PQgetvalue(res, i, i_typname));
		tinfo[i].typlen = strdup(PQgetvalue(res, i, i_typlen));
		tinfo[i].typprtlen = strdup(PQgetvalue(res, i, i_typprtlen));
		tinfo[i].typinput = strdup(PQgetvalue(res, i, i_typinput));
		tinfo[i].typoutput = strdup(PQgetvalue(res, i, i_typoutput));
		tinfo[i].typreceive = strdup(PQgetvalue(res, i, i_typreceive));
		tinfo[i].typsend = strdup(PQgetvalue(res, i, i_typsend));
		tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
		tinfo[i].typdelim = strdup(PQgetvalue(res, i, i_typdelim));
1428 1429 1430 1431
		if (PQgetisnull(res, i, i_typdefault))
			tinfo[i].typdefault = NULL;
		else
			tinfo[i].typdefault = strdup(PQgetvalue(res, i, i_typdefault));
1432
		tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
1433 1434
		tinfo[i].typalign = strdup(PQgetvalue(res, i, i_typalign));
		tinfo[i].typstorage = strdup(PQgetvalue(res, i, i_typstorage));
1435
		tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1436
		tinfo[i].typedefn = strdup(PQgetvalue(res, i, i_typedefn));
1437

1438
		if (strlen(tinfo[i].usename) == 0)
1439 1440
			write_msg(NULL, "WARNING: owner of data type %s appears to be invalid\n",
					  tinfo[i].typname);
1441

1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
		if (strcmp(PQgetvalue(res, i, i_typbyval), "f") == 0)
			tinfo[i].passedbyvalue = 0;
		else
			tinfo[i].passedbyvalue = 1;

		/*
		 * check for user-defined array types, omit system generated ones
		 */
		if ((strcmp(tinfo[i].typelem, "0") != 0) &&
			tinfo[i].typname[0] != '_')
			tinfo[i].isArray = 1;
		else
			tinfo[i].isArray = 0;
1455 1456 1457 1458 1459

		if (strcmp(PQgetvalue(res, i, i_typisdefined), "f") == 0)
			tinfo[i].isDefined = 0;
		else
			tinfo[i].isDefined = 1;
1460 1461 1462 1463 1464 1465
	}

	*numTypes = ntups;

	PQclear(res);

1466 1467
	destroyPQExpBuffer(query);

1468
	return tinfo;
1469 1470 1471 1472
}

/*
 * getOperators:
1473
 *	  read all operators in the system catalogs and return them in the
1474 1475
 * OprInfo* structure
 *
1476
 *	numOprs is set to the number of operators read in
1477
 */
1478
OprInfo *
1479 1480
getOperators(int *numOprs)
{
1481
	PGresult   *res;
1482 1483
	int			ntups;
	int			i;
1484
	PQExpBuffer query = createPQExpBuffer();
1485

B
Bruce Momjian 已提交
1486
	OprInfo    *oprinfo;
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501

	int			i_oid;
	int			i_oprname;
	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_usename;
1502 1503 1504 1505 1506 1507

	/*
	 * find all operators, including builtin operators, filter out
	 * system-defined operators at dump-out time
	 */

B
Hi, all  
Bruce Momjian 已提交
1508
	appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, oprkind, oprcode, "
1509
			   "oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, "
B
Bruce Momjian 已提交
1510 1511 1512
					  "oprcanhash, oprlsortop, oprrsortop, "
	"(select usename from pg_user where oprowner = usesysid) as usename "
					  "from pg_operator");
1513

B
Hi, all  
Bruce Momjian 已提交
1514
	res = PQexec(g_conn, query->data);
1515 1516 1517
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
1518
		write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
1519
		exit_nicely();
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
	}

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

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

	i_oid = PQfnumber(res, "oid");
	i_oprname = PQfnumber(res, "oprname");
	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_usename = PQfnumber(res, "usename");

	for (i = 0; i < ntups; i++)
	{
		oprinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
		oprinfo[i].oprname = strdup(PQgetvalue(res, i, i_oprname));
		oprinfo[i].oprkind = strdup(PQgetvalue(res, i, i_oprkind));
		oprinfo[i].oprcode = strdup(PQgetvalue(res, i, i_oprcode));
		oprinfo[i].oprleft = strdup(PQgetvalue(res, i, i_oprleft));
		oprinfo[i].oprright = strdup(PQgetvalue(res, i, i_oprright));
		oprinfo[i].oprcom = strdup(PQgetvalue(res, i, i_oprcom));
		oprinfo[i].oprnegate = strdup(PQgetvalue(res, i, i_oprnegate));
		oprinfo[i].oprrest = strdup(PQgetvalue(res, i, i_oprrest));
		oprinfo[i].oprjoin = strdup(PQgetvalue(res, i, i_oprjoin));
		oprinfo[i].oprcanhash = strdup(PQgetvalue(res, i, i_oprcanhash));
		oprinfo[i].oprlsortop = strdup(PQgetvalue(res, i, i_oprlsortop));
		oprinfo[i].oprrsortop = strdup(PQgetvalue(res, i, i_oprrsortop));
		oprinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1558 1559

		if (strlen(oprinfo[i].usename) == 0)
1560 1561
			write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
					  oprinfo[i].oprname);
1562

1563 1564 1565 1566
	}

	PQclear(res);

1567 1568
	destroyPQExpBuffer(query);

1569
	return oprinfo;
1570 1571
}

1572
void
1573
clearTypeInfo(TypeInfo *tp, int numTypes)
1574
{
1575
	int			i;
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604

	for (i = 0; i < numTypes; ++i)
	{
		if (tp[i].oid)
			free(tp[i].oid);
		if (tp[i].typowner)
			free(tp[i].typowner);
		if (tp[i].typname)
			free(tp[i].typname);
		if (tp[i].typlen)
			free(tp[i].typlen);
		if (tp[i].typprtlen)
			free(tp[i].typprtlen);
		if (tp[i].typinput)
			free(tp[i].typinput);
		if (tp[i].typoutput)
			free(tp[i].typoutput);
		if (tp[i].typreceive)
			free(tp[i].typreceive);
		if (tp[i].typsend)
			free(tp[i].typsend);
		if (tp[i].typelem)
			free(tp[i].typelem);
		if (tp[i].typdelim)
			free(tp[i].typdelim);
		if (tp[i].typdefault)
			free(tp[i].typdefault);
		if (tp[i].typrelid)
			free(tp[i].typrelid);
1605 1606 1607 1608
		if (tp[i].typalign)
			free(tp[i].typalign);
		if (tp[i].typstorage)
			free(tp[i].typstorage);
1609 1610
		if (tp[i].usename)
			free(tp[i].usename);
1611 1612
		if (tp[i].typedefn)
			free(tp[i].typedefn);
1613 1614
	}
	free(tp);
1615 1616 1617
}

void
1618
clearFuncInfo(FuncInfo *fun, int numFuncs)
1619
{
1620 1621
	int			i,
				a;
1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632

	if (!fun)
		return;
	for (i = 0; i < numFuncs; ++i)
	{
		if (fun[i].oid)
			free(fun[i].oid);
		if (fun[i].proname)
			free(fun[i].proname);
		if (fun[i].usename)
			free(fun[i].usename);
1633
		for (a = 0; a < FUNC_MAX_ARGS; ++a)
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643
			if (fun[i].argtypes[a])
				free(fun[i].argtypes[a]);
		if (fun[i].prorettype)
			free(fun[i].prorettype);
		if (fun[i].prosrc)
			free(fun[i].prosrc);
		if (fun[i].probin)
			free(fun[i].probin);
	}
	free(fun);
1644 1645
}

1646
static void
1647
clearTableInfo(TableInfo *tblinfo, int numTables)
1648
{
1649 1650
	int			i,
				j;
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675

	for (i = 0; i < numTables; ++i)
	{

		if (tblinfo[i].oid)
			free(tblinfo[i].oid);
		if (tblinfo[i].relacl)
			free(tblinfo[i].relacl);
		if (tblinfo[i].usename)
			free(tblinfo[i].usename);

		if (tblinfo[i].relname)
			free(tblinfo[i].relname);

		if (tblinfo[i].sequence)
			continue;

		/* Process Attributes */
		for (j = 0; j < tblinfo[i].numatts; j++)
		{
			if (tblinfo[i].attnames[j])
				free(tblinfo[i].attnames[j]);
			if (tblinfo[i].typnames[j])
				free(tblinfo[i].typnames[j]);
		}
B
Bruce Momjian 已提交
1676

B
Bruce Momjian 已提交
1677 1678 1679
		if (tblinfo[i].triggers)
		{
			for (j = 0; j < tblinfo[i].ntrig; j++)
B
Bruce Momjian 已提交
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
			{
				if (tblinfo[i].triggers[j].tgsrc)
					free(tblinfo[i].triggers[j].tgsrc);
				if (tblinfo[i].triggers[j].oid)
					free(tblinfo[i].triggers[j].oid);
				if (tblinfo[i].triggers[j].tgname)
					free(tblinfo[i].triggers[j].tgname);
				if (tblinfo[i].triggers[j].tgdel)
					free(tblinfo[i].triggers[j].tgdel);
			}
			free(tblinfo[i].triggers);
		}

1693 1694
		if (tblinfo[i].atttypmod)
			free((int *) tblinfo[i].atttypmod);
1695 1696
		if (tblinfo[i].inhAttrs)
			free((int *) tblinfo[i].inhAttrs);
1697 1698 1699 1700
		if (tblinfo[i].inhAttrDef)
			free((int *) tblinfo[i].inhAttrDef);
		if (tblinfo[i].inhNotNull)
			free((int *) tblinfo[i].inhNotNull);
1701 1702
		if (tblinfo[i].attnames)
			free(tblinfo[i].attnames);
1703 1704
		if (tblinfo[i].atttypedefns)
			free(tblinfo[i].atttypedefns);
1705 1706 1707 1708
		if (tblinfo[i].typnames)
			free(tblinfo[i].typnames);
		if (tblinfo[i].notnull)
			free(tblinfo[i].notnull);
1709 1710
		if (tblinfo[i].primary_key_name)
			free(tblinfo[i].primary_key_name);
1711 1712
	}
	free(tblinfo);
1713 1714
}

1715
void
1716
clearInhInfo(InhInfo *inh, int numInherits)
1717
{
1718
	int			i;
1719 1720 1721 1722 1723

	if (!inh)
		return;
	for (i = 0; i < numInherits; ++i)
	{
1724 1725
		if (inh[i].inhrelid)
			free(inh[i].inhrelid);
1726 1727 1728 1729
		if (inh[i].inhparent)
			free(inh[i].inhparent);
	}
	free(inh);
1730 1731 1732
}

void
1733
clearOprInfo(OprInfo *opr, int numOprs)
1734
{
1735
	int			i;
1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770

	if (!opr)
		return;
	for (i = 0; i < numOprs; ++i)
	{
		if (opr[i].oid)
			free(opr[i].oid);
		if (opr[i].oprname)
			free(opr[i].oprname);
		if (opr[i].oprkind)
			free(opr[i].oprkind);
		if (opr[i].oprcode)
			free(opr[i].oprcode);
		if (opr[i].oprleft)
			free(opr[i].oprleft);
		if (opr[i].oprright)
			free(opr[i].oprright);
		if (opr[i].oprcom)
			free(opr[i].oprcom);
		if (opr[i].oprnegate)
			free(opr[i].oprnegate);
		if (opr[i].oprrest)
			free(opr[i].oprrest);
		if (opr[i].oprjoin)
			free(opr[i].oprjoin);
		if (opr[i].oprcanhash)
			free(opr[i].oprcanhash);
		if (opr[i].oprlsortop)
			free(opr[i].oprlsortop);
		if (opr[i].oprrsortop)
			free(opr[i].oprrsortop);
		if (opr[i].usename)
			free(opr[i].usename);
	}
	free(opr);
1771 1772 1773
}

void
1774
clearIndInfo(IndInfo *ind, int numIndexes)
1775
{
1776 1777
	int			i,
				a;
1778 1779 1780

	if (!ind)
		return;
1781
	for (i = 0; i < numIndexes; ++i)
1782
	{
1783 1784 1785 1786
		if (ind[i].indexreloid)
			free(ind[i].indexreloid);
		if (ind[i].indreloid)
			free(ind[i].indreloid);
1787 1788 1789 1790
		if (ind[i].indexrelname)
			free(ind[i].indexrelname);
		if (ind[i].indrelname)
			free(ind[i].indrelname);
1791 1792
		if (ind[i].indexdef)
			free(ind[i].indexdef);
1793 1794
		if (ind[i].indisprimary)
			free(ind[i].indisprimary);
1795 1796 1797 1798 1799 1800 1801
		for (a = 0; a < INDEX_MAX_KEYS; ++a)
		{
			if (ind[i].indkey[a])
				free(ind[i].indkey[a]);
		}
	}
	free(ind);
1802 1803 1804
}

void
B
Bruce Momjian 已提交
1805
clearAggInfo(AggInfo *agginfo, int numArgs)
1806
{
1807
	int			i;
1808 1809 1810 1811 1812 1813 1814 1815 1816

	if (!agginfo)
		return;
	for (i = 0; i < numArgs; ++i)
	{
		if (agginfo[i].oid)
			free(agginfo[i].oid);
		if (agginfo[i].aggname)
			free(agginfo[i].aggname);
1817 1818
		if (agginfo[i].aggtransfn)
			free(agginfo[i].aggtransfn);
1819 1820
		if (agginfo[i].aggfinalfn)
			free(agginfo[i].aggfinalfn);
1821 1822
		if (agginfo[i].aggtranstype)
			free(agginfo[i].aggtranstype);
1823 1824
		if (agginfo[i].aggbasetype)
			free(agginfo[i].aggbasetype);
1825 1826
		if (agginfo[i].agginitval)
			free(agginfo[i].agginitval);
1827 1828 1829 1830
		if (agginfo[i].usename)
			free(agginfo[i].usename);
	}
	free(agginfo);
1831
}
1832 1833 1834

/*
 * getAggregates:
1835
 *	  read all the user-defined aggregates in the system catalogs and
1836 1837
 * return them in the AggInfo* structure
 *
1838
 * numAggs is set to the number of aggregates read in
1839
 */
1840
AggInfo *
1841 1842
getAggregates(int *numAggs)
{
B
Bruce Momjian 已提交
1843
	PGresult   *res;
1844 1845
	int			ntups;
	int			i;
1846
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
1847
	AggInfo    *agginfo;
1848 1849 1850

	int			i_oid;
	int			i_aggname;
1851
	int			i_aggtransfn;
1852
	int			i_aggfinalfn;
1853
	int			i_aggtranstype;
1854
	int			i_aggbasetype;
1855
	int			i_agginitval;
1856
	int			i_usename;
1857
	int			i_convertok;
1858 1859 1860

	/* find all user-defined aggregates */

1861 1862 1863
	if (g_fout->remoteVersion < 70100)
	{
		appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn1 as aggtransfn, "
1864 1865 1866 1867 1868 1869 1870 1871
			   "aggfinalfn, aggtranstype1 as aggtranstype, aggbasetype, "
						  "agginitval1 as agginitval, "
						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok, "
						  "(select usename from pg_user where aggowner = usesysid) as usename "
						  "from pg_aggregate");
	}
	else
	{
1872
		appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn, "
1873 1874 1875 1876 1877
						  "aggfinalfn, aggtranstype, aggbasetype, "
						  "agginitval, "
						  "'t'::boolean as convertok, "
						  "(select usename from pg_user where aggowner = usesysid) as usename "
						  "from pg_aggregate");
1878
	}
1879

B
Hi, all  
Bruce Momjian 已提交
1880
	res = PQexec(g_conn, query->data);
1881 1882 1883
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
1884
		write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
1885
				  PQerrorMessage(g_conn));
1886
		exit_nicely();
1887 1888 1889 1890 1891 1892 1893 1894 1895
	}

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

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

	i_oid = PQfnumber(res, "oid");
	i_aggname = PQfnumber(res, "aggname");
1896
	i_aggtransfn = PQfnumber(res, "aggtransfn");
1897
	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
1898
	i_aggtranstype = PQfnumber(res, "aggtranstype");
1899
	i_aggbasetype = PQfnumber(res, "aggbasetype");
1900
	i_agginitval = PQfnumber(res, "agginitval");
1901
	i_usename = PQfnumber(res, "usename");
1902
	i_convertok = PQfnumber(res, "convertok");
1903 1904 1905 1906 1907

	for (i = 0; i < ntups; i++)
	{
		agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
		agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname));
1908
		agginfo[i].aggtransfn = strdup(PQgetvalue(res, i, i_aggtransfn));
1909
		agginfo[i].aggfinalfn = strdup(PQgetvalue(res, i, i_aggfinalfn));
1910
		agginfo[i].aggtranstype = strdup(PQgetvalue(res, i, i_aggtranstype));
1911
		agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype));
1912
		agginfo[i].agginitval = strdup(PQgetvalue(res, i, i_agginitval));
1913
		agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1914
		if (strlen(agginfo[i].usename) == 0)
1915 1916
			write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
					  agginfo[i].aggname);
1917

1918 1919
		agginfo[i].convertok = (PQgetvalue(res, i, i_convertok)[0] == 't');

1920 1921 1922 1923
	}

	PQclear(res);

1924 1925
	destroyPQExpBuffer(query);

1926
	return agginfo;
1927 1928 1929 1930
}

/*
 * getFuncs:
1931
 *	  read all the user-defined functions in the system catalogs and
1932 1933
 * return them in the FuncInfo* structure
 *
1934 1935
 * numFuncs is set to the number of functions read in
 *
1936 1937
 *
 */
1938
FuncInfo *
1939 1940
getFuncs(int *numFuncs)
{
1941
	PGresult   *res;
1942 1943
	int			ntups;
	int			i;
1944 1945
	PQExpBuffer query = createPQExpBuffer();
	FuncInfo   *finfo;
1946 1947 1948 1949 1950 1951 1952 1953 1954 1955

	int			i_oid;
	int			i_proname;
	int			i_prolang;
	int			i_pronargs;
	int			i_proargtypes;
	int			i_prorettype;
	int			i_proretset;
	int			i_prosrc;
	int			i_probin;
1956
	int			i_iscachable;
1957
	int			i_isstrict;
1958
	int			i_usename;
1959 1960 1961

	/* find all user-defined funcs */

1962 1963 1964 1965
	if (g_fout->remoteVersion < 70100)
	{
		appendPQExpBuffer(query,
		   "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, "
1966 1967 1968 1969 1970 1971 1972 1973 1974
						  "proretset, proargtypes, prosrc, probin, "
						  "(select usename from pg_user where proowner = usesysid) as usename, "
						  "proiscachable, 'f'::boolean as proisstrict "
						  "from pg_proc "
						  "where pg_proc.oid > '%u'::oid",
						  g_last_builtin_oid);
	}
	else
	{
1975
		appendPQExpBuffer(query,
1976
		   "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, "
1977 1978 1979 1980 1981 1982
						  "proretset, proargtypes, prosrc, probin, "
						  "(select usename from pg_user where proowner = usesysid) as usename, "
						  "proiscachable, proisstrict "
						  "from pg_proc "
						  "where pg_proc.oid > '%u'::oid",
						  g_last_builtin_oid);
1983
	}
1984

B
Hi, all  
Bruce Momjian 已提交
1985
	res = PQexec(g_conn, query->data);
1986 1987 1988
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
1989 1990
		write_msg(NULL, "query to obtain list of functions failed: %s",
				  PQerrorMessage(g_conn));
1991
		exit_nicely();
1992 1993 1994 1995 1996 1997 1998 1999
	}

	ntups = PQntuples(res);

	*numFuncs = ntups;

	finfo = (FuncInfo *) malloc(ntups * sizeof(FuncInfo));

2000 2001
	memset((char *) finfo, 0, ntups * sizeof(FuncInfo));

2002 2003 2004 2005 2006 2007 2008 2009 2010
	i_oid = PQfnumber(res, "oid");
	i_proname = PQfnumber(res, "proname");
	i_prolang = PQfnumber(res, "prolang");
	i_pronargs = PQfnumber(res, "pronargs");
	i_proargtypes = PQfnumber(res, "proargtypes");
	i_prorettype = PQfnumber(res, "prorettype");
	i_proretset = PQfnumber(res, "proretset");
	i_prosrc = PQfnumber(res, "prosrc");
	i_probin = PQfnumber(res, "probin");
2011
	i_iscachable = PQfnumber(res, "proiscachable");
2012
	i_isstrict = PQfnumber(res, "proisstrict");
2013 2014 2015 2016 2017 2018 2019
	i_usename = PQfnumber(res, "usename");

	for (i = 0; i < ntups; i++)
	{
		finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
		finfo[i].proname = strdup(PQgetvalue(res, i, i_proname));

2020
		finfo[i].prosrc = strdup(PQgetvalue(res, i, i_prosrc));
2021 2022 2023 2024 2025
		finfo[i].probin = strdup(PQgetvalue(res, i, i_probin));

		finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
		finfo[i].retset = (strcmp(PQgetvalue(res, i, i_proretset), "t") == 0);
		finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
2026
		finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
2027
		finfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
B
Bruce Momjian 已提交
2028 2029
		finfo[i].iscachable = (strcmp(PQgetvalue(res, i, i_iscachable), "t") == 0);
		finfo[i].isstrict = (strcmp(PQgetvalue(res, i, i_isstrict), "t") == 0);
2030 2031

		if (strlen(finfo[i].usename) == 0)
2032 2033
			write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n",
					  finfo[i].proname);
2034

2035 2036
		if (finfo[i].nargs < 0 || finfo[i].nargs > FUNC_MAX_ARGS)
		{
2037 2038
			write_msg(NULL, "failed sanity check: function %s has more than %d (namely %d) arguments\n",
					  finfo[i].proname, FUNC_MAX_ARGS, finfo[i].nargs);
2039
			exit_nicely();
2040 2041 2042 2043
		}
		parseNumericArray(PQgetvalue(res, i, i_proargtypes),
						  finfo[i].argtypes,
						  finfo[i].nargs);
2044 2045 2046 2047 2048
		finfo[i].dumped = 0;
	}

	PQclear(res);

2049
	destroyPQExpBuffer(query);
2050

2051
	return finfo;
2052 2053 2054 2055
}

/*
 * getTables
2056
 *	  read all the user-defined tables (no indexes, no catalogs)
2057 2058
 * in the system catalogs return them in the TableInfo* structure
 *
2059
 * numTables is set to the number of tables read in
2060
 */
2061
TableInfo *
2062
getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename)
2063
{
2064
	PGresult   *res;
2065 2066
	int			ntups;
	int			i;
2067
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
2068
	PQExpBuffer delqry = createPQExpBuffer();
2069
	PQExpBuffer lockquery = createPQExpBuffer();
2070
	TableInfo  *tblinfo;
2071

B
Bruce Momjian 已提交
2072
	int			i_reloid;
2073 2074 2075 2076
	int			i_relname;
	int			i_relkind;
	int			i_relacl;
	int			i_usename;
V
Vadim B. Mikheev 已提交
2077 2078
	int			i_relchecks;
	int			i_reltriggers;
2079
	int			i_relhasindex;
2080
	int			i_relhasoids;
2081

2082
	/*
2083
	 * find all the user-defined tables (no indexes and no catalogs),
2084 2085 2086
	 * ordering by oid is important so that we always process the parent
	 * tables before the child tables when traversing the tblinfo*
	 *
2087
	 * we ignore tables that are not type 'r' (ordinary relation) or 'S'
2088
	 * (sequence) or 'v' (view).
2089 2090
	 */

2091
	if (g_fout->remoteVersion >= 70200)
2092 2093
	{
		appendPQExpBuffer(query,
2094
						"SELECT pg_class.oid, relname, relacl, relkind, "
2095
						  "(select usename from pg_user where relowner = usesysid) as usename, "
2096
					   "relchecks, reltriggers, relhasindex, relhasoids "
2097 2098 2099 2100
						  "from pg_class "
						  "where relname !~ '^pg_' "
						  "and relkind in ('%c', '%c', '%c') "
						  "order by oid",
2101
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2102 2103 2104 2105 2106
	}
	else if (g_fout->remoteVersion >= 70100)
	{
		/* all tables have oids in 7.1 */
		appendPQExpBuffer(query,
2107
						"SELECT pg_class.oid, relname, relacl, relkind, "
2108
						  "(select usename from pg_user where relowner = usesysid) as usename, "
2109
		  "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids "
2110 2111 2112 2113
						  "from pg_class "
						  "where relname !~ '^pg_' "
						  "and relkind in ('%c', '%c', '%c') "
						  "order by oid",
2114 2115 2116 2117
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
	}
	else
	{
2118
		/*
2119 2120
		 * 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.
2121 2122
		 */
		appendPQExpBuffer(query,
2123 2124
						  "SELECT c.oid, relname, relacl, "
						  "CASE WHEN relhasrules and relkind = 'r' "
2125 2126
				  "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
				  "             r.ev_class = c.oid AND r.ev_type = '1') "
2127 2128 2129
						  "THEN '%c'::\"char\" "
						  "ELSE relkind END AS relkind,"
						  "(select usename from pg_user where relowner = usesysid) as usename, "
2130
		  "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids "
2131 2132 2133 2134 2135
						  "from pg_class c "
						  "where relname !~ '^pg_' "
						  "and relkind in ('%c', '%c', '%c') "
						  "order by oid",
						  RELKIND_VIEW,
2136
					   RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2137
	}
2138

B
Hi, all  
Bruce Momjian 已提交
2139
	res = PQexec(g_conn, query->data);
2140 2141 2142
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
2143 2144
		write_msg(NULL, "query to obtain list of tables failed: %s",
				  PQerrorMessage(g_conn));
2145
		exit_nicely();
2146 2147 2148 2149 2150 2151
	}

	ntups = PQntuples(res);

	*numTables = ntups;

2152 2153
	/*
	 * First pass: extract data from result and lock tables.  We do the
2154 2155
	 * locking before anything else, to minimize the window wherein a
	 * table could disappear under us.
2156 2157 2158 2159 2160
	 *
	 * Note that we have to collect info about all tables here, even when
	 * dumping only one, because we don't know which tables might be
	 * inheritance ancestors of the target table.  Possible future
	 * improvement: suppress later collection of schema info about tables
2161 2162
	 * that are determined not to be either targets or ancestors of
	 * targets.
2163
	 */
2164 2165
	tblinfo = (TableInfo *) malloc(ntups * sizeof(TableInfo));

B
Bruce Momjian 已提交
2166
	i_reloid = PQfnumber(res, "oid");
2167 2168
	i_relname = PQfnumber(res, "relname");
	i_relacl = PQfnumber(res, "relacl");
2169
	i_relkind = PQfnumber(res, "relkind");
2170
	i_usename = PQfnumber(res, "usename");
V
Vadim B. Mikheev 已提交
2171 2172
	i_relchecks = PQfnumber(res, "relchecks");
	i_reltriggers = PQfnumber(res, "reltriggers");
B
Hi, all  
Bruce Momjian 已提交
2173
	i_relhasindex = PQfnumber(res, "relhasindex");
2174
	i_relhasoids = PQfnumber(res, "relhasoids");
2175 2176 2177

	for (i = 0; i < ntups; i++)
	{
B
Bruce Momjian 已提交
2178
		tblinfo[i].oid = strdup(PQgetvalue(res, i, i_reloid));
2179 2180
		tblinfo[i].relname = strdup(PQgetvalue(res, i, i_relname));
		tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
2181 2182 2183 2184
		tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
		tblinfo[i].sequence = (tblinfo[i].relkind == RELKIND_SEQUENCE);
		tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
		tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
2185
		tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
V
Vadim B. Mikheev 已提交
2186 2187
		tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
		tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
2188

2189 2190 2191 2192 2193 2194
		/*
		 * Read-lock target tables to make sure they aren't DROPPED or
		 * altered in schema before we get around to dumping them.
		 *
		 * If no target tablename was specified, lock all tables we see,
		 * otherwise lock only the specified table.  (This is incomplete
2195 2196 2197 2198
		 * because we'll still try to collect schema info about all
		 * tables, and could possibly lose during that phase.  But for the
		 * typical use where we're dumping all tables anyway, it matters
		 * not.)
2199
		 *
2200 2201
		 * NOTE: it'd be kinda nice to lock views and sequences too, not only
		 * plain tables, but the backend doesn't presently allow that.
2202 2203
		 */
		if ((tblinfo[i].relkind == RELKIND_RELATION) &&
2204
		(tablename == NULL || strcmp(tblinfo[i].relname, tablename) == 0))
2205
		{
2206
			PGresult   *lres;
2207 2208 2209 2210 2211

			resetPQExpBuffer(lockquery);
			appendPQExpBuffer(lockquery,
							  "LOCK TABLE %s IN ACCESS SHARE MODE",
							  fmtId(tblinfo[i].relname, force_quotes));
2212
			lres = PQexec(g_conn, lockquery->data);
2213 2214
			if (!lres || PQresultStatus(lres) != PGRES_COMMAND_OK)
			{
2215
				write_msg(NULL, "Attempt to lock table \"%s\" failed.  %s",
2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226
						  tblinfo[i].relname, PQerrorMessage(g_conn));
				exit_nicely();
			}
			PQclear(lres);
		}
	}

	PQclear(res);
	res = NULL;

	/*
2227 2228
	 * Second pass: pick up additional information about each table, as
	 * required.
2229 2230 2231 2232
	 */
	for (i = 0; i < *numTables; i++)
	{
		/* Emit notice if join for owner failed */
2233
		if (strlen(tblinfo[i].usename) == 0)
2234 2235
			write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
					  tblinfo[i].relname);
2236

2237 2238
		/* Get definition if it's a view */
		if (tblinfo[i].relkind == RELKIND_VIEW)
2239 2240 2241 2242
		{
			PGresult   *res2;

			resetPQExpBuffer(query);
2243
			appendPQExpBuffer(query, "SELECT definition as viewdef, ");
2244 2245 2246 2247 2248

			/*
			 * XXX 7.2 - replace with att from pg_views or some other
			 * generic source
			 */
2249
			appendPQExpBuffer(query, "(select oid from pg_rewrite where "
2250 2251
					  " rulename=('_RET' || viewname)::name) as view_oid"
							  " from pg_views where viewname = ");
2252
			formatStringLiteral(query, tblinfo[i].relname, CONV_ALL);
2253 2254
			appendPQExpBuffer(query, ";");

2255 2256 2257
			res2 = PQexec(g_conn, query->data);
			if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
			{
2258 2259
				write_msg(NULL, "query to obtain definition of view \"%s\" failed: %s",
						  tblinfo[i].relname, PQerrorMessage(g_conn));
2260
				exit_nicely();
2261 2262
			}

B
Bruce Momjian 已提交
2263
			if (PQntuples(res2) != 1)
2264 2265
			{
				if (PQntuples(res2) < 1)
2266 2267
					write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
							  tblinfo[i].relname);
B
Bruce Momjian 已提交
2268
				else
2269
					write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
2270
							  tblinfo[i].relname);
2271
				exit_nicely();
2272 2273
			}

2274 2275
			if (PQgetisnull(res2, 0, 1))
			{
2276 2277
				write_msg(NULL, "query to obtain definition of view \"%s\" returned NULL oid\n",
						  tblinfo[i].relname);
2278
				exit_nicely();
2279 2280
			}

2281
			tblinfo[i].viewdef = strdup(PQgetvalue(res2, 0, 0));
2282
			tblinfo[i].viewoid = strdup(PQgetvalue(res2, 0, 1));
2283

B
Bruce Momjian 已提交
2284
			if (strlen(tblinfo[i].viewdef) == 0)
2285
			{
2286 2287
				write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
						  tblinfo[i].relname);
2288
				exit_nicely();
2289
			}
2290
			PQclear(res2);
2291 2292 2293 2294
		}
		else
			tblinfo[i].viewdef = NULL;

2295
		/*
2296 2297
		 * Get non-inherited CHECK constraints, if any.
		 *
B
Bruce Momjian 已提交
2298 2299
		 * Exclude inherited CHECKs from CHECK constraints total. If a
		 * constraint matches by name and condition with a constraint
2300 2301
		 * belonging to a parent class (OR conditions match and both names
		 * start with '$', we assume it was inherited.
2302 2303
		 */
		if (tblinfo[i].ncheck > 0)
V
Vadim B. Mikheev 已提交
2304 2305
		{
			PGresult   *res2;
2306 2307
			int			i_rcname,
						i_rcsrc;
V
Vadim B. Mikheev 已提交
2308 2309
			int			ntups2;
			int			i2;
2310

V
Vadim B. Mikheev 已提交
2311
			if (g_verbose)
2312 2313
				write_msg(NULL, "finding CHECK constraints for table %s\n",
						  tblinfo[i].relname);
2314

B
Hi, all  
Bruce Momjian 已提交
2315 2316
			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "SELECT rcname, rcsrc from pg_relcheck "
2317
							  " where rcrelid = '%s'::oid "
2318
							  "   and not exists "
2319 2320
				   "  (select * from pg_relcheck as c, pg_inherits as i "
							"    where i.inhrelid = pg_relcheck.rcrelid "
2321 2322
							  "      and (c.rcname = pg_relcheck.rcname "
							  "          or (    c.rcname[0] = '$' "
2323
						 "              and pg_relcheck.rcname[0] = '$')"
2324 2325 2326
							  "          )"
							  "      and c.rcsrc = pg_relcheck.rcsrc "
							  "      and c.rcrelid = i.inhparent) "
2327
							  " order by rcname ",
2328
							  tblinfo[i].oid);
B
Hi, all  
Bruce Momjian 已提交
2329
			res2 = PQexec(g_conn, query->data);
V
Vadim B. Mikheev 已提交
2330
			if (!res2 ||
2331
				PQresultStatus(res2) != PGRES_TUPLES_OK)
V
Vadim B. Mikheev 已提交
2332
			{
2333
				write_msg(NULL, "query to obtain check constraints failed: %s", PQerrorMessage(g_conn));
2334
				exit_nicely();
V
Vadim B. Mikheev 已提交
2335 2336
			}
			ntups2 = PQntuples(res2);
2337
			if (ntups2 > tblinfo[i].ncheck)
V
Vadim B. Mikheev 已提交
2338
			{
2339 2340 2341
				write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
						  tblinfo[i].ncheck, tblinfo[i].relname, ntups2);
				write_msg(NULL, "(The system catalogs might be corrupted.)\n");
2342
				exit_nicely();
V
Vadim B. Mikheev 已提交
2343
			}
2344

2345 2346 2347 2348
			/*
			 * Set ncheck to the number of *non-inherited* CHECK
			 * constraints
			 */
2349 2350
			tblinfo[i].ncheck = ntups2;

V
Vadim B. Mikheev 已提交
2351 2352
			i_rcname = PQfnumber(res2, "rcname");
			i_rcsrc = PQfnumber(res2, "rcsrc");
2353
			tblinfo[i].check_expr = (char **) malloc(ntups2 * sizeof(char *));
V
Vadim B. Mikheev 已提交
2354 2355
			for (i2 = 0; i2 < ntups2; i2++)
			{
B
Bruce Momjian 已提交
2356 2357
				const char *name = PQgetvalue(res2, i2, i_rcname);
				const char *expr = PQgetvalue(res2, i2, i_rcsrc);
2358

B
Hi, all  
Bruce Momjian 已提交
2359
				resetPQExpBuffer(query);
2360 2361
				if (name[0] != '$')
				{
2362 2363
					appendPQExpBuffer(query, "CONSTRAINT %s ",
									  fmtId(name, force_quotes));
B
Bruce Momjian 已提交
2364
				}
2365
				appendPQExpBuffer(query, "CHECK (%s)", expr);
B
Hi, all  
Bruce Momjian 已提交
2366
				tblinfo[i].check_expr[i2] = strdup(query->data);
V
Vadim B. Mikheev 已提交
2367 2368 2369 2370 2371
			}
			PQclear(res2);
		}
		else
			tblinfo[i].check_expr = NULL;
2372

2373
		/* Get primary key */
2374
		if (tblinfo[i].hasindex)
2375
		{
2376
			PGresult   *res2;
B
Bruce Momjian 已提交
2377

B
Hi, all  
Bruce Momjian 已提交
2378
			resetPQExpBuffer(query);
B
Bruce Momjian 已提交
2379
			appendPQExpBuffer(query,
2380
							  "SELECT indexrelid FROM pg_index i WHERE i.indisprimary AND i.indrelid = '%s'::oid ",
2381
							  tblinfo[i].oid);
2382
			res2 = PQexec(g_conn, query->data);
B
Bruce Momjian 已提交
2383 2384
			if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
			{
2385 2386
				write_msg(NULL, "query to obtain primary key of table \"%s\" failed: %s",
						  tblinfo[i].relname, PQerrorMessage(g_conn));
2387
				exit_nicely();
B
Bruce Momjian 已提交
2388
			}
2389

B
Bruce Momjian 已提交
2390 2391
			if (PQntuples(res2) > 1)
			{
2392
				write_msg(NULL, "query to obtain primary key of table \"%s\" produced more than one result\n",
2393
						  tblinfo[i].relname);
2394
				exit_nicely();
2395 2396
			}

B
Bruce Momjian 已提交
2397
			if (PQntuples(res2) == 1)
2398
				tblinfo[i].pkIndexOid = strdup(PQgetvalue(res2, 0, 0));
B
Bruce Momjian 已提交
2399
			else
2400 2401
				tblinfo[i].pkIndexOid = NULL;

2402
		}
2403
		else
2404
			tblinfo[i].pkIndexOid = NULL;
B
Bruce Momjian 已提交
2405

2406
		/* Get primary key name (if primary key exist) */
2407
		if (tblinfo[i].pkIndexOid != NULL)
2408 2409
		{
			PGresult   *res2;
B
Bruce Momjian 已提交
2410
			int			n;
2411 2412

			resetPQExpBuffer(query);
2413 2414
			appendPQExpBuffer(query,
							  "SELECT relname FROM pg_class "
2415
							  "WHERE oid = '%s'::oid",
2416
							  tblinfo[i].pkIndexOid);
2417

2418 2419 2420
			res2 = PQexec(g_conn, query->data);
			if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
			{
2421 2422
				write_msg(NULL, "query to obtain name of primary key of table \"%s\" failed: %s",
						  tblinfo[i].relname, PQerrorMessage(g_conn));
2423
				exit_nicely();
2424 2425 2426 2427 2428
			}

			n = PQntuples(res2);
			if (n != 1)
			{
2429 2430 2431 2432 2433 2434
				if (n == 0)
					write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned no rows\n",
							  tblinfo[i].relname);
				else
					write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned %d rows\n",
							  tblinfo[i].relname, n);
2435
				exit_nicely();
2436 2437 2438 2439 2440 2441
			}

			tblinfo[i].primary_key_name =
				strdup(fmtId(PQgetvalue(res2, 0, 0), force_quotes));
			if (tblinfo[i].primary_key_name == NULL)
			{
2442
				write_msg(NULL, "out of memory\n");
2443
				exit_nicely();
2444 2445 2446 2447 2448
			}
		}
		else
			tblinfo[i].primary_key_name = NULL;

V
Vadim B. Mikheev 已提交
2449 2450 2451 2452
		/* Get Triggers */
		if (tblinfo[i].ntrig > 0)
		{
			PGresult   *res2;
B
Bruce,  
Bruce Momjian 已提交
2453
			int			i_tgoid,
2454
						i_tgname,
2455 2456 2457
						i_tgfoid,
						i_tgtype,
						i_tgnargs,
2458 2459 2460 2461
						i_tgargs,
						i_tgisconstraint,
						i_tgconstrname,
						i_tgdeferrable,
2462 2463
						i_tgconstrrelid,
						i_tgconstrrelname,
2464
						i_tginitdeferred;
V
Vadim B. Mikheev 已提交
2465 2466
			int			ntups2;
			int			i2;
2467

V
Vadim B. Mikheev 已提交
2468
			if (g_verbose)
2469
				write_msg(NULL, "finding triggers for table %s\n", tblinfo[i].relname);
2470

B
Hi, all  
Bruce Momjian 已提交
2471
			resetPQExpBuffer(query);
2472
			appendPQExpBuffer(query,
2473 2474 2475 2476 2477
					   "SELECT tgname, tgfoid, tgtype, tgnargs, tgargs, "
						   "tgisconstraint, tgconstrname, tgdeferrable, "
							  "tgconstrrelid, tginitdeferred, oid, "
			  "(select relname from pg_class where oid = tgconstrrelid) "
							  "		as tgconstrrelname "
2478 2479 2480
							  "from pg_trigger "
							  "where tgrelid = '%s'::oid ",
							  tblinfo[i].oid);
B
Hi, all  
Bruce Momjian 已提交
2481
			res2 = PQexec(g_conn, query->data);
V
Vadim B. Mikheev 已提交
2482
			if (!res2 ||
2483
				PQresultStatus(res2) != PGRES_TUPLES_OK)
V
Vadim B. Mikheev 已提交
2484
			{
2485
				write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn));
2486
				exit_nicely();
V
Vadim B. Mikheev 已提交
2487 2488 2489 2490
			}
			ntups2 = PQntuples(res2);
			if (ntups2 != tblinfo[i].ntrig)
			{
2491 2492
				write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
						  tblinfo[i].ntrig, tblinfo[i].relname, ntups2);
2493
				exit_nicely();
V
Vadim B. Mikheev 已提交
2494 2495 2496 2497 2498 2499
			}
			i_tgname = PQfnumber(res2, "tgname");
			i_tgfoid = PQfnumber(res2, "tgfoid");
			i_tgtype = PQfnumber(res2, "tgtype");
			i_tgnargs = PQfnumber(res2, "tgnargs");
			i_tgargs = PQfnumber(res2, "tgargs");
B
Bruce,  
Bruce Momjian 已提交
2500
			i_tgoid = PQfnumber(res2, "oid");
2501 2502 2503
			i_tgisconstraint = PQfnumber(res2, "tgisconstraint");
			i_tgconstrname = PQfnumber(res2, "tgconstrname");
			i_tgdeferrable = PQfnumber(res2, "tgdeferrable");
2504 2505
			i_tgconstrrelid = PQfnumber(res2, "tgconstrrelid");
			i_tgconstrrelname = PQfnumber(res2, "tgconstrrelname");
2506 2507
			i_tginitdeferred = PQfnumber(res2, "tginitdeferred");

B
Bruce Momjian 已提交
2508
			tblinfo[i].triggers = (TrigInfo *) malloc(ntups2 * sizeof(TrigInfo));
B
Hi, all  
Bruce Momjian 已提交
2509 2510
			resetPQExpBuffer(query);
			for (i2 = 0; i2 < ntups2; i2++)
V
Vadim B. Mikheev 已提交
2511
			{
2512
				const char *tgfuncoid = PQgetvalue(res2, i2, i_tgfoid);
2513
				char	   *tgfunc = NULL;
2514 2515
				int2		tgtype = atoi(PQgetvalue(res2, i2, i_tgtype));
				int			tgnargs = atoi(PQgetvalue(res2, i2, i_tgnargs));
B
Bruce Momjian 已提交
2516
				const char *tgargs = PQgetvalue(res2, i2, i_tgargs);
2517 2518 2519
				int			tgisconstraint;
				int			tgdeferrable;
				int			tginitdeferred;
2520 2521
				char	   *tgconstrrelid;
				char	   *tgname;
B
Bruce Momjian 已提交
2522
				const char *p;
2523 2524
				int			findx;

2525 2526
				tgname = PQgetvalue(res2, i2, i_tgname);

2527
				if (strcmp(PQgetvalue(res2, i2, i_tgisconstraint), "f") == 0)
2528
					tgisconstraint = 0;
2529
				else
2530
					tgisconstraint = 1;
2531 2532

				if (strcmp(PQgetvalue(res2, i2, i_tgdeferrable), "f") == 0)
2533
					tgdeferrable = 0;
2534
				else
2535
					tgdeferrable = 1;
2536 2537

				if (strcmp(PQgetvalue(res2, i2, i_tginitdeferred), "f") == 0)
2538
					tginitdeferred = 0;
2539
				else
2540
					tginitdeferred = 1;
2541

V
Vadim B. Mikheev 已提交
2542 2543
				for (findx = 0; findx < numFuncs; findx++)
				{
2544
					if (strcmp(finfo[findx].oid, tgfuncoid) == 0 &&
2545
						finfo[findx].nargs == 0 &&
V
Vadim B. Mikheev 已提交
2546 2547 2548
						strcmp(finfo[findx].prorettype, "0") == 0)
						break;
				}
2549

V
Vadim B. Mikheev 已提交
2550 2551
				if (findx == numFuncs)
				{
2552
					PGresult   *r;
2553
					int			numFuncs;
2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570

					/*
					 * the funcname is an oid which we use to find the
					 * name of the pg_proc.  We need to do this because
					 * getFuncs() only reads in the user-defined funcs not
					 * all the funcs.  We might not find what we want by
					 * looking in FuncInfo*
					 */
					resetPQExpBuffer(query);
					appendPQExpBuffer(query,
									  "SELECT proname from pg_proc "
									  "where pg_proc.oid = '%s'::oid",
									  tgfuncoid);

					r = PQexec(g_conn, query->data);
					if (!r || PQresultStatus(r) != PGRES_TUPLES_OK)
					{
2571 2572
						write_msg(NULL, "query to obtain procedure name for trigger \"%s\" failed: %s",
								  tgname, PQerrorMessage(g_conn));
2573
						exit_nicely();
2574 2575 2576 2577
					}

					/* Sanity: Check we got only one tuple */
					numFuncs = PQntuples(r);
B
Bruce Momjian 已提交
2578 2579
					if (numFuncs != 1)
					{
2580 2581 2582 2583 2584 2585
						if (numFuncs == 0)
							write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned no rows\n",
									  tgname, tgfuncoid);
						else
							write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned %d rows\n",
									  tgname, tgfuncoid, numFuncs);
2586
						exit_nicely();
2587
					}
2588

2589
					tgfunc = strdup(PQgetvalue(r, 0, PQfnumber(r, "proname")));
2590
					PQclear(r);
2591
				}
2592
				else
2593
					tgfunc = strdup(finfo[findx].proname);
B
Bruce Momjian 已提交
2594

2595
				appendPQExpBuffer(delqry, "DROP TRIGGER %s ", fmtId(tgname, force_quotes));
B
Bruce Momjian 已提交
2596
				appendPQExpBuffer(delqry, "ON %s;\n",
B
Bruce Momjian 已提交
2597
								fmtId(tblinfo[i].relname, force_quotes));
2598

B
Hi, all  
Bruce Momjian 已提交
2599
				resetPQExpBuffer(query);
2600 2601 2602 2603 2604
				if (tgisconstraint)
				{
					appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
					appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgconstrname), force_quotes));
				}
2605 2606
				else
				{
2607
					appendPQExpBuffer(query, "CREATE TRIGGER ");
2608
					appendPQExpBuffer(query, fmtId(tgname, force_quotes));
2609
				}
B
Bruce Momjian 已提交
2610
				appendPQExpBufferChar(query, ' ');
V
Vadim B. Mikheev 已提交
2611 2612 2613
				/* Trigger type */
				findx = 0;
				if (TRIGGER_FOR_BEFORE(tgtype))
B
Hi, all  
Bruce Momjian 已提交
2614
					appendPQExpBuffer(query, "BEFORE");
V
Vadim B. Mikheev 已提交
2615
				else
B
Hi, all  
Bruce Momjian 已提交
2616
					appendPQExpBuffer(query, "AFTER");
V
Vadim B. Mikheev 已提交
2617 2618
				if (TRIGGER_FOR_INSERT(tgtype))
				{
B
Hi, all  
Bruce Momjian 已提交
2619
					appendPQExpBuffer(query, " INSERT");
V
Vadim B. Mikheev 已提交
2620 2621 2622 2623 2624
					findx++;
				}
				if (TRIGGER_FOR_DELETE(tgtype))
				{
					if (findx > 0)
B
Hi, all  
Bruce Momjian 已提交
2625
						appendPQExpBuffer(query, " OR DELETE");
V
Vadim B. Mikheev 已提交
2626
					else
B
Hi, all  
Bruce Momjian 已提交
2627
						appendPQExpBuffer(query, " DELETE");
V
Vadim B. Mikheev 已提交
2628 2629 2630
					findx++;
				}
				if (TRIGGER_FOR_UPDATE(tgtype))
2631
				{
V
Vadim B. Mikheev 已提交
2632
					if (findx > 0)
B
Hi, all  
Bruce Momjian 已提交
2633
						appendPQExpBuffer(query, " OR UPDATE");
V
Vadim B. Mikheev 已提交
2634
					else
B
Hi, all  
Bruce Momjian 已提交
2635
						appendPQExpBuffer(query, " UPDATE");
2636
				}
2637 2638
				appendPQExpBuffer(query, " ON %s ", fmtId(tblinfo[i].relname, force_quotes));

2639
				if (tgisconstraint)
2640
				{
2641 2642
					tgconstrrelid = PQgetvalue(res2, i2, i_tgconstrrelid);

2643 2644
					if (strcmp(tgconstrrelid, "0") != 0)
					{
2645 2646

						if (PQgetisnull(res2, i2, i_tgconstrrelname))
2647
						{
2648
							write_msg(NULL, "query produced NULL referenced table name for foreign key trigger \"%s\" on table \"%s\" (oid of table: %s)\n",
2649
							  tgname, tblinfo[i].relname, tgconstrrelid);
2650
							exit_nicely();
2651
						}
2652

2653
						appendPQExpBuffer(query, " FROM %s",
2654
										  fmtId(PQgetvalue(res2, i2, i_tgconstrrelname), force_quotes));
2655
					}
2656
					if (!tgdeferrable)
2657 2658 2659 2660
						appendPQExpBuffer(query, " NOT");
					appendPQExpBuffer(query, " DEFERRABLE INITIALLY ");
					if (tginitdeferred)
						appendPQExpBuffer(query, "DEFERRED");
2661
					else
2662
						appendPQExpBuffer(query, "IMMEDIATE");
2663

2664 2665 2666
				}

				appendPQExpBuffer(query, " FOR EACH ROW");
2667 2668
				appendPQExpBuffer(query, " EXECUTE PROCEDURE %s (",
								  fmtId(tgfunc, force_quotes));
V
Vadim B. Mikheev 已提交
2669 2670
				for (findx = 0; findx < tgnargs; findx++)
				{
2671
					const char *s;
2672 2673

					for (p = tgargs;;)
V
Vadim B. Mikheev 已提交
2674
					{
2675
						p = strchr(p, '\\');
V
Vadim B. Mikheev 已提交
2676 2677
						if (p == NULL)
						{
2678 2679 2680 2681
							write_msg(NULL, "bad argument string (%s) for trigger \"%s\" on table \"%s\"\n",
									  PQgetvalue(res2, i2, i_tgargs),
									  tgname,
									  tblinfo[i].relname);
2682
							exit_nicely();
V
Vadim B. Mikheev 已提交
2683 2684 2685 2686 2687 2688 2689
						}
						p++;
						if (*p == '\\')
						{
							p++;
							continue;
						}
2690
						if (p[0] == '0' && p[1] == '0' && p[2] == '0')
V
Vadim B. Mikheev 已提交
2691 2692 2693
							break;
					}
					p--;
2694
					appendPQExpBufferChar(query, '\'');
B
Bruce Momjian 已提交
2695
					for (s = tgargs; s < p;)
V
Vadim B. Mikheev 已提交
2696 2697
					{
						if (*s == '\'')
2698 2699
							appendPQExpBufferChar(query, '\\');
						appendPQExpBufferChar(query, *s++);
V
Vadim B. Mikheev 已提交
2700
					}
B
Bruce Momjian 已提交
2701 2702
					appendPQExpBufferChar(query, '\'');
					appendPQExpBuffer(query, (findx < tgnargs - 1) ? ", " : "");
V
Vadim B. Mikheev 已提交
2703 2704
					tgargs = p + 4;
				}
B
Hi, all  
Bruce Momjian 已提交
2705
				appendPQExpBuffer(query, ");\n");
2706

B
Bruce Momjian 已提交
2707
				tblinfo[i].triggers[i2].tgsrc = strdup(query->data);
B
Bruce,  
Bruce Momjian 已提交
2708 2709

				/*** Initialize trcomments and troids ***/
2710

B
Bruce,  
Bruce Momjian 已提交
2711
				resetPQExpBuffer(query);
2712
				appendPQExpBuffer(query, "TRIGGER %s ",
2713
								  fmtId(tgname, force_quotes));
B
Bruce,  
Bruce Momjian 已提交
2714
				appendPQExpBuffer(query, "ON %s",
2715
								fmtId(tblinfo[i].relname, force_quotes));
B
Bruce Momjian 已提交
2716 2717
				tblinfo[i].triggers[i2].tgcomment = strdup(query->data);
				tblinfo[i].triggers[i2].oid = strdup(PQgetvalue(res2, i2, i_tgoid));
2718
				tblinfo[i].triggers[i2].tgname = strdup(fmtId(tgname, false));
B
Bruce Momjian 已提交
2719
				tblinfo[i].triggers[i2].tgdel = strdup(delqry->data);
B
Bruce,  
Bruce Momjian 已提交
2720

2721 2722
				if (tgfunc)
					free(tgfunc);
V
Vadim B. Mikheev 已提交
2723 2724 2725 2726 2727
			}
			PQclear(res2);
		}
		else
			tblinfo[i].triggers = NULL;
2728

2729 2730
	}

2731 2732
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
2733
	destroyPQExpBuffer(lockquery);
2734

2735
	return tblinfo;
2736 2737 2738 2739
}

/*
 * getInherits
2740
 *	  read all the inheritance information
2741 2742
 * from the system catalogs return them in the InhInfo* structure
 *
2743
 * numInherits is set to the number of tables read in
2744
 */
2745
InhInfo *
2746 2747
getInherits(int *numInherits)
{
2748
	PGresult   *res;
2749 2750
	int			ntups;
	int			i;
2751 2752
	PQExpBuffer query = createPQExpBuffer();
	InhInfo    *inhinfo;
2753

2754
	int			i_inhrelid;
2755
	int			i_inhparent;
2756 2757 2758

	/* find all the inheritance information */

B
Hi, all  
Bruce Momjian 已提交
2759
	appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
2760

B
Hi, all  
Bruce Momjian 已提交
2761
	res = PQexec(g_conn, query->data);
2762 2763 2764
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
2765 2766
		write_msg(NULL, "query to obtain inheritance relationships failed: %s",
				  PQerrorMessage(g_conn));
2767
		exit_nicely();
2768 2769 2770 2771 2772 2773 2774 2775
	}

	ntups = PQntuples(res);

	*numInherits = ntups;

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

2776
	i_inhrelid = PQfnumber(res, "inhrelid");
2777 2778 2779 2780
	i_inhparent = PQfnumber(res, "inhparent");

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

	PQclear(res);
2786 2787 2788

	destroyPQExpBuffer(query);

2789
	return inhinfo;
2790 2791 2792 2793
}

/*
 * getTableAttrs -
2794 2795
 *	  for each table in tblinfo, read its attributes types and names
 *
2796
 * this is implemented in a very inefficient way right now, looping
2797
 * through the tblinfo and doing a join per table to find the attrs and their
2798 2799
 * types
 *
2800
 *	modifies tblinfo
2801 2802
 */
void
2803
getTableAttrs(TableInfo *tblinfo, int numTables)
2804
{
2805 2806
	int			i,
				j;
2807
	PQExpBuffer q = createPQExpBuffer();
2808 2809
	int			i_attname;
	int			i_typname;
2810
	int			i_atttypmod;
2811
	int			i_attnotnull;
V
Vadim B. Mikheev 已提交
2812
	int			i_atthasdef;
2813
	int			i_atttypedefn;
2814
	PGresult   *res;
2815
	int			ntups;
2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829

	for (i = 0; i < numTables; i++)
	{
		if (tblinfo[i].sequence)
			continue;

		/* find all the user attributes and their types */
		/* we must read the attribute names in attribute number order! */

		/*
		 * because we will use the attnum to index into the attnames array
		 * later
		 */
		if (g_verbose)
2830 2831
			write_msg(NULL, "finding the columns and types for table %s\n",
					  tblinfo[i].relname);
2832

B
Hi, all  
Bruce Momjian 已提交
2833
		resetPQExpBuffer(q);
2834 2835 2836 2837

		if (g_fout->remoteVersion < 70100)
		{
			/* Fake the LOJ below */
2838
			appendPQExpBuffer(q,
2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856
				 "  SELECT a.attnum, a.attname, t.typname, a.atttypmod, "
				"        a.attnotnull, a.atthasdef, NULL as atttypedefn "
							  "    from pg_attribute a, pg_type t "
							  "    where a.attrelid = '%s'::oid "
							  "        and a.attnum > 0 "
							  "        and a.atttypid = t.oid "
							  " UNION ALL SELECT a.attnum, a.attname, NULL as typname, a.atttypmod, "
				"        a.attnotnull, a.atthasdef, NULL as atttypedefn "
							  "    from pg_attribute a "
							  "    where a.attrelid = '%s'::oid "
							  "        and a.attnum > 0 "
							  "        and Not Exists(Select * From pg_type t where a.atttypid = t.oid)"
							  "    order by attnum",
							  tblinfo[i].oid, tblinfo[i].oid);

		}
		else
		{
2857
			appendPQExpBuffer(q, "SELECT a.attnum, a.attname, t.typname, a.atttypmod, "
2858 2859 2860 2861 2862
							  "a.attnotnull, a.atthasdef, format_type(a.atttypid, a.atttypmod) as atttypedefn "
							  "from pg_attribute a LEFT OUTER JOIN pg_type t ON a.atttypid = t.oid "
							  "where a.attrelid = '%s'::oid "
							  "and a.attnum > 0 order by attnum",
							  tblinfo[i].oid);
2863 2864
		}

B
Hi, all  
Bruce Momjian 已提交
2865
		res = PQexec(g_conn, q->data);
2866 2867 2868
		if (!res ||
			PQresultStatus(res) != PGRES_TUPLES_OK)
		{
2869
			write_msg(NULL, "query to get table columns failed: %s", PQerrorMessage(g_conn));
2870
			exit_nicely();
2871 2872 2873 2874 2875 2876
		}

		ntups = PQntuples(res);

		i_attname = PQfnumber(res, "attname");
		i_typname = PQfnumber(res, "typname");
2877
		i_atttypmod = PQfnumber(res, "atttypmod");
2878
		i_attnotnull = PQfnumber(res, "attnotnull");
V
Vadim B. Mikheev 已提交
2879
		i_atthasdef = PQfnumber(res, "atthasdef");
2880
		i_atttypedefn = PQfnumber(res, "atttypedefn");
2881 2882 2883

		tblinfo[i].numatts = ntups;
		tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *));
2884
		tblinfo[i].atttypedefns = (char **) malloc(ntups * sizeof(char *));
2885
		tblinfo[i].typnames = (char **) malloc(ntups * sizeof(char *));
2886
		tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int));
2887
		tblinfo[i].inhAttrs = (int *) malloc(ntups * sizeof(int));
2888 2889
		tblinfo[i].inhAttrDef = (int *) malloc(ntups * sizeof(int));
		tblinfo[i].inhNotNull = (int *) malloc(ntups * sizeof(int));
2890
		tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool));
V
Vadim B. Mikheev 已提交
2891
		tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *));
2892 2893 2894 2895
		tblinfo[i].parentRels = NULL;
		tblinfo[i].numParents = 0;
		for (j = 0; j < ntups; j++)
		{
2896 2897 2898
			/* Sanity check on LOJ */
			if (PQgetisnull(res, j, i_typname))
			{
2899
				write_msg(NULL, "query produced NULL name for data type of column %d of table %s\n",
2900
						  j + 1, tblinfo[i].relname);
2901
				exit_nicely();
2902 2903
			}

2904
			tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname));
2905
			tblinfo[i].atttypedefns[j] = strdup(PQgetvalue(res, j, i_atttypedefn));
2906
			tblinfo[i].typnames[j] = strdup(PQgetvalue(res, j, i_typname));
2907
			tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
2908 2909
			tblinfo[i].inhAttrs[j] = 0; /* this flag is set in
										 * flagInhAttrs() */
2910 2911 2912
			tblinfo[i].inhAttrDef[j] = 0;
			tblinfo[i].inhNotNull[j] = 0;

2913
			tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't') ? true : false;
V
Vadim B. Mikheev 已提交
2914 2915 2916
			if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
			{
				PGresult   *res2;
2917
				int			numAttr;
2918

V
Vadim B. Mikheev 已提交
2919
				if (g_verbose)
2920 2921
					write_msg(NULL, "finding DEFAULT expression for column %s\n",
							  tblinfo[i].attnames[j]);
2922

B
Hi, all  
Bruce Momjian 已提交
2923 2924
				resetPQExpBuffer(q);
				appendPQExpBuffer(q, "SELECT adsrc from pg_attrdef "
2925 2926
							 "where adrelid = '%s'::oid and adnum = %d ",
								  tblinfo[i].oid, j + 1);
B
Hi, all  
Bruce Momjian 已提交
2927
				res2 = PQexec(g_conn, q->data);
V
Vadim B. Mikheev 已提交
2928
				if (!res2 ||
2929
					PQresultStatus(res2) != PGRES_TUPLES_OK)
V
Vadim B. Mikheev 已提交
2930
				{
2931 2932
					write_msg(NULL, "query to get column default value failed: %s",
							  PQerrorMessage(g_conn));
2933
					exit_nicely();
V
Vadim B. Mikheev 已提交
2934
				}
2935 2936 2937

				/* Sanity: Check we got only one tuple */
				numAttr = PQntuples(res2);
B
Bruce Momjian 已提交
2938 2939
				if (numAttr != 1)
				{
2940 2941
					write_msg(NULL, "query to get default value for column \"%s\" returned %d rows; expected 1\n",
							  tblinfo[i].attnames[j], numAttr);
2942
					exit_nicely();
2943 2944
				}

V
Vadim B. Mikheev 已提交
2945 2946 2947 2948 2949
				tblinfo[i].adef_expr[j] = strdup(PQgetvalue(res2, 0, PQfnumber(res2, "adsrc")));
				PQclear(res2);
			}
			else
				tblinfo[i].adef_expr[j] = NULL;
2950 2951 2952
		}
		PQclear(res);
	}
2953 2954

	destroyPQExpBuffer(q);
2955 2956 2957 2958
}


/*
2959 2960
 * getIndexes
 *	  read all the user-defined indexes information
2961 2962
 * from the system catalogs return them in the InhInfo* structure
 *
2963
 * numIndexes is set to the number of indexes read in
2964
 *
2965 2966
 *
 */
2967
IndInfo *
2968
getIndexes(int *numIndexes)
2969
{
2970
	int			i;
2971
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
2972
	PGresult   *res;
2973
	int			ntups;
B
Bruce Momjian 已提交
2974
	IndInfo    *indinfo;
2975

2976 2977
	int			i_indexreloid;
	int			i_indreloid;
2978 2979
	int			i_indexrelname;
	int			i_indrelname;
2980
	int			i_indexdef;
2981
	int			i_indisprimary;
2982
	int			i_indkey;
2983 2984

	/*
2985
	 * find all the user-defined indexes.
2986
	 *
2987
	 * Notice we skip indexes on system classes
2988
	 *
2989
	 * XXXX: Use LOJ
2990 2991
	 */

B
Hi, all  
Bruce Momjian 已提交
2992
	appendPQExpBuffer(query,
2993 2994
					  "SELECT i.indexrelid as indexreloid, "
					  "i.indrelid as indreloid, "
2995
				 "t1.relname as indexrelname, t2.relname as indrelname, "
2996 2997 2998
					  "pg_get_indexdef(i.indexrelid) as indexdef, "
					  "i.indisprimary, i.indkey "
					  "from pg_index i, pg_class t1, pg_class t2 "
2999
				   "WHERE t1.oid = i.indexrelid and t2.oid = i.indrelid "
3000
					  "and i.indexrelid > '%u'::oid "
3001
					  "and t2.relname !~ '^pg_' ",
3002
					  g_last_builtin_oid);
3003

3004 3005 3006
	if (g_fout->remoteVersion < 70100)
		appendPQExpBuffer(query, " and t2.relkind != 'l'");

B
Hi, all  
Bruce Momjian 已提交
3007
	res = PQexec(g_conn, query->data);
3008 3009 3010
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
3011
		write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn));
3012
		exit_nicely();
3013 3014 3015 3016
	}

	ntups = PQntuples(res);

3017
	*numIndexes = ntups;
3018 3019 3020

	indinfo = (IndInfo *) malloc(ntups * sizeof(IndInfo));

3021 3022
	memset((char *) indinfo, 0, ntups * sizeof(IndInfo));

3023 3024
	i_indexreloid = PQfnumber(res, "indexreloid");
	i_indreloid = PQfnumber(res, "indreloid");
3025 3026
	i_indexrelname = PQfnumber(res, "indexrelname");
	i_indrelname = PQfnumber(res, "indrelname");
3027
	i_indexdef = PQfnumber(res, "indexdef");
3028
	i_indisprimary = PQfnumber(res, "indisprimary");
3029
	i_indkey = PQfnumber(res, "indkey");
3030 3031 3032

	for (i = 0; i < ntups; i++)
	{
3033 3034
		indinfo[i].indexreloid = strdup(PQgetvalue(res, i, i_indexreloid));
		indinfo[i].indreloid = strdup(PQgetvalue(res, i, i_indreloid));
3035 3036
		indinfo[i].indexrelname = strdup(PQgetvalue(res, i, i_indexrelname));
		indinfo[i].indrelname = strdup(PQgetvalue(res, i, i_indrelname));
3037 3038
		indinfo[i].indexdef = strdup(PQgetvalue(res, i, i_indexdef));
		indinfo[i].indisprimary = strdup(PQgetvalue(res, i, i_indisprimary));
3039 3040 3041
		parseNumericArray(PQgetvalue(res, i, i_indkey),
						  indinfo[i].indkey,
						  INDEX_MAX_KEYS);
3042 3043
	}
	PQclear(res);
3044 3045 3046

	destroyPQExpBuffer(query);

3047
	return indinfo;
3048 3049
}

B
Bruce,  
Bruce Momjian 已提交
3050
/*------------------------------------------------------------------
3051
 * dumpComment --
B
Bruce,  
Bruce Momjian 已提交
3052
 *
3053
 * This routine is used to dump any comments associated with the
B
Bruce,  
Bruce Momjian 已提交
3054
 * oid handed to this routine. The routine takes a constant character
3055 3056 3057 3058 3059 3060
 * string for the target part of the comment-creation command, plus
 * OID, class name, and subid which are the primary key for pg_description.
 * If a matching pg_description entry is found, it is dumped.
 * Additional dependencies can be passed for the comment, too --- this is
 * needed for VIEWs, whose comments are filed under the table OID but
 * which are dumped in order by their rule OID.
B
Bruce,  
Bruce Momjian 已提交
3061 3062 3063
 *------------------------------------------------------------------
*/

T
Tom Lane 已提交
3064
static void
3065
dumpComment(Archive *fout, const char *target, const char *oid,
3066
			const char *classname, int subid,
3067
			const char *((*deps)[]))
3068 3069
{
	PGresult   *res;
B
Bruce,  
Bruce Momjian 已提交
3070
	PQExpBuffer query;
3071
	int			i_description;
B
Bruce,  
Bruce Momjian 已提交
3072

3073 3074 3075 3076
	/* Comments are SCHEMA not data */
	if (dataOnly)
		return;

B
Bruce,  
Bruce Momjian 已提交
3077 3078 3079
	/*** Build query to find comment ***/

	query = createPQExpBuffer();
3080 3081 3082 3083

	if (fout->remoteVersion >= 70200)
	{
		appendPQExpBuffer(query, "SELECT description FROM pg_description "
3084
						  "WHERE objoid = '%s'::oid and classoid = "
3085
					   "(SELECT oid FROM pg_class where relname = '%s') "
3086 3087 3088 3089 3090 3091
						  "and objsubid = %d",
						  oid, classname, subid);
	}
	else
	{
		/* Note: this will fail to find attribute comments in pre-7.2... */
3092
		appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%s'::oid", oid);
3093
	}
B
Bruce,  
Bruce Momjian 已提交
3094 3095 3096 3097

	/*** Execute query ***/

	res = PQexec(g_conn, query->data);
3098 3099
	if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
	{
3100
		write_msg(NULL, "query to get comment on oid %s failed: %s",
3101
				  oid, PQerrorMessage(g_conn));
3102
		exit_nicely();
B
Bruce,  
Bruce Momjian 已提交
3103 3104 3105 3106
	}

	/*** If a comment exists, build COMMENT ON statement ***/

3107
	if (PQntuples(res) == 1)
3108
	{
B
Bruce,  
Bruce Momjian 已提交
3109
		i_description = PQfnumber(res, "description");
B
Bruce Momjian 已提交
3110
		resetPQExpBuffer(query);
3111
		appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
3112 3113
		formatStringLiteral(query, PQgetvalue(res, 0, i_description),
							PASS_LFTAB);
3114
		appendPQExpBuffer(query, ";\n");
B
Bruce Momjian 已提交
3115

3116 3117
		ArchiveEntry(fout, oid, target, "COMMENT", deps,
					 query->data, "" /* Del */ ,
B
Bruce Momjian 已提交
3118
					 "" /* Copy */ , "" /* Owner */ , NULL, NULL);
B
Bruce,  
Bruce Momjian 已提交
3119 3120 3121 3122 3123
	}

	/*** Clear the statement buffer and return ***/

	PQclear(res);
3124
	destroyPQExpBuffer(query);
B
Bruce,  
Bruce Momjian 已提交
3125 3126 3127
}

/*------------------------------------------------------------------
3128
 * dumpDBComment --
B
Bruce,  
Bruce Momjian 已提交
3129
 *
3130 3131
 * This routine is used to dump any comments associated with the
 * database to which we are currently connected. If the user chose
B
Bruce,  
Bruce Momjian 已提交
3132 3133 3134 3135 3136
 * to dump the schema of the database, then this is the first
 * statement issued.
 *------------------------------------------------------------------
*/

3137
void
B
Bruce Momjian 已提交
3138
dumpDBComment(Archive *fout)
3139 3140
{
	PGresult   *res;
B
Bruce,  
Bruce Momjian 已提交
3141
	PQExpBuffer query;
3142
	int			i_oid;
B
Bruce,  
Bruce Momjian 已提交
3143 3144 3145 3146

	/*** Build query to find comment ***/

	query = createPQExpBuffer();
3147
	appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
3148
	formatStringLiteral(query, PQdb(g_conn), CONV_ALL);
B
Bruce,  
Bruce Momjian 已提交
3149 3150 3151 3152

	/*** Execute query ***/

	res = PQexec(g_conn, query->data);
3153 3154
	if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
	{
3155
		write_msg(NULL, "query to get database oid failed: %s",
3156
				  PQerrorMessage(g_conn));
3157
		exit_nicely();
B
Bruce,  
Bruce Momjian 已提交
3158 3159 3160 3161
	}

	/*** If a comment exists, build COMMENT ON statement ***/

3162 3163
	if (PQntuples(res) != 0)
	{
B
Bruce,  
Bruce Momjian 已提交
3164 3165 3166
		i_oid = PQfnumber(res, "oid");
		resetPQExpBuffer(query);
		appendPQExpBuffer(query, "DATABASE %s", fmtId(PQdb(g_conn), force_quotes));
3167 3168
		dumpComment(fout, query->data, PQgetvalue(res, 0, i_oid),
					"pg_database", 0, NULL);
B
Bruce,  
Bruce Momjian 已提交
3169 3170 3171 3172 3173
	}

	/*** Clear the statement buffer and return ***/

	PQclear(res);
3174
	destroyPQExpBuffer(query);
B
Bruce,  
Bruce Momjian 已提交
3175 3176
}

3177 3178
/*
 * dumpTypes
3179
 *	  writes out to fout the queries to recreate all the user-defined types
3180 3181 3182
 *
 */
void
B
Bruce Momjian 已提交
3183
dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
3184
		  TypeInfo *tinfo, int numTypes)
3185
{
3186
	int			i;
3187
	PQExpBuffer q = createPQExpBuffer();
B
Bruce Momjian 已提交
3188
	PQExpBuffer delq = createPQExpBuffer();
3189
	int			funcInd;
3190
	const char *((*deps)[]);
3191
	int			depIdx;
3192 3193 3194 3195

	for (i = 0; i < numTypes; i++)
	{
		/* skip all the builtin types */
3196
		if (atooid(tinfo[i].oid) <= g_last_builtin_oid)
3197 3198 3199
			continue;

		/* skip relation types */
3200
		if (atooid(tinfo[i].typrelid) != 0)
3201 3202
			continue;

3203 3204 3205 3206
		/* skip undefined placeholder types */
		if (!tinfo[i].isDefined)
			continue;

3207 3208 3209 3210 3211
		/* skip all array types that start w/ underscore */
		if ((tinfo[i].typname[0] == '_') &&
			(strcmp(tinfo[i].typinput, "array_in") == 0))
			continue;

3212
		deps = malloc(sizeof(char *) * 10);
3213 3214
		depIdx = 0;

3215 3216 3217 3218 3219 3220
		/*
		 * before we create a type, we need to create the input and output
		 * functions for it, if they haven't been created already
		 */
		funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput);
		if (funcInd != -1)
3221 3222
		{
			(*deps)[depIdx++] = strdup(finfo[funcInd].oid);
3223
			dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes);
3224
		}
3225 3226 3227

		funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput);
		if (funcInd != -1)
3228 3229
		{
			(*deps)[depIdx++] = strdup(finfo[funcInd].oid);
3230
			dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes);
3231
		}
3232

3233
		resetPQExpBuffer(delq);
B
Bruce Momjian 已提交
3234
		appendPQExpBuffer(delq, "DROP TYPE %s;\n", fmtId(tinfo[i].typname, force_quotes));
3235

B
Hi, all  
Bruce Momjian 已提交
3236 3237
		resetPQExpBuffer(q);
		appendPQExpBuffer(q,
3238
						  "CREATE TYPE %s "
3239
						  "( internallength = %s, externallength = %s,",
3240
						  fmtId(tinfo[i].typname, force_quotes),
3241 3242 3243 3244
						  (strcmp(tinfo[i].typlen, "-1") == 0) ?
						  "variable" : tinfo[i].typlen,
						  (strcmp(tinfo[i].typprtlen, "-1") == 0) ?
						  "variable" : tinfo[i].typprtlen);
3245 3246 3247 3248 3249 3250 3251
		/* cannot combine these because fmtId uses static result area */
		appendPQExpBuffer(q, " input = %s,",
						  fmtId(tinfo[i].typinput, force_quotes));
		appendPQExpBuffer(q, " output = %s,",
						  fmtId(tinfo[i].typoutput, force_quotes));
		appendPQExpBuffer(q, " send = %s,",
						  fmtId(tinfo[i].typsend, force_quotes));
3252
		appendPQExpBuffer(q, " receive = %s",
3253
						  fmtId(tinfo[i].typreceive, force_quotes));
3254 3255 3256 3257 3258 3259

		if (tinfo[i].typdefault != NULL)
		{
			appendPQExpBuffer(q, ", default = ");
			formatStringLiteral(q, tinfo[i].typdefault, CONV_ALL);
		}
3260 3261 3262

		if (tinfo[i].isArray)
		{
3263
			char	   *elemType;
3264

3265
			elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque);
3266 3267
			if (elemType == NULL)
			{
3268
				write_msg(NULL, "notice: array type %s - type for elements (oid %s) is not dumped\n",
3269
						  tinfo[i].typname, tinfo[i].typelem);
3270 3271 3272
				continue;
			}

3273
			appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType);
3274
			formatStringLiteral(q, tinfo[i].typdelim, CONV_ALL);
3275 3276

			(*deps)[depIdx++] = strdup(tinfo[i].typelem);
3277
		}
3278

3279 3280 3281 3282 3283
		if (strcmp(tinfo[i].typalign, "c") == 0)
			appendPQExpBuffer(q, ", alignment = char");
		else if (strcmp(tinfo[i].typalign, "s") == 0)
			appendPQExpBuffer(q, ", alignment = int2");
		else if (strcmp(tinfo[i].typalign, "i") == 0)
3284 3285 3286 3287 3288 3289
			appendPQExpBuffer(q, ", alignment = int4");
		else if (strcmp(tinfo[i].typalign, "d") == 0)
			appendPQExpBuffer(q, ", alignment = double");

		if (strcmp(tinfo[i].typstorage, "p") == 0)
			appendPQExpBuffer(q, ", storage = plain");
3290
		else if (strcmp(tinfo[i].typstorage, "e") == 0)
3291
			appendPQExpBuffer(q, ", storage = external");
3292
		else if (strcmp(tinfo[i].typstorage, "x") == 0)
3293
			appendPQExpBuffer(q, ", storage = extended");
3294
		else if (strcmp(tinfo[i].typstorage, "m") == 0)
3295 3296
			appendPQExpBuffer(q, ", storage = main");

3297
		if (tinfo[i].passedbyvalue)
3298
			appendPQExpBuffer(q, ", passedbyvalue);\n");
3299
		else
B
Hi, all  
Bruce Momjian 已提交
3300
			appendPQExpBuffer(q, ");\n");
3301

3302
		(*deps)[depIdx++] = NULL;		/* End of List */
3303 3304

		ArchiveEntry(fout, tinfo[i].oid, tinfo[i].typname, "TYPE", deps,
B
Bruce Momjian 已提交
3305
				  q->data, delq->data, "", tinfo[i].usename, NULL, NULL);
B
Bruce,  
Bruce Momjian 已提交
3306 3307 3308 3309

		/*** Dump Type Comments ***/

		resetPQExpBuffer(q);
B
Bruce Momjian 已提交
3310

B
Bruce,  
Bruce Momjian 已提交
3311
		appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo[i].typname, force_quotes));
3312
		dumpComment(fout, q->data, tinfo[i].oid, "pg_type", 0, NULL);
3313
	}
3314 3315 3316

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
3317 3318
}

3319 3320
/*
 * dumpProcLangs
B
Bruce Momjian 已提交
3321
 *		  writes out to fout the queries to recreate user-defined procedural languages
3322 3323 3324
 *
 */
void
B
Bruce Momjian 已提交
3325
dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
B
Bruce Momjian 已提交
3326
			  TypeInfo *tinfo, int numTypes)
3327
{
3328 3329
	PGresult   *res;
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
3330 3331
	PQExpBuffer defqry = createPQExpBuffer();
	PQExpBuffer delqry = createPQExpBuffer();
3332
	int			ntups;
B
Bruce Momjian 已提交
3333
	int			i_oid;
3334 3335 3336 3337
	int			i_lanname;
	int			i_lanpltrusted;
	int			i_lanplcallfoid;
	int			i_lancompiler;
3338
	Oid			lanoid;
3339 3340 3341
	char	   *lanname;
	char	   *lancompiler;
	const char *lanplcallfoid;
3342 3343 3344
	int			i,
				fidx;

B
Bruce Momjian 已提交
3345
	appendPQExpBuffer(query, "SELECT oid, * FROM pg_language "
3346 3347
					  "WHERE lanispl "
					  "ORDER BY oid");
B
Hi, all  
Bruce Momjian 已提交
3348
	res = PQexec(g_conn, query->data);
3349 3350 3351
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
3352
		write_msg(NULL, "query to obtain list of procedural languages failed: %s",
3353
				  PQerrorMessage(g_conn));
3354
		exit_nicely();
3355 3356 3357
	}
	ntups = PQntuples(res);

B
Bruce Momjian 已提交
3358 3359 3360 3361
	i_lanname = PQfnumber(res, "lanname");
	i_lanpltrusted = PQfnumber(res, "lanpltrusted");
	i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
	i_lancompiler = PQfnumber(res, "lancompiler");
B
Bruce Momjian 已提交
3362
	i_oid = PQfnumber(res, "oid");
3363

B
Bruce Momjian 已提交
3364 3365
	for (i = 0; i < ntups; i++)
	{
3366
		lanoid = atooid(PQgetvalue(res, i, i_oid));
3367 3368 3369
		if (lanoid <= g_last_builtin_oid)
			continue;

3370
		lanplcallfoid = PQgetvalue(res, i, i_lanplcallfoid);
3371 3372


3373 3374 3375 3376 3377 3378 3379
		for (fidx = 0; fidx < numFuncs; fidx++)
		{
			if (!strcmp(finfo[fidx].oid, lanplcallfoid))
				break;
		}
		if (fidx >= numFuncs)
		{
3380
			write_msg(NULL, "handler procedure for procedural language %s not found\n",
3381
					  PQgetvalue(res, i, i_lanname));
3382
			exit_nicely();
3383 3384 3385 3386
		}

		dumpOneFunc(fout, finfo, fidx, tinfo, numTypes);

3387 3388
		lanname = PQgetvalue(res, i, i_lanname);
		lancompiler = PQgetvalue(res, i, i_lancompiler);
3389

3390
		appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE ");
3391
		formatStringLiteral(delqry, lanname, CONV_ALL);
3392
		appendPQExpBuffer(delqry, ";\n");
3393

3394 3395 3396
		appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE ",
						  (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
						  "TRUSTED " : "");
3397
		formatStringLiteral(defqry, lanname, CONV_ALL);
3398 3399
		appendPQExpBuffer(defqry, " HANDLER %s LANCOMPILER ",
						  fmtId(finfo[fidx].proname, force_quotes));
3400
		formatStringLiteral(defqry, lancompiler, CONV_ALL);
3401
		appendPQExpBuffer(defqry, ";\n");
3402

B
Bruce Momjian 已提交
3403
		ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
B
Bruce Momjian 已提交
3404
				   NULL, defqry->data, delqry->data, "", "", NULL, NULL);
B
Bruce Momjian 已提交
3405

3406 3407
		resetPQExpBuffer(defqry);
		resetPQExpBuffer(delqry);
3408 3409 3410 3411
	}

	PQclear(res);

3412 3413 3414
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(defqry);
	destroyPQExpBuffer(delqry);
3415 3416
}

3417 3418
/*
 * dumpFuncs
3419
 *	  writes out to fout the queries to recreate all the user-defined functions
3420 3421 3422
 *
 */
void
B
Bruce Momjian 已提交
3423
dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs,
3424
		  TypeInfo *tinfo, int numTypes)
3425
{
3426
	int			i;
3427 3428 3429

	for (i = 0; i < numFuncs; i++)
		dumpOneFunc(fout, finfo, i, tinfo, numTypes);
3430 3431 3432 3433
}

/*
 * dumpOneFunc:
3434 3435
 *	  dump out only one function,  the index of which is given in the third
 *	argument
3436 3437 3438
 *
 */

3439
static void
B
Bruce Momjian 已提交
3440
dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
3441
			TypeInfo *tinfo, int numTypes)
3442
{
3443
	PQExpBuffer q = createPQExpBuffer();
B
Bruce Momjian 已提交
3444 3445
	PQExpBuffer fn = createPQExpBuffer();
	PQExpBuffer delqry = createPQExpBuffer();
B
Bruce,  
Bruce Momjian 已提交
3446
	PQExpBuffer fnlist = createPQExpBuffer();
3447
	PQExpBuffer asPart = createPQExpBuffer();
3448
	char		func_lang[NAMEDATALEN + 1];
3449 3450
	PGresult   *res;
	int			nlangs;
3451
	int			j;
3452 3453
	int			i_lanname;
	char		query[256];
3454

B
Bruce Momjian 已提交
3455 3456 3457 3458
	char	   *listSep;
	char	   *listSepComma = ",";
	char	   *listSepNone = "";
	char	   *rettypename;
3459

3460
	if (finfo[i].dumped)
3461 3462 3463
		goto done;

	finfo[i].dumped = 1;
3464

B
Bruce Momjian 已提交
3465
	/* becomeUser(fout, finfo[i].usename); */
3466

3467
	sprintf(query, "SELECT lanname FROM pg_language WHERE oid = '%u'::oid",
3468 3469 3470 3471
			finfo[i].lang);
	res = PQexec(g_conn, query);
	if (!res ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
B
Bruce Momjian 已提交
3472
	{
3473
		write_msg(NULL, "query to get name of procedural language failed: %s", PQerrorMessage(g_conn));
3474
		exit_nicely();
B
Bruce Momjian 已提交
3475
	}
3476 3477 3478
	nlangs = PQntuples(res);

	if (nlangs != 1)
B
Bruce Momjian 已提交
3479
	{
3480
		write_msg(NULL, "procedural language for function %s not found\n", finfo[i].proname);
3481
		exit_nicely();
B
Bruce Momjian 已提交
3482 3483
	}

3484
	i_lanname = PQfnumber(res, "lanname");
3485

3486
	/*
B
Bruce Momjian 已提交
3487 3488
	 * See backend/commands/define.c for details of how the 'AS' clause is
	 * used.
3489 3490 3491
	 */
	if (strcmp(finfo[i].probin, "-") != 0)
	{
3492
		appendPQExpBuffer(asPart, "AS ");
3493
		formatStringLiteral(asPart, finfo[i].probin, CONV_ALL);
3494
		if (strcmp(finfo[i].prosrc, "-") != 0)
3495 3496
		{
			appendPQExpBuffer(asPart, ", ");
3497
			formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB);
3498
		}
3499 3500 3501 3502
	}
	else
	{
		if (strcmp(finfo[i].prosrc, "-") != 0)
3503 3504
		{
			appendPQExpBuffer(asPart, "AS ");
3505
			formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB);
3506
		}
3507 3508
	}

3509
	strcpy(func_lang, PQgetvalue(res, 0, i_lanname));
B
Bruce Momjian 已提交
3510

3511
	PQclear(res);
B
Bruce Momjian 已提交
3512

B
Bruce Momjian 已提交
3513 3514
	resetPQExpBuffer(fn);
	appendPQExpBuffer(fn, "%s (", fmtId(finfo[i].proname, force_quotes));
3515 3516
	for (j = 0; j < finfo[i].nargs; j++)
	{
B
Bruce Momjian 已提交
3517
		char	   *typname;
3518

3519
		typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j], zeroAsOpaque);
3520 3521
		if (typname == NULL)
		{
3522 3523
			write_msg(NULL, "WARNING: function \"%s\" not dumped\n",
					  finfo[i].proname);
3524

3525
			write_msg(NULL, "reason: data type name of argument %d (oid %s) not found\n",
3526
					  j, finfo[i].argtypes[j]);
3527
			goto done;
3528 3529
		}

B
Bruce Momjian 已提交
3530 3531 3532
		appendPQExpBuffer(fn, "%s%s",
						  (j > 0) ? "," : "",
						  typname);
B
Bruce,  
Bruce Momjian 已提交
3533
		appendPQExpBuffer(fnlist, "%s%s",
B
Bruce Momjian 已提交
3534 3535
						  (j > 0) ? "," : "",
						  typname);
3536
	}
B
Bruce Momjian 已提交
3537 3538 3539
	appendPQExpBuffer(fn, ")");

	resetPQExpBuffer(delqry);
B
Bruce Momjian 已提交
3540
	appendPQExpBuffer(delqry, "DROP FUNCTION %s;\n", fn->data);
B
Bruce Momjian 已提交
3541

3542 3543 3544 3545
	rettypename = findTypeByOid(tinfo, numTypes, finfo[i].prorettype, zeroAsOpaque);

	if (rettypename == NULL)
	{
3546 3547
		write_msg(NULL, "WARNING: function \"%s\" not dumped\n",
				  finfo[i].proname);
3548

3549 3550
		write_msg(NULL, "reason: name of return data type (oid %s) not found\n",
				  finfo[i].prorettype);
3551
		goto done;
3552 3553
	}

B
Bruce Momjian 已提交
3554
	resetPQExpBuffer(q);
B
Bruce Momjian 已提交
3555
	appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data);
3556 3557
	appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE ",
					  (finfo[i].retset) ? "SETOF " : "",
3558
					  rettypename,
3559
					  asPart->data);
3560
	formatStringLiteral(q, func_lang, CONV_ALL);
3561

B
Bruce Momjian 已提交
3562
	if (finfo[i].iscachable || finfo[i].isstrict)		/* OR in new attrs here */
3563
	{
3564 3565 3566
		appendPQExpBuffer(q, " WITH (");
		listSep = listSepNone;

B
Bruce Momjian 已提交
3567 3568
		if (finfo[i].iscachable)
		{
3569 3570 3571 3572
			appendPQExpBuffer(q, "%s iscachable", listSep);
			listSep = listSepComma;
		}

B
Bruce Momjian 已提交
3573 3574
		if (finfo[i].isstrict)
		{
3575 3576 3577 3578
			appendPQExpBuffer(q, "%s isstrict", listSep);
			listSep = listSepComma;
		}
		appendPQExpBuffer(q, " )");
3579 3580 3581 3582
	}

	appendPQExpBuffer(q, ";\n");

B
Bruce Momjian 已提交
3583
	ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data,
B
Bruce Momjian 已提交
3584
				 "", finfo[i].usename, NULL, NULL);
3585

B
Bruce,  
Bruce Momjian 已提交
3586 3587 3588 3589 3590 3591
	/*** Dump Function Comments ***/

	resetPQExpBuffer(q);
	appendPQExpBuffer(q, "FUNCTION %s ",
					  fmtId(finfo[i].proname, force_quotes));
	appendPQExpBuffer(q, "( %s )", fnlist->data);
3592
	dumpComment(fout, q->data, finfo[i].oid, "pg_proc", 0, NULL);
3593 3594 3595 3596 3597 3598 3599

done:
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(fn);
	destroyPQExpBuffer(delqry);
	destroyPQExpBuffer(fnlist);
	destroyPQExpBuffer(asPart);
3600 3601 3602 3603
}

/*
 * dumpOprs
3604
 *	  writes out to fout the queries to recreate all the user-defined operators
3605 3606
 *
 */
3607
void
B
Bruce Momjian 已提交
3608
dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
3609
		 TypeInfo *tinfo, int numTypes)
3610
{
B
Bruce Momjian 已提交
3611
	int			i;
3612
	PQExpBuffer q = createPQExpBuffer();
B
Bruce Momjian 已提交
3613
	PQExpBuffer delq = createPQExpBuffer();
3614 3615 3616 3617 3618 3619 3620 3621
	PQExpBuffer leftarg = createPQExpBuffer();
	PQExpBuffer rightarg = createPQExpBuffer();
	PQExpBuffer commutator = createPQExpBuffer();
	PQExpBuffer negator = createPQExpBuffer();
	PQExpBuffer restrictor = createPQExpBuffer();
	PQExpBuffer join = createPQExpBuffer();
	PQExpBuffer sort1 = createPQExpBuffer();
	PQExpBuffer sort2 = createPQExpBuffer();
3622 3623 3624

	for (i = 0; i < numOperators; i++)
	{
B
Bruce Momjian 已提交
3625
		char	   *name;
3626

B
Hi, all  
Bruce Momjian 已提交
3627 3628 3629 3630 3631 3632 3633 3634 3635
		resetPQExpBuffer(leftarg);
		resetPQExpBuffer(rightarg);
		resetPQExpBuffer(commutator);
		resetPQExpBuffer(negator);
		resetPQExpBuffer(restrictor);
		resetPQExpBuffer(join);
		resetPQExpBuffer(sort1);
		resetPQExpBuffer(sort2);

3636
		/* skip all the builtin oids */
3637
		if (atooid(oprinfo[i].oid) <= g_last_builtin_oid)
3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653
			continue;

		/*
		 * some operator are invalid because they were the result of user
		 * defining operators before commutators exist
		 */
		if (strcmp(oprinfo[i].oprcode, "-") == 0)
			continue;

		/*
		 * right unary means there's a left arg and left unary means
		 * there's a right arg
		 */
		if (strcmp(oprinfo[i].oprkind, "r") == 0 ||
			strcmp(oprinfo[i].oprkind, "b") == 0)
		{
B
Bruce Momjian 已提交
3654 3655
			name = findTypeByOid(tinfo, numTypes,
								 oprinfo[i].oprleft, zeroAsOpaque);
3656 3657
			if (name == NULL)
			{
3658 3659 3660 3661
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprleft (oid %s) not found\n",
						  oprinfo[i].oprleft);
3662 3663
				continue;
			}
B
Bruce Momjian 已提交
3664
			appendPQExpBuffer(leftarg, ",\n\tLEFTARG = %s ", name);
3665
		}
3666

3667 3668 3669
		if (strcmp(oprinfo[i].oprkind, "l") == 0 ||
			strcmp(oprinfo[i].oprkind, "b") == 0)
		{
B
Bruce Momjian 已提交
3670 3671
			name = findTypeByOid(tinfo, numTypes,
								 oprinfo[i].oprright, zeroAsOpaque);
3672 3673
			if (name == NULL)
			{
3674 3675 3676 3677
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprright (oid %s) not found\n",
						  oprinfo[i].oprright);
3678 3679 3680
				continue;
			}
			appendPQExpBuffer(rightarg, ",\n\tRIGHTARG = %s ", name);
3681
		}
3682

B
Hi, all  
Bruce Momjian 已提交
3683
		if (!(strcmp(oprinfo[i].oprcom, "0") == 0))
3684 3685 3686 3687
		{
			name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom);
			if (name == NULL)
			{
3688 3689 3690 3691
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprcom (oid %s) not found\n",
						  oprinfo[i].oprcom);
3692 3693 3694 3695
				continue;
			}
			appendPQExpBuffer(commutator, ",\n\tCOMMUTATOR = %s ", name);
		}
3696

B
Hi, all  
Bruce Momjian 已提交
3697
		if (!(strcmp(oprinfo[i].oprnegate, "0") == 0))
3698 3699 3700 3701
		{
			name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate);
			if (name == NULL)
			{
3702 3703 3704 3705
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprnegate (oid %s) not found\n",
						  oprinfo[i].oprnegate);
3706 3707 3708 3709
				continue;
			}
			appendPQExpBuffer(negator, ",\n\tNEGATOR = %s ", name);
		}
3710

B
Hi, all  
Bruce Momjian 已提交
3711 3712
		if (!(strcmp(oprinfo[i].oprrest, "-") == 0))
			appendPQExpBuffer(restrictor, ",\n\tRESTRICT = %s ", oprinfo[i].oprrest);
3713

B
Hi, all  
Bruce Momjian 已提交
3714 3715
		if (!(strcmp(oprinfo[i].oprjoin, "-") == 0))
			appendPQExpBuffer(join, ",\n\tJOIN = %s ", oprinfo[i].oprjoin);
3716

B
Hi, all  
Bruce Momjian 已提交
3717
		if (!(strcmp(oprinfo[i].oprlsortop, "0") == 0))
3718
		{
B
Bruce Momjian 已提交
3719
			name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprlsortop);
3720 3721
			if (name == NULL)
			{
3722 3723 3724 3725
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprlsortop (oid %s) not found\n",
						  oprinfo[i].oprlsortop);
3726 3727 3728 3729
				continue;
			}
			appendPQExpBuffer(sort1, ",\n\tSORT1 = %s ", name);
		}
3730

B
Hi, all  
Bruce Momjian 已提交
3731
		if (!(strcmp(oprinfo[i].oprrsortop, "0") == 0))
3732
		{
B
Bruce Momjian 已提交
3733
			name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprrsortop);
3734 3735
			if (name == NULL)
			{
3736 3737 3738 3739
				write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
						  oprinfo[i].oprname, oprinfo[i].oid);
				write_msg(NULL, "reason: oprrsortop (oid %s) not found\n",
						  oprinfo[i].oprrsortop);
3740 3741 3742 3743
				continue;
			}
			appendPQExpBuffer(sort2, ",\n\tSORT2 = %s ", name);
		}
3744

B
Bruce Momjian 已提交
3745
		resetPQExpBuffer(delq);
3746 3747
		appendPQExpBuffer(delq, "DROP OPERATOR %s (%s",
						  oprinfo[i].oprname,
3748 3749
					   findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft,
									 zeroAsNone));
B
Bruce Momjian 已提交
3750
		appendPQExpBuffer(delq, ", %s);\n",
3751 3752
					  findTypeByOid(tinfo, numTypes, oprinfo[i].oprright,
									zeroAsNone));
3753

B
Hi, all  
Bruce Momjian 已提交
3754 3755
		resetPQExpBuffer(q);
		appendPQExpBuffer(q,
3756 3757 3758 3759 3760 3761 3762 3763 3764
						  "CREATE OPERATOR %s "
						  "(PROCEDURE = %s %s%s%s%s%s%s%s%s%s);\n",
						  oprinfo[i].oprname,
						  oprinfo[i].oprcode,
						  leftarg->data,
						  rightarg->data,
						  commutator->data,
						  negator->data,
						  restrictor->data,
B
Bruce Momjian 已提交
3765
		  (strcmp(oprinfo[i].oprcanhash, "t") == 0) ? ",\n\tHASHES" : "",
3766 3767 3768
						  join->data,
						  sort1->data,
						  sort2->data);
3769

B
Bruce Momjian 已提交
3770
		ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL,
B
Bruce Momjian 已提交
3771
				q->data, delq->data, "", oprinfo[i].usename, NULL, NULL);
3772
	}
3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(leftarg);
	destroyPQExpBuffer(rightarg);
	destroyPQExpBuffer(commutator);
	destroyPQExpBuffer(negator);
	destroyPQExpBuffer(restrictor);
	destroyPQExpBuffer(join);
	destroyPQExpBuffer(sort1);
	destroyPQExpBuffer(sort2);
3784 3785 3786 3787
}

/*
 * dumpAggs
3788
 *	  writes out to fout the queries to create all the user-defined aggregates
3789 3790 3791
 *
 */
void
B
Bruce Momjian 已提交
3792
dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
3793
		 TypeInfo *tinfo, int numTypes)
3794
{
B
Bruce Momjian 已提交
3795
	int			i;
3796
	PQExpBuffer q = createPQExpBuffer();
B
Bruce Momjian 已提交
3797 3798
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer aggSig = createPQExpBuffer();
3799
	PQExpBuffer details = createPQExpBuffer();
3800 3801 3802

	for (i = 0; i < numAggs; i++)
	{
B
Bruce Momjian 已提交
3803
		char	   *name;
3804

3805
		resetPQExpBuffer(details);
B
Hi, all  
Bruce Momjian 已提交
3806

3807
		/* skip all the builtin oids */
3808
		if (oidle(atooid(agginfo[i].oid), g_last_builtin_oid))
3809 3810 3811 3812
			continue;

		resetPQExpBuffer(aggSig);
		appendPQExpBuffer(aggSig, "%s(%s)", agginfo[i].aggname,
3813 3814 3815
						  findTypeByOid(tinfo, numTypes,
										agginfo[i].aggbasetype,
										zeroAsStar + useBaseTypeName));
3816 3817 3818

		if (!agginfo[i].convertok)
		{
3819 3820 3821
			write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
					  aggSig->data);

3822
			resetPQExpBuffer(q);
3823
			appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
3824
							  aggSig->data);
3825
			ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
3826
			 q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
3827
			continue;
3828
		}
3829

3830 3831
		name = findTypeByOid(tinfo, numTypes, agginfo[i].aggbasetype,
							 zeroAsAny + useBaseTypeName);
3832 3833
		if (name == NULL)
		{
3834 3835 3836 3837 3838 3839 3840 3841 3842
			write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n",
					  agginfo[i].aggname, agginfo[i].oid);
			write_msg(NULL, "reason: aggbasetype (oid %s) not found\n",
					  agginfo[i].aggbasetype);

			resetPQExpBuffer(q);
			appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid);
			appendPQExpBuffer(q, "-- reason: aggbasetype (oid %s) not found\n", agginfo[i].aggbasetype);
			ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
3843
			 q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
3844 3845 3846
			continue;
		}
		appendPQExpBuffer(details, "BASETYPE = %s, ", name);
3847

3848 3849
		name = findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype,
							 zeroAsOpaque + useBaseTypeName);
3850 3851
		if (name == NULL)
		{
3852 3853 3854 3855 3856 3857 3858 3859 3860
			write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n",
					  agginfo[i].aggname, agginfo[i].oid);
			write_msg(NULL, "reason: aggtranstype (oid %s) not found\n",
					  agginfo[i].aggtranstype);

			resetPQExpBuffer(q);
			appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid);
			appendPQExpBuffer(q, "-- reason: aggtranstype (oid %s) not found\n", agginfo[i].aggtranstype);
			ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
3861
			 q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
3862 3863
			continue;
		}
3864 3865
		appendPQExpBuffer(details,
						  "SFUNC = %s, STYPE = %s",
3866
						  agginfo[i].aggtransfn, name);
3867

3868
		if (agginfo[i].agginitval)
3869 3870
		{
			appendPQExpBuffer(details, ", INITCOND = ");
3871
			formatStringLiteral(details, agginfo[i].agginitval, CONV_ALL);
3872
		}
3873

B
Hi, all  
Bruce Momjian 已提交
3874
		if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0))
3875 3876
			appendPQExpBuffer(details, ", FINALFUNC = %s",
							  agginfo[i].aggfinalfn);
3877

B
Bruce Momjian 已提交
3878 3879
		resetPQExpBuffer(delq);
		appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data);
3880

B
Hi, all  
Bruce Momjian 已提交
3881
		resetPQExpBuffer(q);
3882
		appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n",
3883
						  agginfo[i].aggname,
3884
						  details->data);
3885

B
Bruce Momjian 已提交
3886
		ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
B
Bruce Momjian 已提交
3887
				q->data, delq->data, "", agginfo[i].usename, NULL, NULL);
B
Bruce,  
Bruce Momjian 已提交
3888 3889 3890 3891

		/*** Dump Aggregate Comments ***/

		resetPQExpBuffer(q);
3892
		appendPQExpBuffer(q, "AGGREGATE %s", aggSig->data);
3893
		dumpComment(fout, q->data, agginfo[i].oid, "pg_aggregate", 0, NULL);
3894
	}
3895 3896 3897 3898 3899

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(aggSig);
	destroyPQExpBuffer(details);
3900 3901
}

3902 3903 3904 3905 3906
/*
 * These are some support functions to fix the acl problem of pg_dump
 *
 * Matthew C. Aycock 12/02/97
 */
3907 3908 3909

/* Append a keyword to a keyword list, inserting comma if needed.
 * Caller must make aclbuf big enough for all possible keywords.
3910
 */
3911
static void
B
Bruce Momjian 已提交
3912
AddAcl(char *aclbuf, const char *keyword)
3913
{
3914 3915 3916
	if (*aclbuf)
		strcat(aclbuf, ",");
	strcat(aclbuf, keyword);
3917
}
3918

3919
/*
3920 3921 3922 3923 3924
 * This will take a string of privilege code letters and return a malloced,
 * comma delimited string of keywords for GRANT.
 *
 * Note: for cross-version compatibility, it's important to use ALL when
 * appropriate.
3925
 */
V
Vadim B. Mikheev 已提交
3926
static char *
3927
GetPrivileges(Archive *AH, const char *s)
3928
{
3929
	char		aclbuf[100];
3930
	bool		all = true;
3931

3932
	aclbuf[0] = '\0';
3933

3934 3935 3936 3937 3938
#define CONVERT_PRIV(code,keywd) \
	if (strchr(s, code)) \
		AddAcl(aclbuf, keywd); \
	else \
		all = false
3939

3940 3941 3942
	CONVERT_PRIV('a', "INSERT");
	CONVERT_PRIV('r', "SELECT");
	CONVERT_PRIV('R', "RULE");
3943

3944 3945
	if (AH->remoteVersion >= 70200)
	{
3946 3947 3948 3949
		CONVERT_PRIV('w', "UPDATE");
		CONVERT_PRIV('d', "DELETE");
		CONVERT_PRIV('x', "REFERENCES");
		CONVERT_PRIV('t', "TRIGGER");
3950 3951 3952
	}
	else
	{
3953 3954
		/* 7.0 and 7.1 have a simpler worldview */
		CONVERT_PRIV('w', "UPDATE,DELETE");
3955
	}
3956

3957 3958 3959 3960 3961 3962
#undef CONVERT_PRIV

	if (all)
		return strdup("ALL");
	else
		return strdup(aclbuf);
3963
}
3964

B
Bruce Momjian 已提交
3965
/*
3966
 * The name says it all; a function to append a string if the dest
B
Bruce Momjian 已提交
3967 3968
 * is big enough. If not, it does a realloc.
 */
B
Bruce Momjian 已提交
3969 3970
static void
strcatalloc(char **dest, int *dSize, char *src)
B
Bruce Momjian 已提交
3971
{
B
Bruce Momjian 已提交
3972 3973 3974 3975 3976 3977 3978 3979 3980 3981
	int			dLen = strlen(*dest);
	int			sLen = strlen(src);

	if ((dLen + sLen) >= *dSize)
	{
		*dSize = (dLen + sLen) * 2;
		*dest = realloc(*dest, *dSize);
	}
	strcpy(*dest + dLen, src);
}
B
Bruce Momjian 已提交
3982 3983


B
Bruce Momjian 已提交
3984 3985
/*
 * dumpACL:
3986 3987
 *	  Write out grant/revoke information
 *	  Called for sequences and tables
B
Bruce Momjian 已提交
3988 3989
 */

3990
static void
B
Bruce Momjian 已提交
3991
dumpACL(Archive *fout, TableInfo tbinfo)
B
Bruce Momjian 已提交
3992
{
B
Bruce Momjian 已提交
3993 3994
	const char *acls = tbinfo.relacl;
	char	   *aclbuf,
B
Bruce Momjian 已提交
3995 3996 3997
			   *tok,
			   *eqpos,
			   *priv;
3998
	char	   *objoid;
B
Bruce Momjian 已提交
3999
	char	   *sql;
B
Bruce Momjian 已提交
4000
	char		tmp[1024];
B
Bruce Momjian 已提交
4001
	int			sSize = 4096;
4002 4003 4004 4005

	if (strlen(acls) == 0)
		return;					/* table has default permissions */

B
Bruce Momjian 已提交
4006 4007 4008
	/*
	 * Allocate a larginsh buffer for the output SQL.
	 */
B
Bruce Momjian 已提交
4009
	sql = (char *) malloc(sSize);
B
Bruce Momjian 已提交
4010

B
Bruce Momjian 已提交
4011 4012 4013
	/*
	 * Revoke Default permissions for PUBLIC. Is this actually necessary,
	 * or is it just a waste of time?
4014
	 */
B
Bruce Momjian 已提交
4015
	sprintf(sql, "REVOKE ALL on %s from PUBLIC;\n",
4016
			fmtId(tbinfo.relname, force_quotes));
4017 4018 4019

	/* Make a working copy of acls so we can use strtok */
	aclbuf = strdup(acls);
B
Bruce Momjian 已提交
4020

4021 4022
	/* Scan comma-separated ACL items */
	for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ","))
4023
	{
B
Bruce Momjian 已提交
4024 4025 4026
		/*
		 * Token may start with '{' and/or '"'.  Actually only the start
		 * of the string should have '{', but we don't verify that.
4027 4028 4029 4030 4031 4032 4033 4034
		 */
		if (*tok == '{')
			tok++;
		if (*tok == '"')
			tok++;

		/* User name is string up to = in tok */
		eqpos = strchr(tok, '=');
B
Bruce Momjian 已提交
4035
		if (!eqpos)
B
Bruce Momjian 已提交
4036
		{
4037 4038 4039
			write_msg(NULL, "could not parse ACL list ('%s') for relation %s\n",
					  acls, tbinfo.relname);
			exit_nicely();
B
Bruce Momjian 已提交
4040 4041
		}

B
Bruce Momjian 已提交
4042 4043 4044 4045
		/*
		 * Parse the privileges (right-hand side).	Skip if there are
		 * none.
		 */
4046
		priv = GetPrivileges(fout, eqpos + 1);
4047
		if (*priv)
4048
		{
B
Bruce Momjian 已提交
4049
			sprintf(tmp, "GRANT %s on %s to ",
4050
					priv, fmtId(tbinfo.relname, force_quotes));
B
Bruce Momjian 已提交
4051
			strcatalloc(&sql, &sSize, tmp);
B
Bruce Momjian 已提交
4052 4053 4054

			/*
			 * Note: fmtId() can only be called once per printf, so don't
4055 4056 4057 4058 4059
			 * try to merge printing of username into the above printf.
			 */
			if (eqpos == tok)
			{
				/* Empty left-hand side means "PUBLIC" */
B
Bruce Momjian 已提交
4060
				strcatalloc(&sql, &sSize, "PUBLIC;\n");
4061
			}
4062
			else
4063 4064
			{
				*eqpos = '\0';	/* it's ok to clobber aclbuf */
4065
				if (strncmp(tok, "group ", strlen("group ")) == 0)
B
Bruce Momjian 已提交
4066
					sprintf(tmp, "GROUP %s;\n",
4067 4068
							fmtId(tok + strlen("group "), force_quotes));
				else
B
Bruce Momjian 已提交
4069 4070
					sprintf(tmp, "%s;\n", fmtId(tok, force_quotes));
				strcatalloc(&sql, &sSize, tmp);
4071
			}
4072
		}
4073
		free(priv);
B
Bruce Momjian 已提交
4074
	}
4075 4076

	free(aclbuf);
B
Bruce Momjian 已提交
4077

4078 4079 4080 4081 4082 4083
	if (tbinfo.viewdef != NULL)
		objoid = tbinfo.viewoid;
	else
		objoid = tbinfo.oid;

	ArchiveEntry(fout, objoid, tbinfo.relname, "ACL", NULL, sql, "", "", "", NULL, NULL);
B
Bruce Momjian 已提交
4084

B
Bruce Momjian 已提交
4085 4086
}

4087 4088 4089
static void
_dumpTableAttr70(Archive *fout, TableInfo *tblinfo, int i, int j, PQExpBuffer q)
{
4090 4091 4092
	int32		tmp_typmod;
	int			precision;
	int			scale;
4093 4094 4095 4096 4097 4098 4099 4100 4101

	/* Show lengths on bpchar and varchar */
	if (!strcmp(tblinfo[i].typnames[j], "bpchar"))
	{
		int			len = (tblinfo[i].atttypmod[j] - VARHDRSZ);

		appendPQExpBuffer(q, "character");
		if (len > 1)
			appendPQExpBuffer(q, "(%d)",
4102
							  tblinfo[i].atttypmod[j] - VARHDRSZ);
4103 4104 4105 4106 4107 4108 4109
	}
	else if (!strcmp(tblinfo[i].typnames[j], "varchar"))
	{
		appendPQExpBuffer(q, "character varying");
		if (tblinfo[i].atttypmod[j] != -1)
		{
			appendPQExpBuffer(q, "(%d)",
4110
							  tblinfo[i].atttypmod[j] - VARHDRSZ);
4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126
		}
	}
	else if (!strcmp(tblinfo[i].typnames[j], "numeric"))
	{
		appendPQExpBuffer(q, "numeric");
		if (tblinfo[i].atttypmod[j] != -1)
		{
			tmp_typmod = tblinfo[i].atttypmod[j] - VARHDRSZ;
			precision = (tmp_typmod >> 16) & 0xffff;
			scale = tmp_typmod & 0xffff;
			appendPQExpBuffer(q, "(%d,%d)",
							  precision, scale);
		}
	}

	/*
4127 4128
	 * char is an internal single-byte data type; Let's make sure we force
	 * it through with quotes. - thomas 1998-12-13
4129 4130 4131 4132
	 */
	else if (!strcmp(tblinfo[i].typnames[j], "char"))
	{
		appendPQExpBuffer(q, "%s",
4133
						  fmtId(tblinfo[i].typnames[j], true));
4134 4135 4136 4137
	}
	else
	{
		appendPQExpBuffer(q, "%s",
4138
						  fmtId(tblinfo[i].typnames[j], false));
4139 4140
	}
}
4141

4142 4143
/*
 * dumpTables:
4144
 *	  write out to fout all the user-define tables
4145
 */
4146

4147
void
B
Bruce Momjian 已提交
4148
dumpTables(Archive *fout, TableInfo *tblinfo, int numTables,
4149
		   IndInfo *indinfo, int numIndexes,
4150 4151
		   InhInfo *inhinfo, int numInherits,
		   TypeInfo *tinfo, int numTypes, const char *tablename,
B
Bruce Momjian 已提交
4152 4153
		   const bool aclsSkip, const bool oids,
		   const bool schemaOnly, const bool dataOnly)
4154
{
4155 4156
	int			i,
				j,
B
Bruce Momjian 已提交
4157
				k;
4158
	PQExpBuffer q = createPQExpBuffer();
B
Bruce Momjian 已提交
4159
	PQExpBuffer delq = createPQExpBuffer();
4160
	char	   *serialSeq = NULL;		/* implicit sequence name created
B
Bruce Momjian 已提交
4161 4162 4163 4164
										 * by SERIAL datatype */
	const char *serialSeqSuffix = "_id_seq";	/* suffix for implicit
												 * SERIAL sequences */
	char	  **parentRels;		/* list of names of parent relations */
4165
	int			numParents;
B
Bruce Momjian 已提交
4166
	int			actual_atts;	/* number of attrs in this CREATE statment */
4167
	char	   *reltypename;
4168
	char	   *objoid;
4169
	const char *((*commentDeps)[]);
4170

V
Vadim B. Mikheev 已提交
4171
	/* First - dump SEQUENCEs */
4172
	if (tablename && strlen(tablename) > 0)
B
Bruce Momjian 已提交
4173
	{
4174 4175
		/* XXX this code only works for serial columns named "id" */
		/* We really need dependency analysis! */
B
Bruce Momjian 已提交
4176 4177 4178 4179
		serialSeq = malloc(strlen(tablename) + strlen(serialSeqSuffix) + 1);
		strcpy(serialSeq, tablename);
		strcat(serialSeq, serialSeqSuffix);
	}
V
Vadim B. Mikheev 已提交
4180 4181 4182 4183
	for (i = 0; i < numTables; i++)
	{
		if (!(tblinfo[i].sequence))
			continue;
B
Bruce Momjian 已提交
4184
		if (!tablename || (!strcmp(tblinfo[i].relname, tablename))
B
Bruce Momjian 已提交
4185
			|| (serialSeq && !strcmp(tblinfo[i].relname, serialSeq)))
V
Vadim B. Mikheev 已提交
4186
		{
B
Bruce Momjian 已提交
4187
			/* becomeUser(fout, tblinfo[i].usename); */
4188
			dumpSequence(fout, tblinfo[i], schemaOnly, dataOnly);
4189
			if (!aclsSkip)
B
Bruce Momjian 已提交
4190
				dumpACL(fout, tblinfo[i]);
V
Vadim B. Mikheev 已提交
4191 4192
		}
	}
4193
	if (serialSeq)
B
Bruce Momjian 已提交
4194
		free(serialSeq);
4195

4196 4197
	for (i = 0; i < numTables; i++)
	{
4198
		if (tblinfo[i].sequence)	/* already dumped */
V
Vadim B. Mikheev 已提交
4199
			continue;
4200

4201
		if (!tablename || (!strcmp(tblinfo[i].relname, tablename)) || (strlen(tablename) == 0))
4202 4203
		{

4204 4205
			resetPQExpBuffer(delq);
			resetPQExpBuffer(q);
4206

4207 4208 4209 4210
			/* Use the view definition if there is one */
			if (tblinfo[i].viewdef != NULL)
			{
				reltypename = "VIEW";
4211
				objoid = tblinfo[i].viewoid;
4212
				appendPQExpBuffer(delq, "DROP VIEW %s;\n", fmtId(tblinfo[i].relname, force_quotes));
4213
				appendPQExpBuffer(q, "CREATE VIEW %s as %s\n", fmtId(tblinfo[i].relname, force_quotes), tblinfo[i].viewdef);
4214
				commentDeps = malloc(sizeof(char *) * 2);
4215
				(*commentDeps)[0] = strdup(objoid);
4216
				(*commentDeps)[1] = NULL;		/* end of list */
4217 4218
			}
			else
4219
			{
4220
				reltypename = "TABLE";
4221
				objoid = tblinfo[i].oid;
4222
				commentDeps = NULL;
4223 4224
				parentRels = tblinfo[i].parentRels;
				numParents = tblinfo[i].numParents;
B
Bruce Momjian 已提交
4225

4226
				appendPQExpBuffer(delq, "DROP TABLE %s;\n", fmtId(tblinfo[i].relname, force_quotes));
B
Bruce Momjian 已提交
4227

4228 4229 4230 4231 4232 4233
				appendPQExpBuffer(q, "CREATE TABLE %s (\n\t", fmtId(tblinfo[i].relname, force_quotes));
				actual_atts = 0;
				for (j = 0; j < tblinfo[i].numatts; j++)
				{
					/* Is this one of the table's own attrs ? */
					if (tblinfo[i].inhAttrs[j] == 0)
4234
					{
4235 4236 4237 4238 4239
						/* Format properly if not first attr */
						if (actual_atts > 0)
							appendPQExpBuffer(q, ",\n\t");

						/* Attr name & type */
4240 4241 4242 4243
						appendPQExpBuffer(q, "%s ", fmtId(tblinfo[i].attnames[j], force_quotes));

						if (g_fout->remoteVersion >= 70100)
							appendPQExpBuffer(q, "%s", tblinfo[i].atttypedefns[j]);
4244
						else
4245
							_dumpTableAttr70(fout, tblinfo, i, j, q);
4246 4247

						/* Default value */
4248
						if (tblinfo[i].adef_expr[j] != NULL && tblinfo[i].inhAttrDef[j] == 0)
4249
							appendPQExpBuffer(q, " DEFAULT %s",
B
Bruce Momjian 已提交
4250
											  tblinfo[i].adef_expr[j]);
4251 4252

						/* Not Null constraint */
4253
						if (tblinfo[i].notnull[j] && tblinfo[i].inhNotNull[j] == 0)
4254 4255 4256 4257
							appendPQExpBuffer(q, " NOT NULL");

						actual_atts++;
					}
4258
				}
B
Hi all  
Bruce Momjian 已提交
4259

4260 4261


4262 4263 4264 4265 4266 4267 4268
				/* put the CONSTRAINTS inside the table def */
				for (k = 0; k < tblinfo[i].ncheck; k++)
				{
					if (actual_atts + k > 0)
						appendPQExpBuffer(q, ",\n\t");

					appendPQExpBuffer(q, "%s",
B
Bruce Momjian 已提交
4269
									  tblinfo[i].check_expr[k]);
4270
				}
4271

4272 4273
				/* Primary Key */
				if (tblinfo[i].pkIndexOid != NULL)
4274
				{
B
Bruce Momjian 已提交
4275
					PQExpBuffer consDef;
4276 4277

					/* Find the corresponding index */
4278
					for (k = 0; k < numIndexes; k++)
4279
					{
4280 4281
						if (strcmp(indinfo[k].indexreloid,
								   tblinfo[i].pkIndexOid) == 0)
4282 4283 4284
							break;
					}

4285
					if (k >= numIndexes)
4286
					{
4287
						write_msg(NULL, "dumpTables(): failed sanity check, could not find index (%s) for primary key constraint\n",
4288
								  tblinfo[i].pkIndexOid);
4289
						exit_nicely();
4290 4291 4292 4293
					}

					consDef = getPKconstraint(&tblinfo[i], &indinfo[k]);

B
Bruce Momjian 已提交
4294
					if ((actual_atts + tblinfo[i].ncheck) > 0)
4295
						appendPQExpBuffer(q, ",\n\t");
4296 4297 4298 4299

					appendPQExpBuffer(q, "%s", consDef->data);

					destroyPQExpBuffer(consDef);
4300
				}
B
Bruce Momjian 已提交
4301

4302

4303
				appendPQExpBuffer(q, "\n)");
4304

4305
				if (numParents > 0)
B
Hi all  
Bruce Momjian 已提交
4306
				{
4307
					appendPQExpBuffer(q, "\nINHERITS (");
4308 4309 4310
					for (k = 0; k < numParents; k++)
					{
						appendPQExpBuffer(q, "%s%s",
B
Bruce Momjian 已提交
4311 4312
										  (k > 0) ? ", " : "",
									 fmtId(parentRels[k], force_quotes));
4313 4314
					}
					appendPQExpBuffer(q, ")");
B
Hi all  
Bruce Momjian 已提交
4315 4316
				}

4317 4318 4319
				if (!tblinfo[i].hasoids)
					appendPQExpBuffer(q, " WITHOUT OIDS");

4320 4321
				appendPQExpBuffer(q, ";\n");
			}
B
Bruce Momjian 已提交
4322

B
Bruce Momjian 已提交
4323 4324
			if (!dataOnly)
			{
4325

4326
				ArchiveEntry(fout, objoid, tblinfo[i].relname,
B
Bruce Momjian 已提交
4327 4328
							 reltypename, NULL, q->data, delq->data, "", tblinfo[i].usename,
							 NULL, NULL);
B
Bruce Momjian 已提交
4329

4330 4331 4332 4333
				if (!aclsSkip)
					dumpACL(fout, tblinfo[i]);

			}
4334

4335
			/* Dump Field Comments */
B
Bruce,  
Bruce Momjian 已提交
4336

4337 4338
			for (j = 0; j < tblinfo[i].numatts; j++)
			{
B
Bruce,  
Bruce Momjian 已提交
4339 4340 4341 4342
				resetPQExpBuffer(q);
				appendPQExpBuffer(q, "COLUMN %s", fmtId(tblinfo[i].relname, force_quotes));
				appendPQExpBuffer(q, ".");
				appendPQExpBuffer(q, "%s", fmtId(tblinfo[i].attnames[j], force_quotes));
4343
				dumpComment(fout, q->data, tblinfo[i].oid,
4344
							"pg_class", j + 1, commentDeps);
B
Bruce,  
Bruce Momjian 已提交
4345
			}
4346

B
Bruce,  
Bruce Momjian 已提交
4347
			/* Dump Table Comments */
4348

B
Bruce,  
Bruce Momjian 已提交
4349
			resetPQExpBuffer(q);
4350
			appendPQExpBuffer(q, "%s %s", reltypename, fmtId(tblinfo[i].relname, force_quotes));
4351 4352
			dumpComment(fout, q->data, tblinfo[i].oid,
						"pg_class", 0, commentDeps);
4353

4354 4355
		}
	}
4356 4357 4358

	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
4359 4360
}

B
Bruce Momjian 已提交
4361 4362
static PQExpBuffer
getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo)
4363
{
B
Bruce Momjian 已提交
4364 4365
	PQExpBuffer pkBuf = createPQExpBuffer();
	int			k;
4366 4367

	appendPQExpBuffer(pkBuf, "Constraint %s Primary Key (",
B
Bruce Momjian 已提交
4368
					  tblInfo->primary_key_name);
4369 4370 4371

	for (k = 0; k < INDEX_MAX_KEYS; k++)
	{
4372 4373
		int			indkey;
		const char *attname;
4374 4375 4376 4377

		indkey = atoi(indInfo->indkey[k]);
		if (indkey == InvalidAttrNumber)
			break;
4378
		attname = getAttrName(indkey, tblInfo);
4379 4380

		appendPQExpBuffer(pkBuf, "%s%s",
B
Bruce Momjian 已提交
4381 4382
						  (k == 0) ? "" : ", ",
						  fmtId(attname, force_quotes));
4383 4384 4385 4386 4387 4388 4389
	}

	appendPQExpBuffer(pkBuf, ")");

	return pkBuf;
}

4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400
/*
 * 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)
4401
		return tblInfo->attnames[attrnum - 1];
4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418
	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";
	}
4419
	write_msg(NULL, "getAttrName(): invalid column number %d for table %s\n",
4420
			  attrnum, tblInfo->relname);
4421
	exit_nicely();
4422 4423 4424
	return NULL;				/* keep compiler quiet */
}

4425
/*
4426
 * dumpIndexes:
4427
 *	  write out to fout all the user-defined indexes
4428
 */
4429
void
4430
dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes,
4431
			TableInfo *tblinfo, int numTables, const char *tablename)
4432
{
4433
	int			i;
4434
	int			tableInd;
4435 4436 4437
	PQExpBuffer q = createPQExpBuffer();
	PQExpBuffer delq = createPQExpBuffer();
	PQExpBuffer id1 = createPQExpBuffer();
4438

4439
	for (i = 0; i < numIndexes; i++)
4440
	{
4441 4442 4443 4444
		if (tablename && tablename[0] &&
			(strcmp(indinfo[i].indrelname, tablename) != 0))
			continue;

4445
		tableInd = findTableByName(tblinfo, numTables,
4446 4447 4448
								   indinfo[i].indrelname);
		if (tableInd < 0)
		{
4449
			write_msg(NULL, "dumpIndexes(): failed sanity check, table %s was not found\n",
4450
					  indinfo[i].indrelname);
4451
			exit_nicely();
4452
		}
4453

4454 4455 4456
		/* Handle PK indexes */
		if (strcmp(indinfo[i].indisprimary, "t") == 0)
		{
4457
#if 0
4458 4459 4460 4461 4462

			/*
			 * PK: Enable this code when ALTER TABLE supports PK
			 * constraints.
			 */
4463 4464 4465

			PQExpBuffer consDef = getPKconstraint(&tblinfo[tableInd], &indinfo[i]);

4466
			resetPQExpBuffer(q);
4467

4468
			appendPQExpBuffer(q, "Alter Table %s Add %s;",
4469
						  fmtId(tblinfo[tableInd].relname, force_quotes),
4470 4471 4472
							  consDef->data);

			ArchiveEntry(fout, indinfo[i].oid, tblinfo[tableInd].primary_key_name,
4473
						 "CONSTRAINT", NULL, q->data, "",
4474 4475 4476 4477
						 "", tblinfo[tableInd].usename, NULL, NULL);

			destroyPQExpBuffer(consDef);
#endif
B
Bruce Momjian 已提交
4478 4479 4480 4481 4482

			/*
			 * Don't need to do anything else for this system-generated
			 * index
			 */
4483 4484 4485
			continue;
		}

4486 4487
		resetPQExpBuffer(id1);
		appendPQExpBuffer(id1, fmtId(indinfo[i].indexrelname, force_quotes));
4488

4489 4490
		resetPQExpBuffer(q);
		appendPQExpBuffer(q, "%s;\n", indinfo[i].indexdef);
B
Bruce,  
Bruce Momjian 已提交
4491

4492 4493
		resetPQExpBuffer(delq);
		appendPQExpBuffer(delq, "DROP INDEX %s;\n", id1->data);
B
Bruce Momjian 已提交
4494

4495
		/*
4496 4497 4498
		 * We make the index belong to the owner of its table, which is
		 * not necessarily right but should answer 99% of the time. Would
		 * have to add owner name to IndInfo to do it right.
4499 4500 4501 4502
		 */
		ArchiveEntry(fout, indinfo[i].indexreloid, id1->data,
					 "INDEX", NULL, q->data, delq->data,
					 "", tblinfo[tableInd].usename, NULL, NULL);
4503

4504 4505 4506 4507 4508
		/* Dump Index Comments */
		resetPQExpBuffer(q);
		appendPQExpBuffer(q, "INDEX %s", id1->data);
		dumpComment(fout, q->data, indinfo[i].indexreloid,
					"pg_class", 0, NULL);
4509
	}
4510

4511 4512 4513
	destroyPQExpBuffer(q);
	destroyPQExpBuffer(delq);
	destroyPQExpBuffer(id1);
4514
}
4515

4516
/*
B
Bruce Momjian 已提交
4517
 * dumpTuples
4518 4519 4520 4521 4522
 *	  prints out the tuples in ASCII representation. The output is a valid
 *	  input to COPY FROM stdin.
 *
 *	  We only need to do this for POSTGRES 4.2 databases since the
 *	  COPY TO statment doesn't escape newlines properly. It's been fixed
4523
 *	  in PostgreSQL.
4524 4525 4526 4527
 *
 * the attrmap passed in tells how to map the attributes copied in to the
 * attributes copied out
 */
4528
#ifdef NOT_USED
4529
void
4530
dumpTuples(PGresult *res, FILE *fout, int *attrmap)
4531
{
4532 4533 4534 4535
	int			j,
				k;
	int			m,
				n;
B
Bruce Momjian 已提交
4536
	char	  **outVals = NULL; /* values to copy out */
4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554

	n = PQntuples(res);
	m = PQnfields(res);

	if (m > 0)
	{
		/*
		 * Print out the tuples but only print tuples with at least 1
		 * field.
		 */
		outVals = (char **) malloc(m * sizeof(char *));

		for (j = 0; j < n; j++)
		{
			for (k = 0; k < m; k++)
				outVals[attrmap[k]] = PQgetvalue(res, j, k);
			for (k = 0; k < m; k++)
			{
4555
				char	   *pval = outVals[k];
4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575

				if (k != 0)
					fputc('\t', fout);	/* delimiter for attribute */

				if (pval)
				{
					while (*pval != '\0')
					{
						/* escape tabs, newlines and backslashes */
						if (*pval == '\t' || *pval == '\n' || *pval == '\\')
							fputc('\\', fout);
						fputc(*pval, fout);
						pval++;
					}
				}
			}
			fputc('\n', fout);	/* delimiter for a tuple */
		}
		free(outVals);
	}
4576
}
4577
#endif
4578

4579 4580 4581 4582
/*
 * setMaxOid -
 * find the maximum oid and generate a COPY statement to set it
*/
4583

4584
static void
B
Bruce Momjian 已提交
4585
setMaxOid(Archive *fout)
4586
{
B
Bruce Momjian 已提交
4587 4588
	PGresult   *res;
	Oid			max_oid;
B
Bruce Momjian 已提交
4589
	char		sql[1024];
4590

4591
	res = PQexec(g_conn, "CREATE TEMPORARY TABLE pgdump_oid (dummy int4)");
4592 4593 4594
	if (!res ||
		PQresultStatus(res) != PGRES_COMMAND_OK)
	{
4595
		write_msg(NULL, "could not create pgdump_oid table: %s", PQerrorMessage(g_conn));
4596
		exit_nicely();
4597 4598
	}
	PQclear(res);
4599
	res = PQexec(g_conn, "INSERT INTO pgdump_oid VALUES (0)");
4600 4601 4602
	if (!res ||
		PQresultStatus(res) != PGRES_COMMAND_OK)
	{
4603
		write_msg(NULL, "could not insert into pgdump_oid table: %s", PQerrorMessage(g_conn));
4604
		exit_nicely();
4605
	}
4606
	max_oid = PQoidValue(res);
4607 4608
	if (max_oid == 0)
	{
4609
		write_msg(NULL, "inserted invalid oid\n");
4610
		exit_nicely();
4611 4612
	}
	PQclear(res);
4613
	res = PQexec(g_conn, "DROP TABLE pgdump_oid;");
4614 4615 4616
	if (!res ||
		PQresultStatus(res) != PGRES_COMMAND_OK)
	{
4617
		write_msg(NULL, "could not drop pgdump_oid table: %s", PQerrorMessage(g_conn));
4618
		exit_nicely();
4619 4620 4621
	}
	PQclear(res);
	if (g_verbose)
4622
		write_msg(NULL, "maximum system oid is %u\n", max_oid);
4623 4624 4625
	snprintf(sql, 1024,
			 "CREATE TEMPORARY TABLE pgdump_oid (dummy int4);\n"
			 "COPY pgdump_oid WITH OIDS FROM stdin;\n"
4626
			 "%u\t0\n"
4627 4628 4629
			 "\\.\n"
			 "DROP TABLE pgdump_oid;\n",
			 max_oid);
B
Bruce Momjian 已提交
4630

4631
	ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "", "", "", NULL, NULL);
4632
}
4633 4634 4635

/*
 * findLastBuiltInOid -
4636
 * find the last built in oid
4637 4638
 * we do this by retrieving datlastsysoid from the pg_database entry for this database,
 */
4639

4640
static Oid
4641
findLastBuiltinOid_V71(const char *dbname)
4642
{
B
Bruce Momjian 已提交
4643
	PGresult   *res;
4644
	int			ntups;
4645
	Oid			last_oid;
4646 4647 4648
	PQExpBuffer query = createPQExpBuffer();

	resetPQExpBuffer(query);
4649
	appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
4650
	formatStringLiteral(query, dbname, CONV_ALL);
4651

4652
	res = PQexec(g_conn, query->data);
4653 4654 4655
	if (res == NULL ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
4656
		write_msg(NULL, "error in finding the last system oid: %s", PQerrorMessage(g_conn));
4657
		exit_nicely();
4658 4659
	}
	ntups = PQntuples(res);
4660
	if (ntups < 1)
4661
	{
4662
		write_msg(NULL, "missing pg_database entry for this database\n");
4663
		exit_nicely();
4664 4665 4666
	}
	if (ntups > 1)
	{
4667
		write_msg(NULL, "found more than one pg_database entry for this database\n");
4668
		exit_nicely();
4669
	}
4670
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
4671
	PQclear(res);
4672
	destroyPQExpBuffer(query);
4673
	return last_oid;
4674 4675
}

4676 4677 4678 4679 4680 4681 4682
/*
 * findLastBuiltInOid -
 * find the last built in oid
 * we do this by looking up the oid of 'template1' in pg_database,
 * this is probably not foolproof but comes close
*/

4683
static Oid
4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694
findLastBuiltinOid_V70(void)
{
	PGresult   *res;
	int			ntups;
	int			last_oid;

	res = PQexec(g_conn,
			  "SELECT oid from pg_database where datname = 'template1'");
	if (res == NULL ||
		PQresultStatus(res) != PGRES_TUPLES_OK)
	{
4695
		write_msg(NULL, "error in finding the template1 database: %s", PQerrorMessage(g_conn));
4696
		exit_nicely();
4697 4698 4699 4700
	}
	ntups = PQntuples(res);
	if (ntups < 1)
	{
4701
		write_msg(NULL, "could not find template1 database entry in the pg_database table\n");
4702
		exit_nicely();
4703 4704 4705
	}
	if (ntups > 1)
	{
4706
		write_msg(NULL, "found more than one template1 database entry in the pg_database table\n");
4707
		exit_nicely();
4708 4709 4710 4711 4712
	}
	last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
	PQclear(res);
	return last_oid;
}
4713

4714
static void
4715
dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly)
4716
{
4717
	PGresult   *res;
4718 4719 4720 4721 4722 4723
	char	   *last,
			   *incby,
			   *maxv,
			   *minv,
			   *cache;
	bool		cycled,
B
Bruce Momjian 已提交
4724
				called;
4725
	PQExpBuffer query = createPQExpBuffer();
B
Bruce Momjian 已提交
4726
	PQExpBuffer delqry = createPQExpBuffer();
4727

B
Hi, all  
Bruce Momjian 已提交
4728
	appendPQExpBuffer(query,
4729
			"SELECT sequence_name, last_value, increment_by, max_value, "
4730 4731
				  "min_value, cache_value, is_cycled, is_called from %s",
					  fmtId(tbinfo.relname, force_quotes));
4732

B
Hi, all  
Bruce Momjian 已提交
4733
	res = PQexec(g_conn, query->data);
4734 4735
	if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
	{
4736
		write_msg(NULL, "query to get data of sequence \"%s\" failed: %s", tbinfo.relname, PQerrorMessage(g_conn));
4737
		exit_nicely();
4738 4739 4740 4741
	}

	if (PQntuples(res) != 1)
	{
4742
		write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
4743
				  tbinfo.relname, PQntuples(res));
4744
		exit_nicely();
4745 4746
	}

4747 4748
	/* Disable this check: it fails if sequence has been renamed */
#ifdef NOT_USED
4749 4750
	if (strcmp(PQgetvalue(res, 0, 0), tbinfo.relname) != 0)
	{
4751
		write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
4752
				  tbinfo.relname, PQgetvalue(res, 0, 0));
4753
		exit_nicely();
4754
	}
4755
#endif
4756

4757 4758 4759 4760 4761 4762 4763
	last = PQgetvalue(res, 0, 1);
	incby = PQgetvalue(res, 0, 2);
	maxv = PQgetvalue(res, 0, 3);
	minv = PQgetvalue(res, 0, 4);
	cache = PQgetvalue(res, 0, 5);
	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
	called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
4764

4765
	/*
B
Bruce Momjian 已提交
4766 4767
	 * The logic we use for restoring sequences is as follows: -   Add a
	 * basic CREATE SEQUENCE statement (use last_val for start if called
4768
	 * is false, else use min_val for start_val).
4769
	 *
4770 4771
	 * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
	 * data
4772
	 */
4773

4774 4775 4776
	if (!dataOnly)
	{
		resetPQExpBuffer(delqry);
4777 4778
		appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
						  fmtId(tbinfo.relname, force_quotes));
4779

4780 4781
		resetPQExpBuffer(query);
		appendPQExpBuffer(query,
4782 4783
						  "CREATE SEQUENCE %s start %s increment %s "
						  "maxvalue %s minvalue %s cache %s%s;\n",
B
Bruce Momjian 已提交
4784
						  fmtId(tbinfo.relname, force_quotes),
4785
						  (called ? minv : last),
4786
						  incby, maxv, minv, cache,
4787
						  (cycled ? " cycle" : ""));
4788

P
Philip Warner 已提交
4789
		ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE", NULL,
4790 4791
					 query->data, delqry->data, "", tbinfo.usename,
					 NULL, NULL);
4792
	}
4793

4794 4795 4796 4797 4798
	if (!schemaOnly)
	{
		resetPQExpBuffer(query);
		appendPQExpBuffer(query, "SELECT setval (");
		formatStringLiteral(query, fmtId(tbinfo.relname, force_quotes), CONV_ALL);
4799 4800
		appendPQExpBuffer(query, ", %s, %s);\n",
						  last, (called ? "true" : "false"));
4801

P
Philip Warner 已提交
4802
		ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE SET", NULL,
4803 4804
					 query->data, "" /* Del */ , "", tbinfo.usename,
					 NULL, NULL);
4805
	}
B
Bruce,  
Bruce Momjian 已提交
4806

4807 4808 4809
	if (!dataOnly)
	{
		/* Dump Sequence Comments */
B
Bruce,  
Bruce Momjian 已提交
4810

4811 4812
		resetPQExpBuffer(query);
		appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo.relname, force_quotes));
4813 4814
		dumpComment(fout, query->data, tbinfo.oid,
					"pg_class", 0, NULL);
4815
	}
4816

4817 4818
	PQclear(res);

4819 4820
	destroyPQExpBuffer(query);
	destroyPQExpBuffer(delqry);
4821
}
V
Vadim B. Mikheev 已提交
4822 4823


4824
static void
B
Bruce Momjian 已提交
4825
dumpTriggers(Archive *fout, const char *tablename,
4826
			 TableInfo *tblinfo, int numTables)
V
Vadim B. Mikheev 已提交
4827
{
4828 4829 4830
	int			i,
				j;

V
Vadim B. Mikheev 已提交
4831
	if (g_verbose)
4832
		write_msg(NULL, "dumping out triggers\n");
4833

V
Vadim B. Mikheev 已提交
4834 4835
	for (i = 0; i < numTables; i++)
	{
B
Bruce Momjian 已提交
4836
		if (tablename && (strcmp(tblinfo[i].relname, tablename) != 0) && (strlen(tablename) > 0))
V
Vadim B. Mikheev 已提交
4837
			continue;
4838

V
Vadim B. Mikheev 已提交
4839 4840
		for (j = 0; j < tblinfo[i].ntrig; j++)
		{
B
Bruce Momjian 已提交
4841
			ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname,
B
Bruce Momjian 已提交
4842 4843
				   "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "",
						 tblinfo[i].usename, NULL, NULL);
4844 4845
			dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid,
						"pg_trigger", 0, NULL);
V
Vadim B. Mikheev 已提交
4846 4847 4848
		}
	}
}
4849 4850


4851
static void
B
Bruce Momjian 已提交
4852
dumpRules(Archive *fout, const char *tablename,
B
Bruce Momjian 已提交
4853
		  TableInfo *tblinfo, int numTables)
4854
{
B
Bruce Momjian 已提交
4855 4856 4857 4858
	PGresult   *res;
	int			nrules;
	int			i,
				t;
4859
	PQExpBuffer query = createPQExpBuffer();
4860

B
Bruce Momjian 已提交
4861
	int			i_definition;
4862
	int			i_oid;
4863
	int			i_owner;
4864
	int			i_rulename;
4865 4866

	if (g_verbose)
4867
		write_msg(NULL, "dumping out rules\n");
4868 4869 4870 4871 4872 4873

	/*
	 * For each table we dump
	 */
	for (t = 0; t < numTables; t++)
	{
B
Bruce Momjian 已提交
4874
		if (tablename && (strcmp(tblinfo[t].relname, tablename) != 0) && (strlen(tablename) > 0))
4875 4876 4877
			continue;

		/*
B
Bruce Momjian 已提交
4878 4879
		 * Get all rules defined for this table We include pg_rules in the
		 * cross since it filters out all view rules (pjw 15-Sep-2000).
4880 4881
		 *
		 * XXXX: Use LOJ here
4882
		 */
B
Hi, all  
Bruce Momjian 已提交
4883
		resetPQExpBuffer(query);
4884
		appendPQExpBuffer(query, "SELECT definition,"
4885
						  "   (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, "
4886 4887
						  "   pg_rewrite.oid, pg_rewrite.rulename "
						  "FROM pg_rewrite, pg_class, pg_rules "
4888
						  "WHERE pg_class.relname = ");
4889
		formatStringLiteral(query, tblinfo[t].relname, CONV_ALL);
4890
		appendPQExpBuffer(query,
4891 4892
						  "    AND pg_rewrite.ev_class = pg_class.oid "
						  "    AND pg_rules.tablename = pg_class.relname "
B
Bruce Momjian 已提交
4893
					   "    AND pg_rules.rulename = pg_rewrite.rulename "
4894
						  "ORDER BY pg_rewrite.oid");
B
Hi, all  
Bruce Momjian 已提交
4895
		res = PQexec(g_conn, query->data);
4896 4897 4898
		if (!res ||
			PQresultStatus(res) != PGRES_TUPLES_OK)
		{
4899
			write_msg(NULL, "query to get rules associated with table \"%s\" failed: %s",
4900
					  tblinfo[t].relname, PQerrorMessage(g_conn));
4901
			exit_nicely();
4902 4903 4904 4905
		}

		nrules = PQntuples(res);
		i_definition = PQfnumber(res, "definition");
4906
		i_owner = PQfnumber(res, "viewowner");
B
Bruce,  
Bruce Momjian 已提交
4907 4908
		i_oid = PQfnumber(res, "oid");
		i_rulename = PQfnumber(res, "rulename");
4909 4910 4911 4912

		/*
		 * Dump them out
		 */
B
Bruce,  
Bruce Momjian 已提交
4913

4914 4915
		for (i = 0; i < nrules; i++)
		{
B
Bruce Momjian 已提交
4916
			ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
B
Bruce Momjian 已提交
4917 4918
						 "RULE", NULL, PQgetvalue(res, i, i_definition),
						 "", "", PQgetvalue(res, i, i_owner), NULL, NULL);
4919

B
Bruce,  
Bruce Momjian 已提交
4920 4921 4922 4923
			/* Dump rule comments */

			resetPQExpBuffer(query);
			appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename), force_quotes));
4924 4925
			dumpComment(fout, query->data, PQgetvalue(res, i, i_oid),
						"pg_rewrite", 0, NULL);
4926

B
Bruce,  
Bruce Momjian 已提交
4927 4928
		}

4929 4930
		PQclear(res);
	}
4931 4932

	destroyPQExpBuffer(query);
4933
}