OSDN Git Service

7cb3ea430bb7968216befd4b58f83971a920a4af
[pg-rex/syncrep.git] / src / bin / pg_dump / pg_dump.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_dump.c
4  *        pg_dump is a utility for dumping out a postgres database
5  *        into a script file.
6  *
7  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *      pg_dump will read the system catalogs in a database and
11  *      dump out a script that reproduces
12  *      the schema of the database in terms of
13  *                user-defined types
14  *                user-defined functions
15  *                tables
16  *                indexes
17  *                aggregates
18  *                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.257 2002/04/29 17:30:18 tgl Exp $
26  *
27  *-------------------------------------------------------------------------
28  */
29
30 /*
31  * Although this is not a backend module, we must include postgres.h anyway
32  * so that we can include a bunch of backend include files.  pg_dump has
33  * never pretended to be very independent of the backend anyhow ...
34  */
35 #include "postgres.h"
36
37 #include <unistd.h>                             /* for getopt() */
38 #include <ctype.h>
39 #ifdef ENABLE_NLS
40 #include <locale.h>
41 #endif
42 #ifdef HAVE_GETOPT_H
43 #include <getopt.h>
44 #endif
45 #ifdef HAVE_TERMIOS_H
46 #include <termios.h>
47 #endif
48
49 #ifndef HAVE_STRDUP
50 #include "strdup.h"
51 #endif
52
53 #include "access/attnum.h"
54 #include "access/htup.h"
55 #include "catalog/pg_class.h"
56 #include "catalog/pg_proc.h"
57 #include "catalog/pg_trigger.h"
58 #include "catalog/pg_type.h"
59
60 #include "libpq-fe.h"
61 #include "libpq/libpq-fs.h"
62
63 #include "pg_dump.h"
64 #include "pg_backup.h"
65 #include "pg_backup_archiver.h"
66
67 typedef enum _formatLiteralOptions
68 {
69         CONV_ALL = 0,
70         PASS_LFTAB = 3                          /* NOTE: 1 and 2 are reserved in case we
71                                                                  * want to make a mask. */
72         /* We could make this a bit mask for control chars, but I don't */
73         /* see any value in making it more complex...the current code */
74         /* only checks for 'opts == CONV_ALL' anyway. */
75 } formatLiteralOptions;
76
77 static void dumpComment(Archive *fout, const char *target, const char *oid,
78                         const char *classname, int subid,
79                         const char *((*deps)[]));
80 static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
81 static void dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly);
82 static void dumpACL(Archive *fout, TableInfo tbinfo);
83 static void dumpTriggers(Archive *fout, const char *tablename,
84                          TableInfo *tblinfo, int numTables);
85 static void dumpRules(Archive *fout, const char *tablename,
86                   TableInfo *tblinfo, int numTables);
87 static void formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions opts);
88 static void clearTableInfo(TableInfo *, int);
89 static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
90                         TypeInfo *tinfo, int numTypes);
91 static Oid      findLastBuiltinOid_V71(const char *);
92 static Oid      findLastBuiltinOid_V70(void);
93 static void setMaxOid(Archive *fout);
94
95
96 static void AddAcl(char *aclbuf, const char *keyword);
97 static char *GetPrivileges(Archive *AH, const char *s);
98
99 static int      dumpBlobs(Archive *AH, char *, void *);
100 static int      dumpDatabase(Archive *AH);
101 static PQExpBuffer getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo);
102 static const char *getAttrName(int attrnum, TableInfo *tblInfo);
103
104 extern char *optarg;
105 extern int      optind,
106                         opterr;
107
108 /* global decls */
109 bool            g_verbose;                      /* User wants verbose narration of our
110                                                                  * activities. */
111 Oid                     g_last_builtin_oid; /* value of the last builtin oid */
112 Archive    *g_fout;                             /* the script file */
113 PGconn     *g_conn;                             /* the database connection */
114
115 bool            force_quotes;           /* User wants to suppress double-quotes */
116 bool            dumpData;                       /* dump data using proper insert strings */
117 bool            attrNames;                      /* put attr names into insert strings */
118 bool            schemaOnly;
119 bool            dataOnly;
120 bool            aclsSkip;
121
122 char            g_opaque_type[10];      /* name for the opaque type */
123
124 /* placeholders for the delimiters for comments */
125 char            g_comment_start[10];
126 char            g_comment_end[10];
127
128
129 typedef struct _dumpContext
130 {
131         TableInfo  *tblinfo;
132         int                     tblidx;
133         bool            oids;
134 } DumpContext;
135
136 static void
137 help(const char *progname)
138 {
139         printf(gettext("%s dumps a database as a text file or to other formats.\n\n"), progname);
140         puts(gettext("Usage:"));
141         printf(gettext("  %s [options] dbname\n\n"), progname);
142         puts(gettext("Options:"));
143
144 #ifdef HAVE_GETOPT_LONG
145         puts(gettext(
146                 "  -a, --data-only          dump only the data, not the schema\n"
147                 "  -b, --blobs              include large objects in dump\n"
148                 "  -c, --clean              clean (drop) schema prior to create\n"
149                 "  -C, --create             include commands to create database in dump\n"
150                 "  -d, --inserts            dump data as INSERT, rather than COPY, commands\n"
151                 "  -D, --column-inserts     dump data as INSERT commands with column names\n"
152                 "  -f, --file=FILENAME      output file name\n"
153                 "  -F, --format {c|t|p}     output file format (custom, tar, plain text)\n"
154                 "  -h, --host=HOSTNAME      database server host name\n"
155                 "  -i, --ignore-version     proceed even when server version mismatches\n"
156                 "                           pg_dump version\n"
157                 "  -n, --no-quotes          suppress most quotes around identifiers\n"
158                 "  -N, --quotes             enable most quotes around identifiers\n"
159                 "  -o, --oids               include oids in dump\n"
160                 "  -O, --no-owner           do not output \\connect commands in plain\n"
161                 "                           text format\n"
162                 "  -p, --port=PORT          database server port number\n"
163                 "  -R, --no-reconnect       disable ALL reconnections to the database in\n"
164                 "                           plain text format\n"
165                 "  -s, --schema-only        dump only the schema, no data\n"
166                 "  -S, --superuser=NAME     specify the superuser user name to use in\n"
167                 "                           plain text format\n"
168                 "  -t, --table=TABLE        dump this table only (* for all)\n"
169                 "  -U, --username=NAME      connect as specified database user\n"
170                 "  -v, --verbose            verbose mode\n"
171                 "  -W, --password           force password prompt (should happen automatically)\n"
172                 "  -x, --no-privileges      do not dump privileges (grant/revoke)\n"
173                 "  -X use-set-session-authorization, --use-set-session-authorization\n"
174                 "                           output SET SESSION AUTHORIZATION commands rather\n"
175                 "                           than \\connect commands\n"
176                 "  -Z, --compress {0-9}     compression level for compressed formats\n"
177         ));
178 #else
179         puts(gettext(
180                 "  -a                       dump only the data, not the schema\n"
181                 "  -b                       include large objects in dump\n"
182                 "  -c                       clean (drop) schema prior to create\n"
183                 "  -C                       include commands to create database in dump\n"
184                 "  -d                       dump data as INSERT, rather than COPY, commands\n"
185                 "  -D                       dump data as INSERT commands with column names\n"
186                 "  -f FILENAME              output file name\n"
187                 "  -F {c|t|p}               output file format (custom, tar, plain text)\n"
188                 "  -h HOSTNAME              database server host name\n"
189                 "  -i                       proceed even when server version mismatches\n"
190                 "                           pg_dump version\n"
191                 "  -n                       suppress most quotes around identifiers\n"
192                 "  -N                       enable most quotes around identifiers\n"
193                 "  -o                       include oids in dump\n"
194                 "  -O                       do not output \\connect commands in plain\n"
195                 "                           text format\n"
196                 "  -p PORT                  database server port number\n"
197                 "  -R                       disable ALL reconnections to the database in\n"
198                 "                           plain text format\n"
199                 "  -s                       dump only the schema, no data\n"
200                 "  -S NAME                  specify the superuser user name to use in\n"
201                 "                           plain text format\n"
202                 "  -t TABLE                 dump this table only (* for all)\n"
203                 "  -U NAME                  connect as specified database user\n"
204                 "  -v                       verbose mode\n"
205                 "  -W                       force password prompt (should happen automatically)\n"
206                 "  -x                       do not dump privileges (grant/revoke)\n"
207                 "  -X use-set-session-authorization\n"
208                 "                           output SET SESSION AUTHORIZATION commands rather\n"
209                 "                           than \\connect commands\n"
210                 "  -Z {0-9}                 compression level for compressed formats\n"
211         ));
212 #endif
213         puts(gettext("If no database name is not supplied, then the PGDATABASE environment\n"
214                                  "variable value is used.\n\n"
215                                  "Report bugs to <pgsql-bugs@postgresql.org>."));
216 }
217
218
219 void
220 exit_nicely(void)
221 {
222         PQfinish(g_conn);
223         if (g_verbose)
224                 write_msg(NULL, "*** aborted because of error\n");
225         exit(1);
226 }
227
228
229 #define COPYBUFSIZ              8192
230
231 /*
232  *      Dump a table's contents for loading using the COPY command
233  *      - this routine is called by the Archiver when it wants the table
234  *        to be dumped.
235  */
236
237 static int
238 dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
239 {
240         const DumpContext *dctx = (DumpContext *) dctxv;
241         const char *classname = dctx->tblinfo[dctx->tblidx].relname;
242         const bool      hasoids = dctx->tblinfo[dctx->tblidx].hasoids;
243         const bool      oids = dctx->oids;
244
245         PGresult   *res;
246         char            query[255];
247         int                     ret;
248         bool            copydone;
249         char            copybuf[COPYBUFSIZ];
250
251         if (g_verbose)
252                 write_msg(NULL, "dumping out the contents of table %s\n", classname);
253
254         if (oids && hasoids)
255         {
256                 /*
257                  * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n",
258                  * fmtId(classname, force_quotes));
259                  *
260                  * - Not used as of V1.3 (needs to be in ArchiveEntry call)
261                  *
262                  */
263
264                 sprintf(query, "COPY %s WITH OIDS TO stdout;",
265                                 fmtId(classname, force_quotes));
266         }
267         else
268         {
269                 /*
270                  * archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname,
271                  * force_quotes));
272                  *
273                  * - Not used as of V1.3 (needs to be in ArchiveEntry call)
274                  *
275                  */
276
277                 sprintf(query, "COPY %s TO stdout;", fmtId(classname, force_quotes));
278         }
279         res = PQexec(g_conn, query);
280         if (!res ||
281                 PQresultStatus(res) == PGRES_FATAL_ERROR)
282         {
283                 write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed\n",
284                                   classname);
285                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
286                 write_msg(NULL, "The command was: %s\n", query);
287                 exit_nicely();
288         }
289         else
290         {
291                 if (PQresultStatus(res) != PGRES_COPY_OUT)
292                 {
293                         write_msg(NULL, "SQL command to dump the contents of table \"%s\" executed abnormally.\n",
294                                           classname);
295                         write_msg(NULL, "The server returned status %d when %d was expected.\n",
296                                           PQresultStatus(res), PGRES_COPY_OUT);
297                         write_msg(NULL, "The command was: %s\n", query);
298                         exit_nicely();
299                 }
300                 else
301                 {
302                         copydone = false;
303
304                         while (!copydone)
305                         {
306                                 ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
307
308                                 if (copybuf[0] == '\\' &&
309                                         copybuf[1] == '.' &&
310                                         copybuf[2] == '\0')
311                                 {
312                                         copydone = true;        /* don't print this... */
313                                 }
314                                 else
315                                 {
316                                         archputs(copybuf, fout);
317                                         switch (ret)
318                                         {
319                                                 case EOF:
320                                                         copydone = true;
321                                                         /* FALLTHROUGH */
322                                                 case 0:
323                                                         archputc('\n', fout);
324                                                         break;
325                                                 case 1:
326                                                         break;
327                                         }
328                                 }
329
330                                 /*
331                                  * THROTTLE:
332                                  *
333                                  * There was considerable discussion in late July, 2000
334                                  * regarding slowing down pg_dump when backing up large
335                                  * tables. Users with both slow & fast (muti-processor)
336                                  * machines experienced performance degradation when doing
337                                  * a backup.
338                                  *
339                                  * Initial attempts based on sleeping for a number of ms for
340                                  * each ms of work were deemed too complex, then a simple
341                                  * 'sleep in each loop' implementation was suggested. The
342                                  * latter failed because the loop was too tight. Finally,
343                                  * the following was implemented:
344                                  *
345                                  * If throttle is non-zero, then See how long since the last
346                                  * sleep. Work out how long to sleep (based on ratio). If
347                                  * sleep is more than 100ms, then sleep reset timer EndIf
348                                  * EndIf
349                                  *
350                                  * where the throttle value was the number of ms to sleep per
351                                  * ms of work. The calculation was done in each loop.
352                                  *
353                                  * Most of the hard work is done in the backend, and this
354                                  * solution still did not work particularly well: on slow
355                                  * machines, the ratio was 50:1, and on medium paced
356                                  * machines, 1:1, and on fast multi-processor machines, it
357                                  * had little or no effect, for reasons that were unclear.
358                                  *
359                                  * Further discussion ensued, and the proposal was dropped.
360                                  *
361                                  * For those people who want this feature, it can be
362                                  * implemented using gettimeofday in each loop,
363                                  * calculating the time since last sleep, multiplying that
364                                  * by the sleep ratio, then if the result is more than a
365                                  * preset 'minimum sleep time' (say 100ms), call the
366                                  * 'select' function to sleep for a subsecond period ie.
367                                  *
368                                  * select(0, NULL, NULL, NULL, &tvi);
369                                  *
370                                  * This will return after the interval specified in the
371                                  * structure tvi. Fianally, call gettimeofday again to
372                                  * save the 'last sleep time'.
373                                  */
374                         }
375                         archprintf(fout, "\\.\n");
376                 }
377                 ret = PQendcopy(g_conn);
378                 if (ret != 0)
379                 {
380                         write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname);
381                         write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
382                         write_msg(NULL, "The command was: %s\n", query);
383                         PQclear(res);
384                         exit_nicely();
385                 }
386         }
387
388         return 1;
389 }
390
391 static int
392 dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
393 {
394         const DumpContext *dctx = (DumpContext *) dctxv;
395         const char *classname = dctx->tblinfo[dctx->tblidx].relname;
396
397         PGresult   *res;
398         PQExpBuffer q = createPQExpBuffer();
399         int                     tuple;
400         int                     field;
401
402         if (fout->remoteVersion >= 70100)
403                 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM ONLY %s", fmtId(classname, force_quotes));
404         else
405                 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM %s", fmtId(classname, force_quotes));
406
407         res = PQexec(g_conn, q->data);
408         if (!res ||
409                 PQresultStatus(res) != PGRES_COMMAND_OK)
410         {
411                 write_msg(NULL, "dumpClasses(): SQL command failed\n");
412                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
413                 write_msg(NULL, "The command was: %s\n", q->data);
414                 exit_nicely();
415         }
416
417         do
418         {
419                 PQclear(res);
420
421                 res = PQexec(g_conn, "FETCH 100 FROM _pg_dump_cursor");
422                 if (!res ||
423                         PQresultStatus(res) != PGRES_TUPLES_OK)
424                 {
425                         write_msg(NULL, "dumpClasses(): SQL command failed\n");
426                         write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
427                         write_msg(NULL, "The command was: FETCH 100 FROM _pg_dump_cursor\n");
428                         exit_nicely();
429                 }
430
431                 for (tuple = 0; tuple < PQntuples(res); tuple++)
432                 {
433                         archprintf(fout, "INSERT INTO %s ", fmtId(classname, force_quotes));
434                         if (attrNames == true)
435                         {
436                                 resetPQExpBuffer(q);
437                                 appendPQExpBuffer(q, "(");
438                                 for (field = 0; field < PQnfields(res); field++)
439                                 {
440                                         if (field > 0)
441                                                 appendPQExpBuffer(q, ",");
442                                         appendPQExpBuffer(q, fmtId(PQfname(res, field), force_quotes));
443                                 }
444                                 appendPQExpBuffer(q, ") ");
445                                 archprintf(fout, "%s", q->data);
446                         }
447                         archprintf(fout, "VALUES (");
448                         for (field = 0; field < PQnfields(res); field++)
449                         {
450                                 if (field > 0)
451                                         archprintf(fout, ",");
452                                 if (PQgetisnull(res, tuple, field))
453                                 {
454                                         archprintf(fout, "NULL");
455                                         continue;
456                                 }
457                                 switch (PQftype(res, field))
458                                 {
459                                         case INT2OID:
460                                         case INT4OID:
461                                         case OIDOID:            /* int types */
462                                         case FLOAT4OID:
463                                         case FLOAT8OID:         /* float types */
464                                                 /* These types are printed without quotes */
465                                                 archprintf(fout, "%s",
466                                                                    PQgetvalue(res, tuple, field));
467                                                 break;
468                                         case BITOID:
469                                         case VARBITOID:
470                                                 archprintf(fout, "B'%s'",
471                                                                    PQgetvalue(res, tuple, field));
472                                                 break;
473                                         default:
474
475                                                 /*
476                                                  * All other types are printed as string literals,
477                                                  * with appropriate escaping of special
478                                                  * characters.
479                                                  */
480                                                 resetPQExpBuffer(q);
481                                                 formatStringLiteral(q, PQgetvalue(res, tuple, field), CONV_ALL);
482                                                 archprintf(fout, "%s", q->data);
483                                                 break;
484                                 }
485                         }
486                         archprintf(fout, ");\n");
487                 }
488
489         } while (PQntuples(res) > 0);
490         PQclear(res);
491
492         res = PQexec(g_conn, "CLOSE _pg_dump_cursor");
493         if (!res ||
494                 PQresultStatus(res) != PGRES_COMMAND_OK)
495         {
496                 write_msg(NULL, "dumpClasses(): SQL command failed\n");
497                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
498                 write_msg(NULL, "The command was: CLOSE _pg_dump_cursor\n");
499                 exit_nicely();
500         }
501         PQclear(res);
502
503         destroyPQExpBuffer(q);
504         return 1;
505 }
506
507 /*
508  * Convert a string value to an SQL string literal,
509  * with appropriate escaping of special characters.
510  * Quote mark ' goes to '' per SQL standard, other
511  * stuff goes to \ sequences.
512  * The literal is appended to the given PQExpBuffer.
513  */
514 static void
515 formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions opts)
516 {
517         appendPQExpBufferChar(buf, '\'');
518         while (*str)
519         {
520                 char            ch = *str++;
521
522                 if (ch == '\\' || ch == '\'')
523                 {
524                         appendPQExpBufferChar(buf, ch);         /* double these */
525                         appendPQExpBufferChar(buf, ch);
526                 }
527                 else if ((unsigned char) ch < (unsigned char) ' ' &&
528                                  (opts == CONV_ALL
529                                   || (ch != '\n' && ch != '\t')
530                                   ))
531                 {
532                         /*
533                          * generate octal escape for control chars other than
534                          * whitespace
535                          */
536                         appendPQExpBufferChar(buf, '\\');
537                         appendPQExpBufferChar(buf, ((ch >> 6) & 3) + '0');
538                         appendPQExpBufferChar(buf, ((ch >> 3) & 7) + '0');
539                         appendPQExpBufferChar(buf, (ch & 7) + '0');
540                 }
541                 else
542                         appendPQExpBufferChar(buf, ch);
543         }
544         appendPQExpBufferChar(buf, '\'');
545 }
546
547 /*
548  * DumpClasses -
549  *        dump the contents of all the classes.
550  */
551 static void
552 dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
553                  const char *onlytable, const bool oids, const bool force_quotes)
554 {
555         int                     i;
556         DataDumperPtr dumpFn;
557         DumpContext *dumpCtx;
558         char            copyBuf[512];
559         char       *copyStmt;
560
561         if (g_verbose)
562         {
563                 if (onlytable == NULL || (strlen(onlytable) == 0))
564                         write_msg(NULL, "preparing to dump the contents of all %d tables/sequences\n", numTables);
565                 else
566                         write_msg(NULL, "preparing to dump the contents of only one table/sequence\n");
567         }
568
569         for (i = 0; i < numTables; i++)
570         {
571                 const char *classname = tblinfo[i].relname;
572
573                 /* Skip VIEW relations */
574                 if (tblinfo[i].viewdef != NULL)
575                         continue;
576
577                 if (tblinfo[i].relkind == RELKIND_SEQUENCE)             /* already dumped */
578                         continue;
579
580                 if (!onlytable || (strcmp(classname, onlytable) == 0) || (strlen(onlytable) == 0))
581                 {
582                         if (g_verbose)
583                                 write_msg(NULL, "preparing to dump the contents of table %s\n", classname);
584
585 #if 0
586                         becomeUser(fout, tblinfo[i].usename);
587 #endif
588
589                         dumpCtx = (DumpContext *) malloc(sizeof(DumpContext));
590                         dumpCtx->tblinfo = (TableInfo *) tblinfo;
591                         dumpCtx->tblidx = i;
592                         dumpCtx->oids = oids;
593
594                         if (!dumpData)
595                         {
596                                 /* Dump/restore using COPY */
597                                 dumpFn = dumpClasses_nodumpData;
598                                 sprintf(copyBuf, "COPY %s %sFROM stdin;\n",
599                                                 fmtId(tblinfo[i].relname, force_quotes),
600                                                 (oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
601                                 copyStmt = copyBuf;
602                         }
603                         else
604                         {
605                                 /* Restore using INSERT */
606                                 dumpFn = dumpClasses_dumpData;
607                                 copyStmt = NULL;
608                         }
609
610                         ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
611                                                  "TABLE DATA", NULL, "", "",
612                                                  copyStmt, tblinfo[i].usename,
613                                                  dumpFn, dumpCtx);
614                 }
615         }
616 }
617
618
619
620 static int
621 parse_version(const char *versionString)
622 {
623         int                     cnt;
624         int                     vmaj,
625                                 vmin,
626                                 vrev;
627
628         cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev);
629
630         if (cnt < 2)
631         {
632                 write_msg(NULL, "unable to parse version string \"%s\"\n", versionString);
633                 exit(1);
634         }
635
636         if (cnt == 2)
637                 vrev = 0;
638
639         return (100 * vmaj + vmin) * 100 + vrev;
640 }
641
642
643
644 int
645 main(int argc, char **argv)
646 {
647         int                     c;
648         const char *filename = NULL;
649         const char *format = "p";
650         const char *dbname = NULL;
651         const char *pghost = NULL;
652         const char *pgport = NULL;
653         const char *username = NULL;
654         char       *tablename = NULL;
655         bool            oids = false;
656         TableInfo  *tblinfo;
657         int                     numTables;
658         bool            force_password = false;
659         int                     compressLevel = -1;
660         bool            ignore_version = false;
661         int                     plainText = 0;
662         int                     outputClean = 0;
663         int                     outputCreate = 0;
664         int                     outputBlobs = 0;
665         int                     outputNoOwner = 0;
666         int                     outputNoReconnect = 0;
667         static int      use_setsessauth = 0;
668         char       *outputSuperuser = NULL;
669
670         RestoreOptions *ropt;
671
672 #ifdef HAVE_GETOPT_LONG
673         static struct option long_options[] = {
674                 {"data-only", no_argument, NULL, 'a'},
675                 {"blobs", no_argument, NULL, 'b'},
676                 {"clean", no_argument, NULL, 'c'},
677                 {"create", no_argument, NULL, 'C'},
678                 {"file", required_argument, NULL, 'f'},
679                 {"format", required_argument, NULL, 'F'},
680                 {"inserts", no_argument, NULL, 'd'},
681                 {"attribute-inserts", no_argument, NULL, 'D'},
682                 {"column-inserts", no_argument, NULL, 'D'},
683                 {"host", required_argument, NULL, 'h'},
684                 {"ignore-version", no_argument, NULL, 'i'},
685                 {"no-reconnect", no_argument, NULL, 'R'},
686                 {"no-quotes", no_argument, NULL, 'n'},
687                 {"quotes", no_argument, NULL, 'N'},
688                 {"oids", no_argument, NULL, 'o'},
689                 {"no-owner", no_argument, NULL, 'O'},
690                 {"port", required_argument, NULL, 'p'},
691                 {"schema-only", no_argument, NULL, 's'},
692                 {"superuser", required_argument, NULL, 'S'},
693                 {"table", required_argument, NULL, 't'},
694                 {"password", no_argument, NULL, 'W'},
695                 {"username", required_argument, NULL, 'U'},
696                 {"verbose", no_argument, NULL, 'v'},
697                 {"no-privileges", no_argument, NULL, 'x'},
698                 {"no-acl", no_argument, NULL, 'x'},
699                 {"compress", required_argument, NULL, 'Z'},
700                 {"help", no_argument, NULL, '?'},
701                 {"version", no_argument, NULL, 'V'},
702
703                 /*
704                  * the following options don't have an equivalent short option
705                  * letter, but are available as '-X long-name'
706                  */
707                 {"use-set-session-authorization", no_argument, &use_setsessauth, 1}
708         };
709         int                     optindex;
710 #endif
711
712 #ifdef ENABLE_NLS
713         setlocale(LC_ALL, "");
714         bindtextdomain("pg_dump", LOCALEDIR);
715         textdomain("pg_dump");
716 #endif
717
718         g_verbose = false;
719         force_quotes = true;
720
721         strcpy(g_comment_start, "-- ");
722         g_comment_end[0] = '\0';
723         strcpy(g_opaque_type, "opaque");
724
725         dataOnly = schemaOnly = dumpData = attrNames = false;
726
727         if (!strrchr(argv[0], '/'))
728                 progname = argv[0];
729         else
730                 progname = strrchr(argv[0], '/') + 1;
731
732         /* Set default options based on progname */
733         if (strcmp(progname, "pg_backup") == 0)
734         {
735                 format = "c";
736                 outputBlobs = true;
737         }
738
739         if (argc > 1)
740         {
741                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
742                 {
743                         help(progname);
744                         exit(0);
745                 }
746                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
747                 {
748                         puts("pg_dump (PostgreSQL) " PG_VERSION);
749                         exit(0);
750                 }
751         }
752
753 #ifdef HAVE_GETOPT_LONG
754         while ((c = getopt_long(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?", long_options, &optindex)) != -1)
755 #else
756         while ((c = getopt(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?-")) != -1)
757 #endif
758
759         {
760                 switch (c)
761                 {
762                         case 'a':                       /* Dump data only */
763                                 dataOnly = true;
764                                 break;
765
766                         case 'b':                       /* Dump blobs */
767                                 outputBlobs = true;
768                                 break;
769
770                         case 'c':                       /* clean (i.e., drop) schema prior to
771                                                                  * create */
772                                 outputClean = 1;
773                                 break;
774
775                         case 'C':                       /* Create DB */
776
777                                 outputCreate = 1;
778                                 break;
779
780                         case 'd':                       /* dump data as proper insert strings */
781                                 dumpData = true;
782                                 break;
783
784                         case 'D':                       /* dump data as proper insert strings with
785                                                                  * attr names */
786                                 dumpData = true;
787                                 attrNames = true;
788                                 break;
789
790                         case 'f':
791                                 filename = optarg;
792                                 break;
793
794                         case 'F':
795                                 format = optarg;
796                                 break;
797
798                         case 'h':                       /* server host */
799                                 pghost = optarg;
800                                 break;
801
802                         case 'i':                       /* ignore database version mismatch */
803                                 ignore_version = true;
804                                 break;
805
806                         case 'n':                       /* Do not force double-quotes on
807                                                                  * identifiers */
808                                 force_quotes = false;
809                                 break;
810
811                         case 'N':                       /* Force double-quotes on identifiers */
812                                 force_quotes = true;
813                                 break;
814
815                         case 'o':                       /* Dump oids */
816                                 oids = true;
817                                 break;
818
819
820                         case 'O':                       /* Don't reconnect to match owner */
821                                 outputNoOwner = 1;
822                                 break;
823
824                         case 'p':                       /* server port */
825                                 pgport = optarg;
826                                 break;
827
828                         case 'R':                       /* No reconnect */
829                                 outputNoReconnect = 1;
830                                 break;
831
832                         case 's':                       /* dump schema only */
833                                 schemaOnly = true;
834                                 break;
835
836                         case 'S':                       /* Username for superuser in plain text
837                                                                  * output */
838                                 outputSuperuser = strdup(optarg);
839                                 break;
840
841                         case 't':                       /* Dump data for this table only */
842                                 {
843                                         int                     i;
844
845                                         tablename = strdup(optarg);
846
847                                         /*
848                                          * quoted string? Then strip quotes and preserve
849                                          * case...
850                                          */
851                                         if (tablename[0] == '"')
852                                         {
853                                                 strcpy(tablename, &tablename[1]);
854                                                 if (*(tablename + strlen(tablename) - 1) == '"')
855                                                         *(tablename + strlen(tablename) - 1) = '\0';
856                                         }
857                                         /* otherwise, convert table name to lowercase... */
858                                         else
859                                         {
860                                                 for (i = 0; tablename[i]; i++)
861                                                         if (isupper((unsigned char) tablename[i]))
862                                                                 tablename[i] = tolower((unsigned char) tablename[i]);
863
864                                                 /*
865                                                  * '*' is a special case meaning ALL tables, but
866                                                  * only if unquoted
867                                                  */
868                                                 if (strcmp(tablename, "*") == 0)
869                                                         tablename[0] = '\0';
870
871                                         }
872                                 }
873                                 break;
874
875                         case 'u':
876                                 force_password = true;
877                                 username = simple_prompt("User name: ", 100, true);
878                                 break;
879
880                         case 'U':
881                                 username = optarg;
882                                 break;
883
884                         case 'v':                       /* verbose */
885                                 g_verbose = true;
886                                 break;
887
888                         case 'W':
889                                 force_password = true;
890                                 break;
891
892                         case 'x':                       /* skip ACL dump */
893                                 aclsSkip = true;
894                                 break;
895
896                                 /*
897                                  * Option letters were getting scarce, so I invented this
898                                  * new scheme: '-X feature' turns on some feature. Compare
899                                  * to the -f option in GCC.  You should also add an
900                                  * equivalent GNU-style option --feature.  Features that
901                                  * require arguments should use '-X feature=foo'.
902                                  */
903                         case 'X':
904                                 if (strcmp(optarg, "use-set-session-authorization") == 0)
905                                         use_setsessauth = 1;
906                                 else
907                                 {
908                                         fprintf(stderr,
909                                                         gettext("%s: invalid -X option -- %s\n"),
910                                                         progname, optarg);
911                                         fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname);
912                                         exit(1);
913                                 }
914                                 break;
915                         case 'Z':                       /* Compression Level */
916                                 compressLevel = atoi(optarg);
917                                 break;
918
919 #ifndef HAVE_GETOPT_LONG
920                         case '-':
921                                 fprintf(stderr,
922                                                 gettext("%s was compiled without support for long options.\n"
923                                                  "Use --help for help on invocation options.\n"),
924                                                 progname);
925                                 exit(1);
926                                 break;
927 #else
928                                 /* This covers the long options equivalent to -X xxx. */
929                         case 0:
930                                 break;
931 #endif
932                         default:
933                                 fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname);
934                                 exit(1);
935                 }
936         }
937
938         if (optind < (argc - 1))
939         {
940                 fprintf(stderr,
941                         gettext("%s: too many command line options (first is '%s')\n"
942                                         "Try '%s --help' for more information.\n"),
943                                 progname, argv[optind + 1], progname);
944                 exit(1);
945         }
946
947         /* Get the target database name */
948         if (optind < argc)
949                 dbname = argv[optind];
950         else
951                 dbname = getenv("PGDATABASE");
952         if (!dbname)
953         {
954                 write_msg(NULL, "no database name specified\n");
955                 exit(1);
956         }
957
958         if (dataOnly && schemaOnly)
959         {
960                 write_msg(NULL, "The options \"schema only\" (-s) and \"data only\" (-a) cannot be used together.\n");
961                 exit(1);
962         }
963
964         if (outputBlobs && tablename != NULL && strlen(tablename) > 0)
965         {
966                 write_msg(NULL, "Large object output is not supported for a single table.\n");
967                 write_msg(NULL, "Use all tables or a full dump instead.\n");
968                 exit(1);
969         }
970
971         if (dumpData == true && oids == true)
972         {
973                 write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together.\n");
974                 write_msg(NULL, "(The INSERT command cannot set oids.)\n");
975                 exit(1);
976         }
977
978         if (outputBlobs == true && (format[0] == 'p' || format[0] == 'P'))
979         {
980                 write_msg(NULL, "large object output is not supported for plain text dump files.\n");
981                 write_msg(NULL, "(Use a different output format.)\n");
982                 exit(1);
983         }
984
985         /* open the output file */
986         switch (format[0])
987         {
988
989                 case 'c':
990                 case 'C':
991                         g_fout = CreateArchive(filename, archCustom, compressLevel);
992                         break;
993
994                 case 'f':
995                 case 'F':
996                         g_fout = CreateArchive(filename, archFiles, compressLevel);
997                         break;
998
999                 case 'p':
1000                 case 'P':
1001                         plainText = 1;
1002                         g_fout = CreateArchive(filename, archNull, 0);
1003                         break;
1004
1005                 case 't':
1006                 case 'T':
1007                         g_fout = CreateArchive(filename, archTar, compressLevel);
1008                         break;
1009
1010                 default:
1011                         write_msg(NULL, "invalid output format '%s' specified\n", format);
1012                         exit(1);
1013         }
1014
1015         if (g_fout == NULL)
1016         {
1017                 write_msg(NULL, "could not open output file %s for writing\n", filename);
1018                 exit(1);
1019         }
1020
1021         /* Let the archiver know how noisy to be */
1022         g_fout->verbose = g_verbose;
1023
1024         /*
1025          * Open the database using the Archiver, so it knows about it. Errors
1026          * mean death.
1027          */
1028         g_fout->minRemoteVersion = 70000;       /* we can handle back to 7.0 */
1029         g_fout->maxRemoteVersion = parse_version(PG_VERSION);
1030         g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, username, force_password, ignore_version);
1031
1032         /*
1033          * Start serializable transaction to dump consistent data
1034          */
1035         {
1036                 PGresult   *res;
1037
1038                 res = PQexec(g_conn, "begin");
1039                 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1040                         exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
1041                                                   PQerrorMessage(g_conn));
1042
1043                 PQclear(res);
1044                 res = PQexec(g_conn, "set transaction isolation level serializable");
1045                 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1046                         exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
1047                                                   PQerrorMessage(g_conn));
1048
1049                 PQclear(res);
1050         }
1051
1052         if (g_fout->remoteVersion >= 70100)
1053                 g_last_builtin_oid = findLastBuiltinOid_V71(dbname);
1054         else
1055                 g_last_builtin_oid = findLastBuiltinOid_V70();
1056
1057         /* Dump the database definition */
1058         if (!dataOnly)
1059                 dumpDatabase(g_fout);
1060
1061         if (oids == true)
1062                 setMaxOid(g_fout);
1063
1064         if (g_verbose)
1065                 write_msg(NULL, "last built-in oid is %u\n", g_last_builtin_oid);
1066         tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly);
1067
1068         if (!schemaOnly)
1069                 dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes);
1070
1071         if (outputBlobs)
1072                 ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0);
1073
1074         if (!dataOnly)                          /* dump indexes and triggers at the end
1075                                                                  * for performance */
1076         {
1077                 dumpTriggers(g_fout, tablename, tblinfo, numTables);
1078                 dumpRules(g_fout, tablename, tblinfo, numTables);
1079         }
1080
1081         /* Now sort the output nicely */
1082         SortTocByOID(g_fout);
1083         MoveToStart(g_fout, "DATABASE");
1084         MoveToEnd(g_fout, "TABLE DATA");
1085         MoveToEnd(g_fout, "BLOBS");
1086         MoveToEnd(g_fout, "INDEX");
1087         MoveToEnd(g_fout, "CONSTRAINT");
1088         MoveToEnd(g_fout, "TRIGGER");
1089         MoveToEnd(g_fout, "RULE");
1090         MoveToEnd(g_fout, "SEQUENCE SET");
1091
1092         /*
1093          * Moving all comments to end is annoying, but must do it for comments
1094          * on stuff we just moved, and we don't seem to have quite enough
1095          * dependency structure to get it really right...
1096          */
1097         MoveToEnd(g_fout, "COMMENT");
1098
1099         if (plainText)
1100         {
1101                 ropt = NewRestoreOptions();
1102                 ropt->filename = (char *) filename;
1103                 ropt->dropSchema = outputClean;
1104                 ropt->aclsSkip = aclsSkip;
1105                 ropt->superuser = outputSuperuser;
1106                 ropt->create = outputCreate;
1107                 ropt->noOwner = outputNoOwner;
1108                 ropt->noReconnect = outputNoReconnect;
1109                 ropt->use_setsessauth = use_setsessauth;
1110
1111                 if (outputSuperuser)
1112                         ropt->superuser = outputSuperuser;
1113                 else
1114                         ropt->superuser = PQuser(g_conn);
1115
1116                 if (compressLevel == -1)
1117                         ropt->compression = 0;
1118                 else
1119                         ropt->compression = compressLevel;
1120
1121                 ropt->suppressDumpWarnings = true;              /* We've already shown
1122                                                                                                  * them */
1123
1124                 RestoreArchive(g_fout, ropt);
1125         }
1126
1127         CloseArchive(g_fout);
1128
1129         clearTableInfo(tblinfo, numTables);
1130         PQfinish(g_conn);
1131         exit(0);
1132 }
1133
1134 /*
1135  * dumpDatabase:
1136  *      dump the database definition
1137  *
1138  */
1139 static int
1140 dumpDatabase(Archive *AH)
1141 {
1142         PQExpBuffer dbQry = createPQExpBuffer();
1143         PQExpBuffer delQry = createPQExpBuffer();
1144         PQExpBuffer creaQry = createPQExpBuffer();
1145         PGresult   *res;
1146         int                     ntups;
1147         int                     i_dba,
1148                                 i_encoding,
1149                                 i_datpath;
1150         const char *datname,
1151                            *dba,
1152                            *encoding,
1153                            *datpath;
1154
1155         datname = PQdb(g_conn);
1156
1157         if (g_verbose)
1158                 write_msg(NULL, "saving database definition\n");
1159
1160         /* Get the database owner and parameters from pg_database */
1161         appendPQExpBuffer(dbQry, "select (select usename from pg_user where usesysid = datdba) as dba,"
1162                                           " encoding, datpath from pg_database"
1163                                           " where datname = ");
1164         formatStringLiteral(dbQry, datname, CONV_ALL);
1165
1166         res = PQexec(g_conn, dbQry->data);
1167         if (!res ||
1168                 PQresultStatus(res) != PGRES_TUPLES_OK)
1169         {
1170                 write_msg(NULL, "SQL command failed\n");
1171                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
1172                 write_msg(NULL, "The command was: %s\n", dbQry->data);
1173                 exit_nicely();
1174         }
1175
1176         ntups = PQntuples(res);
1177
1178         if (ntups <= 0)
1179         {
1180                 write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
1181                                   datname);
1182                 exit_nicely();
1183         }
1184
1185         if (ntups != 1)
1186         {
1187                 write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
1188                                   ntups, datname);
1189                 exit_nicely();
1190         }
1191
1192         i_dba = PQfnumber(res, "dba");
1193         i_encoding = PQfnumber(res, "encoding");
1194         i_datpath = PQfnumber(res, "datpath");
1195         dba = PQgetvalue(res, 0, i_dba);
1196         encoding = PQgetvalue(res, 0, i_encoding);
1197         datpath = PQgetvalue(res, 0, i_datpath);
1198
1199         appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
1200                                           fmtId(datname, force_quotes));
1201         if (strlen(encoding) > 0)
1202                 appendPQExpBuffer(creaQry, " ENCODING = %s", encoding);
1203         if (strlen(datpath) > 0)
1204                 appendPQExpBuffer(creaQry, " LOCATION = '%s'", datpath);
1205         appendPQExpBuffer(creaQry, ";\n");
1206
1207         appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
1208                                           fmtId(datname, force_quotes));
1209
1210         ArchiveEntry(AH, "0" /* OID */ , datname /* Name */ , "DATABASE", NULL,
1211                                  creaQry->data /* Create */ , delQry->data /* Del */ ,
1212                                  "" /* Copy */ , dba /* Owner */ ,
1213                                  NULL /* Dumper */ , NULL /* Dumper Arg */ );
1214
1215         PQclear(res);
1216
1217         destroyPQExpBuffer(dbQry);
1218         destroyPQExpBuffer(delQry);
1219         destroyPQExpBuffer(creaQry);
1220
1221         return 1;
1222 }
1223
1224
1225 /*
1226  * dumpBlobs:
1227  *      dump all blobs
1228  *
1229  */
1230
1231 #define loBufSize 16384
1232 #define loFetchSize 1000
1233
1234 static int
1235 dumpBlobs(Archive *AH, char *junkOid, void *junkVal)
1236 {
1237         PQExpBuffer oidQry = createPQExpBuffer();
1238         PQExpBuffer oidFetchQry = createPQExpBuffer();
1239         PGresult   *res;
1240         int                     i;
1241         int                     loFd;
1242         char            buf[loBufSize];
1243         int                     cnt;
1244         Oid                     blobOid;
1245
1246         if (g_verbose)
1247                 write_msg(NULL, "saving large objects\n");
1248
1249         /* Cursor to get all BLOB tables */
1250         if (AH->remoteVersion >= 70100)
1251                 appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT DISTINCT loid FROM pg_largeobject");
1252         else
1253                 appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
1254
1255         res = PQexec(g_conn, oidQry->data);
1256         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1257         {
1258                 write_msg(NULL, "dumpBlobs(): cursor declaration failed: %s", PQerrorMessage(g_conn));
1259                 exit_nicely();
1260         }
1261
1262         /* Fetch for cursor */
1263         appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
1264
1265         do
1266         {
1267                 /* Do a fetch */
1268                 PQclear(res);
1269                 res = PQexec(g_conn, oidFetchQry->data);
1270
1271                 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
1272                 {
1273                         write_msg(NULL, "dumpBlobs(): fetch from cursor failed: %s",
1274                                           PQerrorMessage(g_conn));
1275                         exit_nicely();
1276                 }
1277
1278                 /* Process the tuples, if any */
1279                 for (i = 0; i < PQntuples(res); i++)
1280                 {
1281                         blobOid = atooid(PQgetvalue(res, i, 0));
1282                         /* Open the BLOB */
1283                         loFd = lo_open(g_conn, blobOid, INV_READ);
1284                         if (loFd == -1)
1285                         {
1286                                 write_msg(NULL, "dumpBlobs(): could not open large object: %s",
1287                                                   PQerrorMessage(g_conn));
1288                                 exit_nicely();
1289                         }
1290
1291                         StartBlob(AH, blobOid);
1292
1293                         /* Now read it in chunks, sending data to archive */
1294                         do
1295                         {
1296                                 cnt = lo_read(g_conn, loFd, buf, loBufSize);
1297                                 if (cnt < 0)
1298                                 {
1299                                         write_msg(NULL, "dumpBlobs(): error reading large object: %s",
1300                                                           PQerrorMessage(g_conn));
1301                                         exit_nicely();
1302                                 }
1303
1304                                 WriteData(AH, buf, cnt);
1305
1306                         } while (cnt > 0);
1307
1308                         lo_close(g_conn, loFd);
1309
1310                         EndBlob(AH, blobOid);
1311
1312                 }
1313         } while (PQntuples(res) > 0);
1314
1315         destroyPQExpBuffer(oidQry);
1316         destroyPQExpBuffer(oidFetchQry);
1317
1318         return 1;
1319 }
1320
1321 /*
1322  * getTypes:
1323  *        read all base types in the system catalogs and return them in the
1324  * TypeInfo* structure
1325  *
1326  *      numTypes is set to the number of types read in
1327  *
1328  */
1329 TypeInfo *
1330 getTypes(int *numTypes)
1331 {
1332         PGresult   *res;
1333         int                     ntups;
1334         int                     i;
1335         PQExpBuffer query = createPQExpBuffer();
1336         TypeInfo   *tinfo;
1337
1338         int                     i_oid;
1339         int                     i_typowner;
1340         int                     i_typname;
1341         int                     i_typlen;
1342         int                     i_typprtlen;
1343         int                     i_typinput;
1344         int                     i_typoutput;
1345         int                     i_typreceive;
1346         int                     i_typsend;
1347         int                     i_typelem;
1348         int                     i_typdelim;
1349         int                     i_typdefault;
1350         int                     i_typrelid;
1351         int                     i_typalign;
1352         int                     i_typstorage;
1353         int                     i_typbyval;
1354         int                     i_typisdefined;
1355         int                     i_usename;
1356         int                     i_typedefn;
1357         int                     i_typtype;
1358
1359         /* find all base types */
1360
1361         /*
1362          * we include even the built-in types because those may be used as
1363          * array elements by user-defined types
1364          *
1365          * we filter out the built-in types when we dump out the types
1366          */
1367
1368         if (g_fout->remoteVersion < 70100)
1369         {
1370                 appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, "
1371                   "typinput, typoutput, typreceive, typsend, typelem, typdelim, "
1372                                                   "typdefault, typrelid, typalign, 'p'::char as typstorage, typbyval, typisdefined, "
1373                                                   "(select usename from pg_user where typowner = usesysid) as usename, "
1374                                                   "typname as typedefn, typtype "
1375                                                   "from pg_type");
1376         }
1377         else
1378         {
1379                 appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, "
1380                   "typinput, typoutput, typreceive, typsend, typelem, typdelim, "
1381                                                   "typdefault, typrelid, typalign, typstorage, typbyval, typisdefined, "
1382                                                   "(select usename from pg_user where typowner = usesysid) as usename, "
1383                                                   "format_type(pg_type.oid, NULL) as typedefn, typtype "
1384                                                   "from pg_type");
1385         }
1386
1387         res = PQexec(g_conn, query->data);
1388         if (!res ||
1389                 PQresultStatus(res) != PGRES_TUPLES_OK)
1390         {
1391                 write_msg(NULL, "query to obtain list of data types failed: %s", PQerrorMessage(g_conn));
1392                 exit_nicely();
1393         }
1394
1395         ntups = PQntuples(res);
1396
1397         tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
1398
1399         i_oid = PQfnumber(res, "oid");
1400         i_typowner = PQfnumber(res, "typowner");
1401         i_typname = PQfnumber(res, "typname");
1402         i_typlen = PQfnumber(res, "typlen");
1403         i_typprtlen = PQfnumber(res, "typprtlen");
1404         i_typinput = PQfnumber(res, "typinput");
1405         i_typoutput = PQfnumber(res, "typoutput");
1406         i_typreceive = PQfnumber(res, "typreceive");
1407         i_typsend = PQfnumber(res, "typsend");
1408         i_typelem = PQfnumber(res, "typelem");
1409         i_typdelim = PQfnumber(res, "typdelim");
1410         i_typdefault = PQfnumber(res, "typdefault");
1411         i_typrelid = PQfnumber(res, "typrelid");
1412         i_typalign = PQfnumber(res, "typalign");
1413         i_typstorage = PQfnumber(res, "typstorage");
1414         i_typbyval = PQfnumber(res, "typbyval");
1415         i_typisdefined = PQfnumber(res, "typisdefined");
1416         i_usename = PQfnumber(res, "usename");
1417         i_typedefn = PQfnumber(res, "typedefn");
1418         i_typtype = PQfnumber(res, "typtype");
1419
1420         for (i = 0; i < ntups; i++)
1421         {
1422                 tinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1423                 tinfo[i].typowner = strdup(PQgetvalue(res, i, i_typowner));
1424                 tinfo[i].typname = strdup(PQgetvalue(res, i, i_typname));
1425                 tinfo[i].typlen = strdup(PQgetvalue(res, i, i_typlen));
1426                 tinfo[i].typprtlen = strdup(PQgetvalue(res, i, i_typprtlen));
1427                 tinfo[i].typinput = strdup(PQgetvalue(res, i, i_typinput));
1428                 tinfo[i].typoutput = strdup(PQgetvalue(res, i, i_typoutput));
1429                 tinfo[i].typreceive = strdup(PQgetvalue(res, i, i_typreceive));
1430                 tinfo[i].typsend = strdup(PQgetvalue(res, i, i_typsend));
1431                 tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
1432                 tinfo[i].typdelim = strdup(PQgetvalue(res, i, i_typdelim));
1433                 if (PQgetisnull(res, i, i_typdefault))
1434                         tinfo[i].typdefault = NULL;
1435                 else
1436                         tinfo[i].typdefault = strdup(PQgetvalue(res, i, i_typdefault));
1437                 tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
1438                 tinfo[i].typalign = strdup(PQgetvalue(res, i, i_typalign));
1439                 tinfo[i].typstorage = strdup(PQgetvalue(res, i, i_typstorage));
1440                 tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1441                 tinfo[i].typedefn = strdup(PQgetvalue(res, i, i_typedefn));
1442                 tinfo[i].typtype = strdup(PQgetvalue(res, i, i_typtype));
1443
1444                 if (strlen(tinfo[i].usename) == 0)
1445                         write_msg(NULL, "WARNING: owner of data type %s appears to be invalid\n",
1446                                           tinfo[i].typname);
1447
1448                 if (strcmp(PQgetvalue(res, i, i_typbyval), "f") == 0)
1449                         tinfo[i].passedbyvalue = 0;
1450                 else
1451                         tinfo[i].passedbyvalue = 1;
1452
1453                 /*
1454                  * check for user-defined array types, omit system generated ones
1455                  */
1456                 if ((strcmp(tinfo[i].typelem, "0") != 0) &&
1457                         tinfo[i].typname[0] != '_')
1458                         tinfo[i].isArray = 1;
1459                 else
1460                         tinfo[i].isArray = 0;
1461
1462                 if (strcmp(PQgetvalue(res, i, i_typisdefined), "f") == 0)
1463                         tinfo[i].isDefined = 0;
1464                 else
1465                         tinfo[i].isDefined = 1;
1466         }
1467
1468         *numTypes = ntups;
1469
1470         PQclear(res);
1471
1472         destroyPQExpBuffer(query);
1473
1474         return tinfo;
1475 }
1476
1477 /*
1478  * getOperators:
1479  *        read all operators in the system catalogs and return them in the
1480  * OprInfo* structure
1481  *
1482  *      numOprs is set to the number of operators read in
1483  */
1484 OprInfo *
1485 getOperators(int *numOprs)
1486 {
1487         PGresult   *res;
1488         int                     ntups;
1489         int                     i;
1490         PQExpBuffer query = createPQExpBuffer();
1491
1492         OprInfo    *oprinfo;
1493
1494         int                     i_oid;
1495         int                     i_oprname;
1496         int                     i_oprkind;
1497         int                     i_oprcode;
1498         int                     i_oprleft;
1499         int                     i_oprright;
1500         int                     i_oprcom;
1501         int                     i_oprnegate;
1502         int                     i_oprrest;
1503         int                     i_oprjoin;
1504         int                     i_oprcanhash;
1505         int                     i_oprlsortop;
1506         int                     i_oprrsortop;
1507         int                     i_usename;
1508
1509         /*
1510          * find all operators, including builtin operators, filter out
1511          * system-defined operators at dump-out time
1512          */
1513
1514         appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, oprkind, oprcode, "
1515                            "oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, "
1516                                           "oprcanhash, oprlsortop, oprrsortop, "
1517         "(select usename from pg_user where oprowner = usesysid) as usename "
1518                                           "from pg_operator");
1519
1520         res = PQexec(g_conn, query->data);
1521         if (!res ||
1522                 PQresultStatus(res) != PGRES_TUPLES_OK)
1523         {
1524                 write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
1525                 exit_nicely();
1526         }
1527
1528         ntups = PQntuples(res);
1529         *numOprs = ntups;
1530
1531         oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
1532
1533         i_oid = PQfnumber(res, "oid");
1534         i_oprname = PQfnumber(res, "oprname");
1535         i_oprkind = PQfnumber(res, "oprkind");
1536         i_oprcode = PQfnumber(res, "oprcode");
1537         i_oprleft = PQfnumber(res, "oprleft");
1538         i_oprright = PQfnumber(res, "oprright");
1539         i_oprcom = PQfnumber(res, "oprcom");
1540         i_oprnegate = PQfnumber(res, "oprnegate");
1541         i_oprrest = PQfnumber(res, "oprrest");
1542         i_oprjoin = PQfnumber(res, "oprjoin");
1543         i_oprcanhash = PQfnumber(res, "oprcanhash");
1544         i_oprlsortop = PQfnumber(res, "oprlsortop");
1545         i_oprrsortop = PQfnumber(res, "oprrsortop");
1546         i_usename = PQfnumber(res, "usename");
1547
1548         for (i = 0; i < ntups; i++)
1549         {
1550                 oprinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1551                 oprinfo[i].oprname = strdup(PQgetvalue(res, i, i_oprname));
1552                 oprinfo[i].oprkind = strdup(PQgetvalue(res, i, i_oprkind));
1553                 oprinfo[i].oprcode = strdup(PQgetvalue(res, i, i_oprcode));
1554                 oprinfo[i].oprleft = strdup(PQgetvalue(res, i, i_oprleft));
1555                 oprinfo[i].oprright = strdup(PQgetvalue(res, i, i_oprright));
1556                 oprinfo[i].oprcom = strdup(PQgetvalue(res, i, i_oprcom));
1557                 oprinfo[i].oprnegate = strdup(PQgetvalue(res, i, i_oprnegate));
1558                 oprinfo[i].oprrest = strdup(PQgetvalue(res, i, i_oprrest));
1559                 oprinfo[i].oprjoin = strdup(PQgetvalue(res, i, i_oprjoin));
1560                 oprinfo[i].oprcanhash = strdup(PQgetvalue(res, i, i_oprcanhash));
1561                 oprinfo[i].oprlsortop = strdup(PQgetvalue(res, i, i_oprlsortop));
1562                 oprinfo[i].oprrsortop = strdup(PQgetvalue(res, i, i_oprrsortop));
1563                 oprinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1564
1565                 if (strlen(oprinfo[i].usename) == 0)
1566                         write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
1567                                           oprinfo[i].oprname);
1568
1569         }
1570
1571         PQclear(res);
1572
1573         destroyPQExpBuffer(query);
1574
1575         return oprinfo;
1576 }
1577
1578 void
1579 clearTypeInfo(TypeInfo *tp, int numTypes)
1580 {
1581         int                     i;
1582
1583         for (i = 0; i < numTypes; ++i)
1584         {
1585                 if (tp[i].oid)
1586                         free(tp[i].oid);
1587                 if (tp[i].typowner)
1588                         free(tp[i].typowner);
1589                 if (tp[i].typname)
1590                         free(tp[i].typname);
1591                 if (tp[i].typlen)
1592                         free(tp[i].typlen);
1593                 if (tp[i].typprtlen)
1594                         free(tp[i].typprtlen);
1595                 if (tp[i].typinput)
1596                         free(tp[i].typinput);
1597                 if (tp[i].typoutput)
1598                         free(tp[i].typoutput);
1599                 if (tp[i].typreceive)
1600                         free(tp[i].typreceive);
1601                 if (tp[i].typsend)
1602                         free(tp[i].typsend);
1603                 if (tp[i].typelem)
1604                         free(tp[i].typelem);
1605                 if (tp[i].typdelim)
1606                         free(tp[i].typdelim);
1607                 if (tp[i].typdefault)
1608                         free(tp[i].typdefault);
1609                 if (tp[i].typrelid)
1610                         free(tp[i].typrelid);
1611                 if (tp[i].typalign)
1612                         free(tp[i].typalign);
1613                 if (tp[i].typstorage)
1614                         free(tp[i].typstorage);
1615                 if (tp[i].usename)
1616                         free(tp[i].usename);
1617                 if (tp[i].typedefn)
1618                         free(tp[i].typedefn);
1619         }
1620         free(tp);
1621 }
1622
1623 void
1624 clearFuncInfo(FuncInfo *fun, int numFuncs)
1625 {
1626         int                     i,
1627                                 a;
1628
1629         if (!fun)
1630                 return;
1631         for (i = 0; i < numFuncs; ++i)
1632         {
1633                 if (fun[i].oid)
1634                         free(fun[i].oid);
1635                 if (fun[i].proname)
1636                         free(fun[i].proname);
1637                 if (fun[i].usename)
1638                         free(fun[i].usename);
1639                 if (fun[i].argtypes)
1640                 {
1641                         for (a = 0; a < fun[i].nargs; ++a)
1642                                 if (fun[i].argtypes[a])
1643                                         free(fun[i].argtypes[a]);
1644                         free(fun[i].argtypes);
1645                 }
1646                 if (fun[i].prorettype)
1647                         free(fun[i].prorettype);
1648                 if (fun[i].prosrc)
1649                         free(fun[i].prosrc);
1650                 if (fun[i].probin)
1651                         free(fun[i].probin);
1652         }
1653         free(fun);
1654 }
1655
1656 static void
1657 clearTableInfo(TableInfo *tblinfo, int numTables)
1658 {
1659         int                     i,
1660                                 j;
1661
1662         for (i = 0; i < numTables; ++i)
1663         {
1664
1665                 if (tblinfo[i].oid)
1666                         free(tblinfo[i].oid);
1667                 if (tblinfo[i].relacl)
1668                         free(tblinfo[i].relacl);
1669                 if (tblinfo[i].usename)
1670                         free(tblinfo[i].usename);
1671
1672                 if (tblinfo[i].relname)
1673                         free(tblinfo[i].relname);
1674
1675                 if (tblinfo[i].relkind == RELKIND_SEQUENCE)
1676                         continue;
1677
1678                 /* Process Attributes */
1679                 for (j = 0; j < tblinfo[i].numatts; j++)
1680                 {
1681                         if (tblinfo[i].attnames[j])
1682                                 free(tblinfo[i].attnames[j]);
1683                         if (tblinfo[i].typnames[j])
1684                                 free(tblinfo[i].typnames[j]);
1685                 }
1686
1687                 if (tblinfo[i].triggers)
1688                 {
1689                         for (j = 0; j < tblinfo[i].ntrig; j++)
1690                         {
1691                                 if (tblinfo[i].triggers[j].tgsrc)
1692                                         free(tblinfo[i].triggers[j].tgsrc);
1693                                 if (tblinfo[i].triggers[j].oid)
1694                                         free(tblinfo[i].triggers[j].oid);
1695                                 if (tblinfo[i].triggers[j].tgname)
1696                                         free(tblinfo[i].triggers[j].tgname);
1697                                 if (tblinfo[i].triggers[j].tgdel)
1698                                         free(tblinfo[i].triggers[j].tgdel);
1699                         }
1700                         free(tblinfo[i].triggers);
1701                 }
1702
1703                 if (tblinfo[i].atttypmod)
1704                         free((int *) tblinfo[i].atttypmod);
1705                 if (tblinfo[i].inhAttrs)
1706                         free((int *) tblinfo[i].inhAttrs);
1707                 if (tblinfo[i].inhAttrDef)
1708                         free((int *) tblinfo[i].inhAttrDef);
1709                 if (tblinfo[i].inhNotNull)
1710                         free((int *) tblinfo[i].inhNotNull);
1711                 if (tblinfo[i].attnames)
1712                         free(tblinfo[i].attnames);
1713                 if (tblinfo[i].atttypedefns)
1714                         free(tblinfo[i].atttypedefns);
1715                 if (tblinfo[i].typnames)
1716                         free(tblinfo[i].typnames);
1717                 if (tblinfo[i].notnull)
1718                         free(tblinfo[i].notnull);
1719                 if (tblinfo[i].primary_key_name)
1720                         free(tblinfo[i].primary_key_name);
1721         }
1722         free(tblinfo);
1723 }
1724
1725 void
1726 clearInhInfo(InhInfo *inh, int numInherits)
1727 {
1728         int                     i;
1729
1730         if (!inh)
1731                 return;
1732         for (i = 0; i < numInherits; ++i)
1733         {
1734                 if (inh[i].inhrelid)
1735                         free(inh[i].inhrelid);
1736                 if (inh[i].inhparent)
1737                         free(inh[i].inhparent);
1738         }
1739         free(inh);
1740 }
1741
1742 void
1743 clearOprInfo(OprInfo *opr, int numOprs)
1744 {
1745         int                     i;
1746
1747         if (!opr)
1748                 return;
1749         for (i = 0; i < numOprs; ++i)
1750         {
1751                 if (opr[i].oid)
1752                         free(opr[i].oid);
1753                 if (opr[i].oprname)
1754                         free(opr[i].oprname);
1755                 if (opr[i].oprkind)
1756                         free(opr[i].oprkind);
1757                 if (opr[i].oprcode)
1758                         free(opr[i].oprcode);
1759                 if (opr[i].oprleft)
1760                         free(opr[i].oprleft);
1761                 if (opr[i].oprright)
1762                         free(opr[i].oprright);
1763                 if (opr[i].oprcom)
1764                         free(opr[i].oprcom);
1765                 if (opr[i].oprnegate)
1766                         free(opr[i].oprnegate);
1767                 if (opr[i].oprrest)
1768                         free(opr[i].oprrest);
1769                 if (opr[i].oprjoin)
1770                         free(opr[i].oprjoin);
1771                 if (opr[i].oprcanhash)
1772                         free(opr[i].oprcanhash);
1773                 if (opr[i].oprlsortop)
1774                         free(opr[i].oprlsortop);
1775                 if (opr[i].oprrsortop)
1776                         free(opr[i].oprrsortop);
1777                 if (opr[i].usename)
1778                         free(opr[i].usename);
1779         }
1780         free(opr);
1781 }
1782
1783 void
1784 clearIndInfo(IndInfo *ind, int numIndexes)
1785 {
1786         int                     i,
1787                                 a;
1788
1789         if (!ind)
1790                 return;
1791         for (i = 0; i < numIndexes; ++i)
1792         {
1793                 if (ind[i].indexreloid)
1794                         free(ind[i].indexreloid);
1795                 if (ind[i].indreloid)
1796                         free(ind[i].indreloid);
1797                 if (ind[i].indexrelname)
1798                         free(ind[i].indexrelname);
1799                 if (ind[i].indrelname)
1800                         free(ind[i].indrelname);
1801                 if (ind[i].indexdef)
1802                         free(ind[i].indexdef);
1803                 if (ind[i].indisprimary)
1804                         free(ind[i].indisprimary);
1805                 if (ind[i].indkey)
1806                 {
1807                         for (a = 0; a < ind[i].indnkeys; ++a)
1808                                 if (ind[i].indkey[a])
1809                                         free(ind[i].indkey[a]);
1810                         free(ind[i].indkey);
1811                 }
1812         }
1813         free(ind);
1814 }
1815
1816 void
1817 clearAggInfo(AggInfo *agginfo, int numArgs)
1818 {
1819         int                     i;
1820
1821         if (!agginfo)
1822                 return;
1823         for (i = 0; i < numArgs; ++i)
1824         {
1825                 if (agginfo[i].oid)
1826                         free(agginfo[i].oid);
1827                 if (agginfo[i].aggname)
1828                         free(agginfo[i].aggname);
1829                 if (agginfo[i].aggtransfn)
1830                         free(agginfo[i].aggtransfn);
1831                 if (agginfo[i].aggfinalfn)
1832                         free(agginfo[i].aggfinalfn);
1833                 if (agginfo[i].aggtranstype)
1834                         free(agginfo[i].aggtranstype);
1835                 if (agginfo[i].aggbasetype)
1836                         free(agginfo[i].aggbasetype);
1837                 if (agginfo[i].agginitval)
1838                         free(agginfo[i].agginitval);
1839                 if (agginfo[i].usename)
1840                         free(agginfo[i].usename);
1841         }
1842         free(agginfo);
1843 }
1844
1845 /*
1846  * getAggregates:
1847  *        read all the user-defined aggregates in the system catalogs and
1848  * return them in the AggInfo* structure
1849  *
1850  * numAggs is set to the number of aggregates read in
1851  */
1852 AggInfo *
1853 getAggregates(int *numAggs)
1854 {
1855         PGresult   *res;
1856         int                     ntups;
1857         int                     i;
1858         PQExpBuffer query = createPQExpBuffer();
1859         AggInfo    *agginfo;
1860
1861         int                     i_oid;
1862         int                     i_aggname;
1863         int                     i_aggtransfn;
1864         int                     i_aggfinalfn;
1865         int                     i_aggtranstype;
1866         int                     i_aggbasetype;
1867         int                     i_agginitval;
1868         int                     i_usename;
1869         int                     i_convertok;
1870
1871         /* find all user-defined aggregates */
1872
1873         if (g_fout->remoteVersion < 70100)
1874         {
1875                 appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn1 as aggtransfn, "
1876                            "aggfinalfn, aggtranstype1 as aggtranstype, aggbasetype, "
1877                                                   "agginitval1 as agginitval, "
1878                                                   "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok, "
1879                                                   "(select usename from pg_user where aggowner = usesysid) as usename "
1880                                                   "from pg_aggregate");
1881         }
1882         else if (g_fout->remoteVersion < 70300)
1883         {
1884                 appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn, "
1885                                                   "aggfinalfn, aggtranstype, aggbasetype, "
1886                                                   "agginitval, "
1887                                                   "'t'::boolean as convertok, "
1888                                                   "(select usename from pg_user where aggowner = usesysid) as usename "
1889                                                   "from pg_aggregate");
1890         }
1891         else
1892         {
1893                 appendPQExpBuffer(query, "SELECT p.oid, proname as aggname, aggtransfn, "
1894                                                   "aggfinalfn, aggtranstype, proargtypes[0] as aggbasetype, "
1895                                                   "agginitval, "
1896                                                   "'t'::boolean as convertok, "
1897                                                   "(select usename from pg_user where proowner = usesysid) as usename "
1898                                                   "from pg_aggregate a, pg_proc p "
1899                                                   "where a.aggfnoid = p.oid");
1900         }
1901
1902         res = PQexec(g_conn, query->data);
1903         if (!res ||
1904                 PQresultStatus(res) != PGRES_TUPLES_OK)
1905         {
1906                 write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
1907                                   PQerrorMessage(g_conn));
1908                 exit_nicely();
1909         }
1910
1911         ntups = PQntuples(res);
1912         *numAggs = ntups;
1913
1914         agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
1915
1916         i_oid = PQfnumber(res, "oid");
1917         i_aggname = PQfnumber(res, "aggname");
1918         i_aggtransfn = PQfnumber(res, "aggtransfn");
1919         i_aggfinalfn = PQfnumber(res, "aggfinalfn");
1920         i_aggtranstype = PQfnumber(res, "aggtranstype");
1921         i_aggbasetype = PQfnumber(res, "aggbasetype");
1922         i_agginitval = PQfnumber(res, "agginitval");
1923         i_usename = PQfnumber(res, "usename");
1924         i_convertok = PQfnumber(res, "convertok");
1925
1926         for (i = 0; i < ntups; i++)
1927         {
1928                 agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1929                 agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname));
1930                 agginfo[i].aggtransfn = strdup(PQgetvalue(res, i, i_aggtransfn));
1931                 agginfo[i].aggfinalfn = strdup(PQgetvalue(res, i, i_aggfinalfn));
1932                 agginfo[i].aggtranstype = strdup(PQgetvalue(res, i, i_aggtranstype));
1933                 agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype));
1934                 agginfo[i].agginitval = strdup(PQgetvalue(res, i, i_agginitval));
1935                 agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1936                 if (strlen(agginfo[i].usename) == 0)
1937                         write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
1938                                           agginfo[i].aggname);
1939
1940                 agginfo[i].convertok = (PQgetvalue(res, i, i_convertok)[0] == 't');
1941
1942         }
1943
1944         PQclear(res);
1945
1946         destroyPQExpBuffer(query);
1947
1948         return agginfo;
1949 }
1950
1951 /*
1952  * getFuncs:
1953  *        read all the user-defined functions in the system catalogs and
1954  * return them in the FuncInfo* structure
1955  *
1956  * numFuncs is set to the number of functions read in
1957  *
1958  *
1959  */
1960 FuncInfo *
1961 getFuncs(int *numFuncs)
1962 {
1963         PGresult   *res;
1964         int                     ntups;
1965         int                     i;
1966         PQExpBuffer query = createPQExpBuffer();
1967         FuncInfo   *finfo;
1968
1969         int                     i_oid;
1970         int                     i_proname;
1971         int                     i_prolang;
1972         int                     i_pronargs;
1973         int                     i_proargtypes;
1974         int                     i_prorettype;
1975         int                     i_proretset;
1976         int                     i_prosrc;
1977         int                     i_probin;
1978         int                     i_provolatile;
1979         int                     i_isimplicit;
1980         int                     i_isstrict;
1981         int                     i_usename;
1982
1983         /* find all user-defined funcs */
1984
1985         if (g_fout->remoteVersion < 70100)
1986         {
1987                 appendPQExpBuffer(query,
1988                    "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, "
1989                                                   "proretset, proargtypes, prosrc, probin, "
1990                                                   "(select usename from pg_user where proowner = usesysid) as usename, "
1991                                                   "case when proiscachable then 'i' else 'v' end as provolatile, "
1992                                                   "'f'::boolean as proimplicit, "
1993                                                   "'f'::boolean as proisstrict "
1994                                                   "from pg_proc "
1995                                                   "where pg_proc.oid > '%u'::oid",
1996                                                   g_last_builtin_oid);
1997         }
1998         else if (g_fout->remoteVersion < 70300)
1999         {
2000                 appendPQExpBuffer(query,
2001                    "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, "
2002                                                   "proretset, proargtypes, prosrc, probin, "
2003                                                   "(select usename from pg_user where proowner = usesysid) as usename, "
2004                                                   "case when proiscachable then 'i' else 'v' end as provolatile, "
2005                                                   "'f'::boolean as proimplicit, "
2006                                                   "proisstrict "
2007                                                   "from pg_proc "
2008                                                   "where pg_proc.oid > '%u'::oid",
2009                                                   g_last_builtin_oid);
2010         }
2011         else
2012         {
2013                 appendPQExpBuffer(query,
2014                    "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, "
2015                                                   "proretset, proargtypes, prosrc, probin, "
2016                                                   "(select usename from pg_user where proowner = usesysid) as usename, "
2017                                                   "provolatile, proimplicit, proisstrict "
2018                                                   "from pg_proc "
2019                                                   "where pg_proc.oid > '%u'::oid and not proisagg",
2020                                                   g_last_builtin_oid);
2021         }
2022
2023         res = PQexec(g_conn, query->data);
2024         if (!res ||
2025                 PQresultStatus(res) != PGRES_TUPLES_OK)
2026         {
2027                 write_msg(NULL, "query to obtain list of functions failed: %s",
2028                                   PQerrorMessage(g_conn));
2029                 exit_nicely();
2030         }
2031
2032         ntups = PQntuples(res);
2033
2034         *numFuncs = ntups;
2035
2036         finfo = (FuncInfo *) malloc(ntups * sizeof(FuncInfo));
2037
2038         memset((char *) finfo, 0, ntups * sizeof(FuncInfo));
2039
2040         i_oid = PQfnumber(res, "oid");
2041         i_proname = PQfnumber(res, "proname");
2042         i_prolang = PQfnumber(res, "prolang");
2043         i_pronargs = PQfnumber(res, "pronargs");
2044         i_proargtypes = PQfnumber(res, "proargtypes");
2045         i_prorettype = PQfnumber(res, "prorettype");
2046         i_proretset = PQfnumber(res, "proretset");
2047         i_prosrc = PQfnumber(res, "prosrc");
2048         i_probin = PQfnumber(res, "probin");
2049         i_provolatile = PQfnumber(res, "provolatile");
2050         i_isimplicit = PQfnumber(res, "proimplicit");
2051         i_isstrict = PQfnumber(res, "proisstrict");
2052         i_usename = PQfnumber(res, "usename");
2053
2054         for (i = 0; i < ntups; i++)
2055         {
2056                 finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
2057                 finfo[i].proname = strdup(PQgetvalue(res, i, i_proname));
2058
2059                 finfo[i].prosrc = strdup(PQgetvalue(res, i, i_prosrc));
2060                 finfo[i].probin = strdup(PQgetvalue(res, i, i_probin));
2061
2062                 finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
2063                 finfo[i].retset = (strcmp(PQgetvalue(res, i, i_proretset), "t") == 0);
2064                 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
2065                 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
2066                 finfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
2067                 finfo[i].provolatile = (PQgetvalue(res, i, i_provolatile))[0];
2068                 finfo[i].isimplicit = (strcmp(PQgetvalue(res, i, i_isimplicit), "t") == 0);
2069                 finfo[i].isstrict = (strcmp(PQgetvalue(res, i, i_isstrict), "t") == 0);
2070
2071                 if (strlen(finfo[i].usename) == 0)
2072                         write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n",
2073                                           finfo[i].proname);
2074
2075                 finfo[i].argtypes = malloc(finfo[i].nargs * sizeof(finfo[i].argtypes[0]));
2076                 parseNumericArray(PQgetvalue(res, i, i_proargtypes),
2077                                                   finfo[i].argtypes,
2078                                                   finfo[i].nargs);
2079                 finfo[i].dumped = 0;
2080         }
2081
2082         PQclear(res);
2083
2084         destroyPQExpBuffer(query);
2085
2086         return finfo;
2087 }
2088
2089 /*
2090  * getTables
2091  *        read all the user-defined tables (no indexes, no catalogs)
2092  * in the system catalogs return them in the TableInfo* structure
2093  *
2094  * numTables is set to the number of tables read in
2095  */
2096 TableInfo *
2097 getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename)
2098 {
2099         PGresult   *res;
2100         int                     ntups;
2101         int                     i;
2102         PQExpBuffer query = createPQExpBuffer();
2103         PQExpBuffer delqry = createPQExpBuffer();
2104         PQExpBuffer lockquery = createPQExpBuffer();
2105         TableInfo  *tblinfo;
2106
2107         int                     i_reloid;
2108         int                     i_relname;
2109         int                     i_relkind;
2110         int                     i_relacl;
2111         int                     i_usename;
2112         int                     i_relchecks;
2113         int                     i_reltriggers;
2114         int                     i_relhasindex;
2115         int                     i_relhasoids;
2116
2117         /*
2118          * find all the user-defined tables (no indexes and no catalogs),
2119          * ordering by oid is important so that we always process the parent
2120          * tables before the child tables when traversing the tblinfo*
2121          *
2122          * we ignore tables that are not type 'r' (ordinary relation) or 'S'
2123          * (sequence) or 'v' (view).
2124          */
2125
2126         if (g_fout->remoteVersion >= 70200)
2127         {
2128                 appendPQExpBuffer(query,
2129                                                 "SELECT pg_class.oid, relname, relacl, relkind, "
2130                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2131                                            "relchecks, reltriggers, relhasindex, relhasoids "
2132                                                   "from pg_class "
2133                                                   "where relname !~ '^pg_' "
2134                                                   "and relkind in ('%c', '%c', '%c') "
2135                                                   "order by oid",
2136                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2137         }
2138         else if (g_fout->remoteVersion >= 70100)
2139         {
2140                 /* all tables have oids in 7.1 */
2141                 appendPQExpBuffer(query,
2142                                                 "SELECT pg_class.oid, relname, relacl, relkind, "
2143                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2144                   "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids "
2145                                                   "from pg_class "
2146                                                   "where relname !~ '^pg_' "
2147                                                   "and relkind in ('%c', '%c', '%c') "
2148                                                   "order by oid",
2149                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2150         }
2151         else
2152         {
2153                 /*
2154                  * Before 7.1, view relkind was not set to 'v', so we must check
2155                  * if we have a view by looking for a rule in pg_rewrite.
2156                  */
2157                 appendPQExpBuffer(query,
2158                                                   "SELECT c.oid, relname, relacl, "
2159                                                   "CASE WHEN relhasrules and relkind = 'r' "
2160                                   "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
2161                                   "             r.ev_class = c.oid AND r.ev_type = '1') "
2162                                                   "THEN '%c'::\"char\" "
2163                                                   "ELSE relkind END AS relkind,"
2164                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2165                   "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids "
2166                                                   "from pg_class c "
2167                                                   "where relname !~ '^pg_' "
2168                                                   "and relkind in ('%c', '%c', '%c') "
2169                                                   "order by oid",
2170                                                   RELKIND_VIEW,
2171                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2172         }
2173
2174         res = PQexec(g_conn, query->data);
2175         if (!res ||
2176                 PQresultStatus(res) != PGRES_TUPLES_OK)
2177         {
2178                 write_msg(NULL, "query to obtain list of tables failed: %s",
2179                                   PQerrorMessage(g_conn));
2180                 exit_nicely();
2181         }
2182
2183         ntups = PQntuples(res);
2184
2185         *numTables = ntups;
2186
2187         /*
2188          * First pass: extract data from result and lock tables.  We do the
2189          * locking before anything else, to minimize the window wherein a
2190          * table could disappear under us.
2191          *
2192          * Note that we have to collect info about all tables here, even when
2193          * dumping only one, because we don't know which tables might be
2194          * inheritance ancestors of the target table.  Possible future
2195          * improvement: suppress later collection of schema info about tables
2196          * that are determined not to be either targets or ancestors of
2197          * targets.
2198          */
2199         tblinfo = (TableInfo *) malloc(ntups * sizeof(TableInfo));
2200
2201         i_reloid = PQfnumber(res, "oid");
2202         i_relname = PQfnumber(res, "relname");
2203         i_relacl = PQfnumber(res, "relacl");
2204         i_relkind = PQfnumber(res, "relkind");
2205         i_usename = PQfnumber(res, "usename");
2206         i_relchecks = PQfnumber(res, "relchecks");
2207         i_reltriggers = PQfnumber(res, "reltriggers");
2208         i_relhasindex = PQfnumber(res, "relhasindex");
2209         i_relhasoids = PQfnumber(res, "relhasoids");
2210
2211         for (i = 0; i < ntups; i++)
2212         {
2213                 tblinfo[i].oid = strdup(PQgetvalue(res, i, i_reloid));
2214                 tblinfo[i].relname = strdup(PQgetvalue(res, i, i_relname));
2215                 tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
2216                 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
2217                 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
2218                 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
2219                 tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
2220                 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
2221                 tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
2222
2223                 /*
2224                  * Read-lock target tables to make sure they aren't DROPPED or
2225                  * altered in schema before we get around to dumping them.
2226                  *
2227                  * If no target tablename was specified, lock all tables we see,
2228                  * otherwise lock only the specified table.  (This is incomplete
2229                  * because we'll still try to collect schema info about all
2230                  * tables, and could possibly lose during that phase.  But for the
2231                  * typical use where we're dumping all tables anyway, it matters
2232                  * not.)
2233                  *
2234                  * NOTE: it'd be kinda nice to lock views and sequences too, not only
2235                  * plain tables, but the backend doesn't presently allow that.
2236                  */
2237                 if ((tblinfo[i].relkind == RELKIND_RELATION) &&
2238                 (tablename == NULL || strcmp(tblinfo[i].relname, tablename) == 0))
2239                 {
2240                         PGresult   *lres;
2241
2242                         resetPQExpBuffer(lockquery);
2243                         appendPQExpBuffer(lockquery,
2244                                                           "LOCK TABLE %s IN ACCESS SHARE MODE",
2245                                                           fmtId(tblinfo[i].relname, force_quotes));
2246                         lres = PQexec(g_conn, lockquery->data);
2247                         if (!lres || PQresultStatus(lres) != PGRES_COMMAND_OK)
2248                         {
2249                                 write_msg(NULL, "Attempt to lock table \"%s\" failed.  %s",
2250                                                   tblinfo[i].relname, PQerrorMessage(g_conn));
2251                                 exit_nicely();
2252                         }
2253                         PQclear(lres);
2254                 }
2255         }
2256
2257         PQclear(res);
2258         res = NULL;
2259
2260         /*
2261          * Second pass: pick up additional information about each table, as
2262          * required.
2263          */
2264         for (i = 0; i < *numTables; i++)
2265         {
2266                 /* Emit notice if join for owner failed */
2267                 if (strlen(tblinfo[i].usename) == 0)
2268                         write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
2269                                           tblinfo[i].relname);
2270
2271                 /* Get definition if it's a view */
2272                 if (tblinfo[i].relkind == RELKIND_VIEW)
2273                 {
2274                         PGresult   *res2;
2275
2276                         resetPQExpBuffer(query);
2277
2278                         if (g_fout->remoteVersion < 70300)
2279                         {
2280                                 appendPQExpBuffer(query, "SELECT definition as viewdef, "
2281                                                                   "(select oid from pg_rewrite where "
2282                                                                   " rulename=('_RET' || viewname)::name) as view_oid"
2283                                                                   " from pg_views where viewname = ");
2284                                 formatStringLiteral(query, tblinfo[i].relname, CONV_ALL);
2285                                 appendPQExpBuffer(query, ";");
2286                         }
2287                         else
2288                         {
2289                                 /* Beginning in 7.3, viewname is not unique; use OID */
2290                                 appendPQExpBuffer(query, "SELECT pg_get_viewdef(ev_class) as viewdef, "
2291                                                                   "oid as view_oid"
2292                                                                   " from pg_rewrite where"
2293                                                                   " ev_class = '%s'::oid and"
2294                                                                   " rulename = '_RETURN';",
2295                                                                   tblinfo[i].oid);
2296                         }
2297
2298                         res2 = PQexec(g_conn, query->data);
2299                         if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
2300                         {
2301                                 write_msg(NULL, "query to obtain definition of view \"%s\" failed: %s",
2302                                                   tblinfo[i].relname, PQerrorMessage(g_conn));
2303                                 exit_nicely();
2304                         }
2305
2306                         if (PQntuples(res2) != 1)
2307                         {
2308                                 if (PQntuples(res2) < 1)
2309                                         write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
2310                                                           tblinfo[i].relname);
2311                                 else
2312                                         write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
2313                                                           tblinfo[i].relname);
2314                                 exit_nicely();
2315                         }
2316
2317                         if (PQgetisnull(res2, 0, 1))
2318                         {
2319                                 write_msg(NULL, "query to obtain definition of view \"%s\" returned NULL oid\n",
2320                                                   tblinfo[i].relname);
2321                                 exit_nicely();
2322                         }
2323
2324                         tblinfo[i].viewdef = strdup(PQgetvalue(res2, 0, 0));
2325                         tblinfo[i].viewoid = strdup(PQgetvalue(res2, 0, 1));
2326
2327                         if (strlen(tblinfo[i].viewdef) == 0)
2328                         {
2329                                 write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
2330                                                   tblinfo[i].relname);
2331                                 exit_nicely();
2332                         }
2333                         PQclear(res2);
2334                 }
2335                 else
2336                         tblinfo[i].viewdef = NULL;
2337
2338                 /*
2339                  * Get non-inherited CHECK constraints, if any.
2340                  *
2341                  * Exclude inherited CHECKs from CHECK constraints total. If a
2342                  * constraint matches by name and condition with a constraint
2343                  * belonging to a parent class (OR conditions match and both names
2344                  * start with '$', we assume it was inherited.
2345                  */
2346                 if (tblinfo[i].ncheck > 0)
2347                 {
2348                         PGresult   *res2;
2349                         int                     i_rcname,
2350                                                 i_rcsrc;
2351                         int                     ntups2;
2352                         int                     i2;
2353
2354                         if (g_verbose)
2355                                 write_msg(NULL, "finding CHECK constraints for table %s\n",
2356                                                   tblinfo[i].relname);
2357
2358                         resetPQExpBuffer(query);
2359                         appendPQExpBuffer(query, "SELECT rcname, rcsrc from pg_relcheck "
2360                                                           " where rcrelid = '%s'::oid "
2361                                                           "   and not exists "
2362                                    "  (select * from pg_relcheck as c, pg_inherits as i "
2363                                                         "    where i.inhrelid = pg_relcheck.rcrelid "
2364                                                           "      and (c.rcname = pg_relcheck.rcname "
2365                                                           "          or (    c.rcname[0] = '$' "
2366                                                  "              and pg_relcheck.rcname[0] = '$')"
2367                                                           "          )"
2368                                                           "      and c.rcsrc = pg_relcheck.rcsrc "
2369                                                           "      and c.rcrelid = i.inhparent) "
2370                                                           " order by rcname ",
2371                                                           tblinfo[i].oid);
2372                         res2 = PQexec(g_conn, query->data);
2373                         if (!res2 ||
2374                                 PQresultStatus(res2) != PGRES_TUPLES_OK)
2375                         {
2376                                 write_msg(NULL, "query to obtain check constraints failed: %s", PQerrorMessage(g_conn));
2377                                 exit_nicely();
2378                         }
2379                         ntups2 = PQntuples(res2);
2380                         if (ntups2 > tblinfo[i].ncheck)
2381                         {
2382                                 write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
2383                                                   tblinfo[i].ncheck, tblinfo[i].relname, ntups2);
2384                                 write_msg(NULL, "(The system catalogs might be corrupted.)\n");
2385                                 exit_nicely();
2386                         }
2387
2388                         /*
2389                          * Set ncheck to the number of *non-inherited* CHECK
2390                          * constraints
2391                          */
2392                         tblinfo[i].ncheck = ntups2;
2393
2394                         i_rcname = PQfnumber(res2, "rcname");
2395                         i_rcsrc = PQfnumber(res2, "rcsrc");
2396                         tblinfo[i].check_expr = (char **) malloc(ntups2 * sizeof(char *));
2397                         for (i2 = 0; i2 < ntups2; i2++)
2398                         {
2399                                 const char *name = PQgetvalue(res2, i2, i_rcname);
2400                                 const char *expr = PQgetvalue(res2, i2, i_rcsrc);
2401
2402                                 resetPQExpBuffer(query);
2403                                 if (name[0] != '$')
2404                                 {
2405                                         appendPQExpBuffer(query, "CONSTRAINT %s ",
2406                                                                           fmtId(name, force_quotes));
2407                                 }
2408                                 appendPQExpBuffer(query, "CHECK (%s)", expr);
2409                                 tblinfo[i].check_expr[i2] = strdup(query->data);
2410                         }
2411                         PQclear(res2);
2412                 }
2413                 else
2414                         tblinfo[i].check_expr = NULL;
2415
2416                 /* Get primary key */
2417                 if (tblinfo[i].hasindex)
2418                 {
2419                         PGresult   *res2;
2420
2421                         resetPQExpBuffer(query);
2422                         appendPQExpBuffer(query,
2423                                                           "SELECT indexrelid FROM pg_index i WHERE i.indisprimary AND i.indrelid = '%s'::oid ",
2424                                                           tblinfo[i].oid);
2425                         res2 = PQexec(g_conn, query->data);
2426                         if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
2427                         {
2428                                 write_msg(NULL, "query to obtain primary key of table \"%s\" failed: %s",
2429                                                   tblinfo[i].relname, PQerrorMessage(g_conn));
2430                                 exit_nicely();
2431                         }
2432
2433                         if (PQntuples(res2) > 1)
2434                         {
2435                                 write_msg(NULL, "query to obtain primary key of table \"%s\" produced more than one result\n",
2436                                                   tblinfo[i].relname);
2437                                 exit_nicely();
2438                         }
2439
2440                         if (PQntuples(res2) == 1)
2441                                 tblinfo[i].pkIndexOid = strdup(PQgetvalue(res2, 0, 0));
2442                         else
2443                                 tblinfo[i].pkIndexOid = NULL;
2444
2445                 }
2446                 else
2447                         tblinfo[i].pkIndexOid = NULL;
2448
2449                 /* Get primary key name (if primary key exist) */
2450                 if (tblinfo[i].pkIndexOid != NULL)
2451                 {
2452                         PGresult   *res2;
2453                         int                     n;
2454
2455                         resetPQExpBuffer(query);
2456                         appendPQExpBuffer(query,
2457                                                           "SELECT relname FROM pg_class "
2458                                                           "WHERE oid = '%s'::oid",
2459                                                           tblinfo[i].pkIndexOid);
2460
2461                         res2 = PQexec(g_conn, query->data);
2462                         if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK)
2463                         {
2464                                 write_msg(NULL, "query to obtain name of primary key of table \"%s\" failed: %s",
2465                                                   tblinfo[i].relname, PQerrorMessage(g_conn));
2466                                 exit_nicely();
2467                         }
2468
2469                         n = PQntuples(res2);
2470                         if (n != 1)
2471                         {
2472                                 if (n == 0)
2473                                         write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned no rows\n",
2474                                                           tblinfo[i].relname);
2475                                 else
2476                                         write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned %d rows\n",
2477                                                           tblinfo[i].relname, n);
2478                                 exit_nicely();
2479                         }
2480
2481                         tblinfo[i].primary_key_name =
2482                                 strdup(fmtId(PQgetvalue(res2, 0, 0), force_quotes));
2483                         if (tblinfo[i].primary_key_name == NULL)
2484                         {
2485                                 write_msg(NULL, "out of memory\n");
2486                                 exit_nicely();
2487                         }
2488                 }
2489                 else
2490                         tblinfo[i].primary_key_name = NULL;
2491
2492                 /* Get Triggers */
2493                 if (tblinfo[i].ntrig > 0)
2494                 {
2495                         PGresult   *res2;
2496                         int                     i_tgoid,
2497                                                 i_tgname,
2498                                                 i_tgfoid,
2499                                                 i_tgtype,
2500                                                 i_tgnargs,
2501                                                 i_tgargs,
2502                                                 i_tgisconstraint,
2503                                                 i_tgconstrname,
2504                                                 i_tgdeferrable,
2505                                                 i_tgconstrrelid,
2506                                                 i_tgconstrrelname,
2507                                                 i_tginitdeferred;
2508                         int                     ntups2;
2509                         int                     i2;
2510
2511                         if (g_verbose)
2512                                 write_msg(NULL, "finding triggers for table %s\n", tblinfo[i].relname);
2513
2514                         resetPQExpBuffer(query);
2515                         appendPQExpBuffer(query,
2516                                            "SELECT tgname, tgfoid, tgtype, tgnargs, tgargs, "
2517                                                    "tgisconstraint, tgconstrname, tgdeferrable, "
2518                                                           "tgconstrrelid, tginitdeferred, oid, "
2519                           "(select relname from pg_class where oid = tgconstrrelid) "
2520                                                           "             as tgconstrrelname "
2521                                                           "from pg_trigger "
2522                                                           "where tgrelid = '%s'::oid ",
2523                                                           tblinfo[i].oid);
2524                         res2 = PQexec(g_conn, query->data);
2525                         if (!res2 ||
2526                                 PQresultStatus(res2) != PGRES_TUPLES_OK)
2527                         {
2528                                 write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn));
2529                                 exit_nicely();
2530                         }
2531                         ntups2 = PQntuples(res2);
2532                         if (ntups2 != tblinfo[i].ntrig)
2533                         {
2534                                 write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
2535                                                   tblinfo[i].ntrig, tblinfo[i].relname, ntups2);
2536                                 exit_nicely();
2537                         }
2538                         i_tgname = PQfnumber(res2, "tgname");
2539                         i_tgfoid = PQfnumber(res2, "tgfoid");
2540                         i_tgtype = PQfnumber(res2, "tgtype");
2541                         i_tgnargs = PQfnumber(res2, "tgnargs");
2542                         i_tgargs = PQfnumber(res2, "tgargs");
2543                         i_tgoid = PQfnumber(res2, "oid");
2544                         i_tgisconstraint = PQfnumber(res2, "tgisconstraint");
2545                         i_tgconstrname = PQfnumber(res2, "tgconstrname");
2546                         i_tgdeferrable = PQfnumber(res2, "tgdeferrable");
2547                         i_tgconstrrelid = PQfnumber(res2, "tgconstrrelid");
2548                         i_tgconstrrelname = PQfnumber(res2, "tgconstrrelname");
2549                         i_tginitdeferred = PQfnumber(res2, "tginitdeferred");
2550
2551                         tblinfo[i].triggers = (TrigInfo *) malloc(ntups2 * sizeof(TrigInfo));
2552                         resetPQExpBuffer(query);
2553                         for (i2 = 0; i2 < ntups2; i2++)
2554                         {
2555                                 const char *tgfuncoid = PQgetvalue(res2, i2, i_tgfoid);
2556                                 char       *tgfunc = NULL;
2557                                 int2            tgtype = atoi(PQgetvalue(res2, i2, i_tgtype));
2558                                 int                     tgnargs = atoi(PQgetvalue(res2, i2, i_tgnargs));
2559                                 const char *tgargs = PQgetvalue(res2, i2, i_tgargs);
2560                                 int                     tgisconstraint;
2561                                 int                     tgdeferrable;
2562                                 int                     tginitdeferred;
2563                                 char       *tgconstrrelid;
2564                                 char       *tgname;
2565                                 const char *p;
2566                                 int                     findx;
2567
2568                                 tgname = PQgetvalue(res2, i2, i_tgname);
2569
2570                                 if (strcmp(PQgetvalue(res2, i2, i_tgisconstraint), "f") == 0)
2571                                         tgisconstraint = 0;
2572                                 else
2573                                         tgisconstraint = 1;
2574
2575                                 if (strcmp(PQgetvalue(res2, i2, i_tgdeferrable), "f") == 0)
2576                                         tgdeferrable = 0;
2577                                 else
2578                                         tgdeferrable = 1;
2579
2580                                 if (strcmp(PQgetvalue(res2, i2, i_tginitdeferred), "f") == 0)
2581                                         tginitdeferred = 0;
2582                                 else
2583                                         tginitdeferred = 1;
2584
2585                                 for (findx = 0; findx < numFuncs; findx++)
2586                                 {
2587                                         if (strcmp(finfo[findx].oid, tgfuncoid) == 0 &&
2588                                                 finfo[findx].nargs == 0 &&
2589                                                 strcmp(finfo[findx].prorettype, "0") == 0)
2590                                                 break;
2591                                 }
2592
2593                                 if (findx == numFuncs)
2594                                 {
2595                                         PGresult   *r;
2596                                         int                     numFuncs;
2597
2598                                         /*
2599                                          * the funcname is an oid which we use to find the
2600                                          * name of the pg_proc.  We need to do this because
2601                                          * getFuncs() only reads in the user-defined funcs not
2602                                          * all the funcs.  We might not find what we want by
2603                                          * looking in FuncInfo*
2604                                          */
2605                                         resetPQExpBuffer(query);
2606                                         appendPQExpBuffer(query,
2607                                                                           "SELECT proname from pg_proc "
2608                                                                           "where pg_proc.oid = '%s'::oid",
2609                                                                           tgfuncoid);
2610
2611                                         r = PQexec(g_conn, query->data);
2612                                         if (!r || PQresultStatus(r) != PGRES_TUPLES_OK)
2613                                         {
2614                                                 write_msg(NULL, "query to obtain procedure name for trigger \"%s\" failed: %s",
2615                                                                   tgname, PQerrorMessage(g_conn));
2616                                                 exit_nicely();
2617                                         }
2618
2619                                         /* Sanity: Check we got only one tuple */
2620                                         numFuncs = PQntuples(r);
2621                                         if (numFuncs != 1)
2622                                         {
2623                                                 if (numFuncs == 0)
2624                                                         write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned no rows\n",
2625                                                                           tgname, tgfuncoid);
2626                                                 else
2627                                                         write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned %d rows\n",
2628                                                                           tgname, tgfuncoid, numFuncs);
2629                                                 exit_nicely();
2630                                         }
2631
2632                                         tgfunc = strdup(PQgetvalue(r, 0, PQfnumber(r, "proname")));
2633                                         PQclear(r);
2634                                 }
2635                                 else
2636                                         tgfunc = strdup(finfo[findx].proname);
2637
2638                                 appendPQExpBuffer(delqry, "DROP TRIGGER %s ", fmtId(tgname, force_quotes));
2639                                 appendPQExpBuffer(delqry, "ON %s;\n",
2640                                                                 fmtId(tblinfo[i].relname, force_quotes));
2641
2642                                 resetPQExpBuffer(query);
2643                                 if (tgisconstraint)
2644                                 {
2645                                         appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
2646                                         appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgconstrname), force_quotes));
2647                                 }
2648                                 else
2649                                 {
2650                                         appendPQExpBuffer(query, "CREATE TRIGGER ");
2651                                         appendPQExpBuffer(query, fmtId(tgname, force_quotes));
2652                                 }
2653                                 appendPQExpBufferChar(query, ' ');
2654                                 /* Trigger type */
2655                                 findx = 0;
2656                                 if (TRIGGER_FOR_BEFORE(tgtype))
2657                                         appendPQExpBuffer(query, "BEFORE");
2658                                 else
2659                                         appendPQExpBuffer(query, "AFTER");
2660                                 if (TRIGGER_FOR_INSERT(tgtype))
2661                                 {
2662                                         appendPQExpBuffer(query, " INSERT");
2663                                         findx++;
2664                                 }
2665                                 if (TRIGGER_FOR_DELETE(tgtype))
2666                                 {
2667                                         if (findx > 0)
2668                                                 appendPQExpBuffer(query, " OR DELETE");
2669                                         else
2670                                                 appendPQExpBuffer(query, " DELETE");
2671                                         findx++;
2672                                 }
2673                                 if (TRIGGER_FOR_UPDATE(tgtype))
2674                                 {
2675                                         if (findx > 0)
2676                                                 appendPQExpBuffer(query, " OR UPDATE");
2677                                         else
2678                                                 appendPQExpBuffer(query, " UPDATE");
2679                                 }
2680                                 appendPQExpBuffer(query, " ON %s ", fmtId(tblinfo[i].relname, force_quotes));
2681
2682                                 if (tgisconstraint)
2683                                 {
2684                                         tgconstrrelid = PQgetvalue(res2, i2, i_tgconstrrelid);
2685
2686                                         if (strcmp(tgconstrrelid, "0") != 0)
2687                                         {
2688
2689                                                 if (PQgetisnull(res2, i2, i_tgconstrrelname))
2690                                                 {
2691                                                         write_msg(NULL, "query produced NULL referenced table name for foreign key trigger \"%s\" on table \"%s\" (oid of table: %s)\n",
2692                                                           tgname, tblinfo[i].relname, tgconstrrelid);
2693                                                         exit_nicely();
2694                                                 }
2695
2696                                                 appendPQExpBuffer(query, " FROM %s",
2697                                                                                   fmtId(PQgetvalue(res2, i2, i_tgconstrrelname), force_quotes));
2698                                         }
2699                                         if (!tgdeferrable)
2700                                                 appendPQExpBuffer(query, " NOT");
2701                                         appendPQExpBuffer(query, " DEFERRABLE INITIALLY ");
2702                                         if (tginitdeferred)
2703                                                 appendPQExpBuffer(query, "DEFERRED");
2704                                         else
2705                                                 appendPQExpBuffer(query, "IMMEDIATE");
2706
2707                                 }
2708
2709                                 appendPQExpBuffer(query, " FOR EACH ROW");
2710                                 appendPQExpBuffer(query, " EXECUTE PROCEDURE %s (",
2711                                                                   fmtId(tgfunc, force_quotes));
2712                                 for (findx = 0; findx < tgnargs; findx++)
2713                                 {
2714                                         const char *s;
2715
2716                                         for (p = tgargs;;)
2717                                         {
2718                                                 p = strchr(p, '\\');
2719                                                 if (p == NULL)
2720                                                 {
2721                                                         write_msg(NULL, "bad argument string (%s) for trigger \"%s\" on table \"%s\"\n",
2722                                                                           PQgetvalue(res2, i2, i_tgargs),
2723                                                                           tgname,
2724                                                                           tblinfo[i].relname);
2725                                                         exit_nicely();
2726                                                 }
2727                                                 p++;
2728                                                 if (*p == '\\')
2729                                                 {
2730                                                         p++;
2731                                                         continue;
2732                                                 }
2733                                                 if (p[0] == '0' && p[1] == '0' && p[2] == '0')
2734                                                         break;
2735                                         }
2736                                         p--;
2737                                         appendPQExpBufferChar(query, '\'');
2738                                         for (s = tgargs; s < p;)
2739                                         {
2740                                                 if (*s == '\'')
2741                                                         appendPQExpBufferChar(query, '\\');
2742                                                 appendPQExpBufferChar(query, *s++);
2743                                         }
2744                                         appendPQExpBufferChar(query, '\'');
2745                                         appendPQExpBuffer(query, (findx < tgnargs - 1) ? ", " : "");
2746                                         tgargs = p + 4;
2747                                 }
2748                                 appendPQExpBuffer(query, ");\n");
2749
2750                                 tblinfo[i].triggers[i2].tgsrc = strdup(query->data);
2751
2752                                 /*** Initialize trcomments and troids ***/
2753
2754                                 resetPQExpBuffer(query);
2755                                 appendPQExpBuffer(query, "TRIGGER %s ",
2756                                                                   fmtId(tgname, force_quotes));
2757                                 appendPQExpBuffer(query, "ON %s",
2758                                                                 fmtId(tblinfo[i].relname, force_quotes));
2759                                 tblinfo[i].triggers[i2].tgcomment = strdup(query->data);
2760                                 tblinfo[i].triggers[i2].oid = strdup(PQgetvalue(res2, i2, i_tgoid));
2761                                 tblinfo[i].triggers[i2].tgname = strdup(fmtId(tgname, false));
2762                                 tblinfo[i].triggers[i2].tgdel = strdup(delqry->data);
2763
2764                                 if (tgfunc)
2765                                         free(tgfunc);
2766                         }
2767                         PQclear(res2);
2768                 }
2769                 else
2770                         tblinfo[i].triggers = NULL;
2771
2772         }
2773
2774         destroyPQExpBuffer(query);
2775         destroyPQExpBuffer(delqry);
2776         destroyPQExpBuffer(lockquery);
2777
2778         return tblinfo;
2779 }
2780
2781 /*
2782  * getInherits
2783  *        read all the inheritance information
2784  * from the system catalogs return them in the InhInfo* structure
2785  *
2786  * numInherits is set to the number of tables read in
2787  */
2788 InhInfo *
2789 getInherits(int *numInherits)
2790 {
2791         PGresult   *res;
2792         int                     ntups;
2793         int                     i;
2794         PQExpBuffer query = createPQExpBuffer();
2795         InhInfo    *inhinfo;
2796
2797         int                     i_inhrelid;
2798         int                     i_inhparent;
2799
2800         /* find all the inheritance information */
2801
2802         appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
2803
2804         res = PQexec(g_conn, query->data);
2805         if (!res ||
2806                 PQresultStatus(res) != PGRES_TUPLES_OK)
2807         {
2808                 write_msg(NULL, "query to obtain inheritance relationships failed: %s",
2809                                   PQerrorMessage(g_conn));
2810                 exit_nicely();
2811         }
2812
2813         ntups = PQntuples(res);
2814
2815         *numInherits = ntups;
2816
2817         inhinfo = (InhInfo *) malloc(ntups * sizeof(InhInfo));
2818
2819         i_inhrelid = PQfnumber(res, "inhrelid");
2820         i_inhparent = PQfnumber(res, "inhparent");
2821
2822         for (i = 0; i < ntups; i++)
2823         {
2824                 inhinfo[i].inhrelid = strdup(PQgetvalue(res, i, i_inhrelid));
2825                 inhinfo[i].inhparent = strdup(PQgetvalue(res, i, i_inhparent));
2826         }
2827
2828         PQclear(res);
2829
2830         destroyPQExpBuffer(query);
2831
2832         return inhinfo;
2833 }
2834
2835 /*
2836  * getTableAttrs -
2837  *        for each table in tblinfo, read its attributes types and names
2838  *
2839  * this is implemented in a very inefficient way right now, looping
2840  * through the tblinfo and doing a join per table to find the attrs and their
2841  * types
2842  *
2843  *      modifies tblinfo
2844  */
2845 void
2846 getTableAttrs(TableInfo *tblinfo, int numTables)
2847 {
2848         int                     i,
2849                                 j;
2850         PQExpBuffer q = createPQExpBuffer();
2851         int                     i_attname;
2852         int                     i_typname;
2853         int                     i_atttypmod;
2854         int                     i_attnotnull;
2855         int                     i_atthasdef;
2856         int                     i_atttypedefn;
2857         PGresult   *res;
2858         int                     ntups;
2859
2860         for (i = 0; i < numTables; i++)
2861         {
2862                 if (tblinfo[i].relkind == RELKIND_SEQUENCE)
2863                         continue;
2864
2865                 /* find all the user attributes and their types */
2866                 /* we must read the attribute names in attribute number order! */
2867
2868                 /*
2869                  * because we will use the attnum to index into the attnames array
2870                  * later
2871                  */
2872                 if (g_verbose)
2873                         write_msg(NULL, "finding the columns and types for table %s\n",
2874                                           tblinfo[i].relname);
2875
2876                 resetPQExpBuffer(q);
2877
2878                 if (g_fout->remoteVersion < 70100)
2879                 {
2880                         /* Fake the LOJ below */
2881                         appendPQExpBuffer(q,
2882                                  "  SELECT a.attnum, a.attname, t.typname, a.atttypmod, "
2883                                 "        a.attnotnull, a.atthasdef, NULL as atttypedefn "
2884                                                           "    from pg_attribute a, pg_type t "
2885                                                           "    where a.attrelid = '%s'::oid "
2886                                                           "        and a.attnum > 0 "
2887                                                           "        and a.atttypid = t.oid "
2888                                                           " UNION ALL SELECT a.attnum, a.attname, NULL as typname, a.atttypmod, "
2889                                 "        a.attnotnull, a.atthasdef, NULL as atttypedefn "
2890                                                           "    from pg_attribute a "
2891                                                           "    where a.attrelid = '%s'::oid "
2892                                                           "        and a.attnum > 0 "
2893                                                           "        and Not Exists(Select * From pg_type t where a.atttypid = t.oid)"
2894                                                           "    order by attnum",
2895                                                           tblinfo[i].oid, tblinfo[i].oid);
2896
2897                 }
2898                 else
2899                 {
2900                         appendPQExpBuffer(q, "SELECT a.attnum, a.attname, t.typname, a.atttypmod, "
2901                                                           "a.attnotnull, a.atthasdef, format_type(a.atttypid, a.atttypmod) as atttypedefn "
2902                                                           "from pg_attribute a LEFT OUTER JOIN pg_type t ON a.atttypid = t.oid "
2903                                                           "where a.attrelid = '%s'::oid "
2904                                                           "and a.attnum > 0 order by attnum",
2905                                                           tblinfo[i].oid);
2906                 }
2907
2908                 res = PQexec(g_conn, q->data);
2909                 if (!res ||
2910                         PQresultStatus(res) != PGRES_TUPLES_OK)
2911                 {
2912                         write_msg(NULL, "query to get table columns failed: %s", PQerrorMessage(g_conn));
2913                         exit_nicely();
2914                 }
2915
2916                 ntups = PQntuples(res);
2917
2918                 i_attname = PQfnumber(res, "attname");
2919                 i_typname = PQfnumber(res, "typname");
2920                 i_atttypmod = PQfnumber(res, "atttypmod");
2921                 i_attnotnull = PQfnumber(res, "attnotnull");
2922                 i_atthasdef = PQfnumber(res, "atthasdef");
2923                 i_atttypedefn = PQfnumber(res, "atttypedefn");
2924
2925                 tblinfo[i].numatts = ntups;
2926                 tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *));
2927                 tblinfo[i].atttypedefns = (char **) malloc(ntups * sizeof(char *));
2928                 tblinfo[i].typnames = (char **) malloc(ntups * sizeof(char *));
2929                 tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int));
2930                 tblinfo[i].inhAttrs = (int *) malloc(ntups * sizeof(int));
2931                 tblinfo[i].inhAttrDef = (int *) malloc(ntups * sizeof(int));
2932                 tblinfo[i].inhNotNull = (int *) malloc(ntups * sizeof(int));
2933                 tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool));
2934                 tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *));
2935                 tblinfo[i].parentRels = NULL;
2936                 tblinfo[i].numParents = 0;
2937                 for (j = 0; j < ntups; j++)
2938                 {
2939                         /* Sanity check on LOJ */
2940                         if (PQgetisnull(res, j, i_typname))
2941                         {
2942                                 write_msg(NULL, "query produced NULL name for data type of column %d of table %s\n",
2943                                                   j + 1, tblinfo[i].relname);
2944                                 exit_nicely();
2945                         }
2946
2947                         tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname));
2948                         tblinfo[i].atttypedefns[j] = strdup(PQgetvalue(res, j, i_atttypedefn));
2949                         tblinfo[i].typnames[j] = strdup(PQgetvalue(res, j, i_typname));
2950                         tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
2951                         tblinfo[i].inhAttrs[j] = 0; /* this flag is set in
2952                                                                                  * flagInhAttrs() */
2953                         tblinfo[i].inhAttrDef[j] = 0;
2954                         tblinfo[i].inhNotNull[j] = 0;
2955
2956                         tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't') ? true : false;
2957                         if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
2958                         {
2959                                 PGresult   *res2;
2960                                 int                     numAttr;
2961
2962                                 if (g_verbose)
2963                                         write_msg(NULL, "finding DEFAULT expression for column %s\n",
2964                                                           tblinfo[i].attnames[j]);
2965
2966                                 resetPQExpBuffer(q);
2967                                 appendPQExpBuffer(q, "SELECT adsrc from pg_attrdef "
2968                                                          "where adrelid = '%s'::oid and adnum = %d ",
2969                                                                   tblinfo[i].oid, j + 1);
2970                                 res2 = PQexec(g_conn, q->data);
2971                                 if (!res2 ||
2972                                         PQresultStatus(res2) != PGRES_TUPLES_OK)
2973                                 {
2974                                         write_msg(NULL, "query to get column default value failed: %s",
2975                                                           PQerrorMessage(g_conn));
2976                                         exit_nicely();
2977                                 }
2978
2979                                 /* Sanity: Check we got only one tuple */
2980                                 numAttr = PQntuples(res2);
2981                                 if (numAttr != 1)
2982                                 {
2983                                         write_msg(NULL, "query to get default value for column \"%s\" returned %d rows; expected 1\n",
2984                                                           tblinfo[i].attnames[j], numAttr);
2985                                         exit_nicely();
2986                                 }
2987
2988                                 tblinfo[i].adef_expr[j] = strdup(PQgetvalue(res2, 0, PQfnumber(res2, "adsrc")));
2989                                 PQclear(res2);
2990                         }
2991                         else
2992                                 tblinfo[i].adef_expr[j] = NULL;
2993                 }
2994                 PQclear(res);
2995         }
2996
2997         destroyPQExpBuffer(q);
2998 }
2999
3000
3001 /*
3002  * getIndexes
3003  *        read all the user-defined indexes information
3004  * from the system catalogs return them in the InhInfo* structure
3005  *
3006  * numIndexes is set to the number of indexes read in
3007  *
3008  *
3009  */
3010 IndInfo *
3011 getIndexes(int *numIndexes)
3012 {
3013         int                     i;
3014         PQExpBuffer query = createPQExpBuffer();
3015         PGresult   *res;
3016         int                     ntups;
3017         IndInfo    *indinfo;
3018
3019         int                     i_indexreloid;
3020         int                     i_indreloid;
3021         int                     i_indexrelname;
3022         int                     i_indrelname;
3023         int                     i_indexdef;
3024         int                     i_indisprimary;
3025         int                     i_indnkeys;
3026         int                     i_indkey;
3027
3028         /*
3029          * find all the user-defined indexes.
3030          *
3031          * Notice we skip indexes on system classes
3032          *
3033          * XXXX: Use LOJ
3034          */
3035
3036         appendPQExpBuffer(query,
3037                                           "SELECT i.indexrelid as indexreloid, "
3038                                           "i.indrelid as indreloid, "
3039                                           "t1.relname as indexrelname, t2.relname as indrelname, "
3040                                           "pg_get_indexdef(i.indexrelid) as indexdef, "
3041                                           "i.indisprimary, i.indkey, "
3042                                           "CASE WHEN i.indproc <> 0 "
3043                                           "  THEN (SELECT pronargs FROM pg_proc WHERE pg_proc.oid = i.indproc) "
3044                                           "  ELSE t1.relnatts END as indnkeys "
3045                                           "FROM pg_index i, pg_class t1, pg_class t2 "
3046                                           "WHERE t1.oid = i.indexrelid and t2.oid = i.indrelid "
3047                                           "and i.indexrelid > '%u'::oid "
3048                                           "and t2.relname !~ '^pg_' ",
3049                                           g_last_builtin_oid);
3050
3051         if (g_fout->remoteVersion < 70100)
3052                 appendPQExpBuffer(query, " and t2.relkind != 'l'");
3053
3054         res = PQexec(g_conn, query->data);
3055         if (!res ||
3056                 PQresultStatus(res) != PGRES_TUPLES_OK)
3057         {
3058                 write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn));
3059                 exit_nicely();
3060         }
3061
3062         ntups = PQntuples(res);
3063
3064         *numIndexes = ntups;
3065
3066         indinfo = (IndInfo *) malloc(ntups * sizeof(IndInfo));
3067
3068         memset((char *) indinfo, 0, ntups * sizeof(IndInfo));
3069
3070         i_indexreloid = PQfnumber(res, "indexreloid");
3071         i_indreloid = PQfnumber(res, "indreloid");
3072         i_indexrelname = PQfnumber(res, "indexrelname");
3073         i_indrelname = PQfnumber(res, "indrelname");
3074         i_indexdef = PQfnumber(res, "indexdef");
3075         i_indisprimary = PQfnumber(res, "indisprimary");
3076         i_indnkeys = PQfnumber(res, "indnkeys");
3077         i_indkey = PQfnumber(res, "indkey");
3078
3079         for (i = 0; i < ntups; i++)
3080         {
3081                 indinfo[i].indexreloid = strdup(PQgetvalue(res, i, i_indexreloid));
3082                 indinfo[i].indreloid = strdup(PQgetvalue(res, i, i_indreloid));
3083                 indinfo[i].indexrelname = strdup(PQgetvalue(res, i, i_indexrelname));
3084                 indinfo[i].indrelname = strdup(PQgetvalue(res, i, i_indrelname));
3085                 indinfo[i].indexdef = strdup(PQgetvalue(res, i, i_indexdef));
3086                 indinfo[i].indisprimary = strdup(PQgetvalue(res, i, i_indisprimary));
3087                 indinfo[i].indnkeys = atoi(PQgetvalue(res, i, i_indnkeys));
3088                 indinfo[i].indkey = malloc(indinfo[i].indnkeys * sizeof(indinfo[i].indkey[0]));
3089                 parseNumericArray(PQgetvalue(res, i, i_indkey),
3090                                                   indinfo[i].indkey,
3091                                                   indinfo[i].indnkeys);
3092         }
3093         PQclear(res);
3094
3095         destroyPQExpBuffer(query);
3096
3097         return indinfo;
3098 }
3099
3100 /*------------------------------------------------------------------
3101  * dumpComment --
3102  *
3103  * This routine is used to dump any comments associated with the
3104  * oid handed to this routine. The routine takes a constant character
3105  * string for the target part of the comment-creation command, plus
3106  * OID, class name, and subid which are the primary key for pg_description.
3107  * If a matching pg_description entry is found, it is dumped.
3108  * Additional dependencies can be passed for the comment, too --- this is
3109  * needed for VIEWs, whose comments are filed under the table OID but
3110  * which are dumped in order by their rule OID.
3111  *------------------------------------------------------------------
3112 */
3113
3114 static void
3115 dumpComment(Archive *fout, const char *target, const char *oid,
3116                         const char *classname, int subid,
3117                         const char *((*deps)[]))
3118 {
3119         PGresult   *res;
3120         PQExpBuffer query;
3121         int                     i_description;
3122
3123         /* Comments are SCHEMA not data */
3124         if (dataOnly)
3125                 return;
3126
3127         /*** Build query to find comment ***/
3128
3129         query = createPQExpBuffer();
3130
3131         if (fout->remoteVersion >= 70200)
3132         {
3133                 appendPQExpBuffer(query, "SELECT description FROM pg_description "
3134                                                   "WHERE objoid = '%s'::oid and classoid = "
3135                                            "(SELECT oid FROM pg_class where relname = '%s') "
3136                                                   "and objsubid = %d",
3137                                                   oid, classname, subid);
3138         }
3139         else
3140         {
3141                 /* Note: this will fail to find attribute comments in pre-7.2... */
3142                 appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%s'::oid", oid);
3143         }
3144
3145         /*** Execute query ***/
3146
3147         res = PQexec(g_conn, query->data);
3148         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
3149         {
3150                 write_msg(NULL, "query to get comment on oid %s failed: %s",
3151                                   oid, PQerrorMessage(g_conn));
3152                 exit_nicely();
3153         }
3154
3155         /*** If a comment exists, build COMMENT ON statement ***/
3156
3157         if (PQntuples(res) == 1)
3158         {
3159                 i_description = PQfnumber(res, "description");
3160                 resetPQExpBuffer(query);
3161                 appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
3162                 formatStringLiteral(query, PQgetvalue(res, 0, i_description),
3163                                                         PASS_LFTAB);
3164                 appendPQExpBuffer(query, ";\n");
3165
3166                 ArchiveEntry(fout, oid, target, "COMMENT", deps,
3167                                          query->data, "" /* Del */ ,
3168                                          "" /* Copy */ , "" /* Owner */ , NULL, NULL);
3169         }
3170
3171         /*** Clear the statement buffer and return ***/
3172
3173         PQclear(res);
3174         destroyPQExpBuffer(query);
3175 }
3176
3177 /*------------------------------------------------------------------
3178  * dumpDBComment --
3179  *
3180  * This routine is used to dump any comments associated with the
3181  * database to which we are currently connected. If the user chose
3182  * to dump the schema of the database, then this is the first
3183  * statement issued.
3184  *------------------------------------------------------------------
3185 */
3186
3187 void
3188 dumpDBComment(Archive *fout)
3189 {
3190         PGresult   *res;
3191         PQExpBuffer query;
3192         int                     i_oid;
3193
3194         /*** Build query to find comment ***/
3195
3196         query = createPQExpBuffer();
3197         appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
3198         formatStringLiteral(query, PQdb(g_conn), CONV_ALL);
3199
3200         /*** Execute query ***/
3201
3202         res = PQexec(g_conn, query->data);
3203         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
3204         {
3205                 write_msg(NULL, "query to get database oid failed: %s",
3206                                   PQerrorMessage(g_conn));
3207                 exit_nicely();
3208         }
3209
3210         /*** If a comment exists, build COMMENT ON statement ***/
3211
3212         if (PQntuples(res) != 0)
3213         {
3214                 i_oid = PQfnumber(res, "oid");
3215                 resetPQExpBuffer(query);
3216                 appendPQExpBuffer(query, "DATABASE %s", fmtId(PQdb(g_conn), force_quotes));
3217                 dumpComment(fout, query->data, PQgetvalue(res, 0, i_oid),
3218                                         "pg_database", 0, NULL);
3219         }
3220
3221         /*** Clear the statement buffer and return ***/
3222
3223         PQclear(res);
3224         destroyPQExpBuffer(query);
3225 }
3226
3227 /*
3228  * dumpOneDomain
3229  *    wites out to fout the queries to recrease a user-defined domains
3230  *    as requested by dumpTypes
3231  */
3232 static void
3233 dumpOneDomain(Archive *fout, TypeInfo *tinfo)
3234 {
3235         PQExpBuffer q = createPQExpBuffer();
3236         PQExpBuffer delq = createPQExpBuffer();
3237
3238         PGresult   *res;
3239         PQExpBuffer query = createPQExpBuffer();
3240         int                     ntups;
3241         const char *((*deps)[]);
3242         int                     depIdx = 0;
3243
3244
3245         deps = malloc(sizeof(char *) * 10);
3246
3247         /* Fetch domain specific details */
3248         resetPQExpBuffer(query);
3249         appendPQExpBuffer(query, "SELECT typnotnull, "
3250                                                          "format_type(typbasetype, typtypmod) as typdefn, "
3251                                                          "typbasetype "
3252                                                          "FROM pg_type "
3253                                                          "WHERE typname = '%s'",
3254                                                          tinfo->typname);
3255
3256         res = PQexec(g_conn, query->data);
3257         if (!res ||
3258                 PQresultStatus(res) != PGRES_TUPLES_OK)
3259         {
3260                 write_msg(NULL, "query to obtain domain information failed: %s", PQerrorMessage(g_conn));
3261                 exit_nicely();
3262         }
3263
3264         /* Expecting a single result only */
3265         ntups = PQntuples(res);
3266         if (ntups != 1)
3267                 write_msg(NULL, "Domain %s non-existant.", fmtId(tinfo->typname, force_quotes));
3268
3269
3270         /* Drop the old copy */
3271         resetPQExpBuffer(delq);
3272         appendPQExpBuffer(delq, "DROP DOMAIN %s RESTRICT;\n", fmtId(tinfo->typname, force_quotes));
3273
3274         resetPQExpBuffer(q);
3275         appendPQExpBuffer(q,
3276                                           "CREATE DOMAIN %s AS %s",
3277                                           fmtId(tinfo->typname, force_quotes),
3278                                           PQgetvalue(res, 0, PQfnumber(res, "typdefn"))
3279                                           );
3280
3281         /* Depends on the base type */
3282         (*deps)[depIdx++] = strdup(PQgetvalue(res, 0, PQfnumber(res, "typbasetype")));
3283
3284         if (PQgetvalue(res, 0, PQfnumber(res, "typnotnull"))[0] == 't')
3285                 appendPQExpBuffer(q, " NOT NULL");
3286
3287         if (tinfo->typdefault)
3288         {
3289                 appendPQExpBuffer(q,
3290                                                   " DEFAULT %s",
3291                                                   tinfo->typdefault);
3292         }
3293
3294         appendPQExpBuffer(q, ";\n");
3295
3296
3297         (*deps)[depIdx++] = NULL;               /* End of List */
3298
3299         ArchiveEntry(fout, tinfo->oid, tinfo->typname, "DOMAIN", deps,
3300                                  q->data, delq->data, "", tinfo->usename, NULL, NULL);
3301
3302         /*** Dump Domain Comments ***/
3303         resetPQExpBuffer(q);
3304
3305         appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->typname, force_quotes));
3306         dumpComment(fout, q->data, tinfo->oid, "pg_type", 0, NULL);
3307 }
3308
3309 /*
3310  * dumpTypes
3311  *        writes out to fout the queries to recreate all the user-defined types
3312  *
3313  */
3314 void
3315 dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
3316                   TypeInfo *tinfo, int numTypes)
3317 {
3318         int                     i;
3319         PQExpBuffer q = createPQExpBuffer();
3320         PQExpBuffer delq = createPQExpBuffer();
3321         int                     funcInd;
3322         const char *((*deps)[]);
3323         int                     depIdx;
3324
3325         for (i = 0; i < numTypes; i++)
3326         {
3327                 /* skip all the builtin types */
3328                 if (atooid(tinfo[i].oid) <= g_last_builtin_oid)
3329                         continue;
3330
3331                 /* skip relation types */
3332                 if (atooid(tinfo[i].typrelid) != 0)
3333                         continue;
3334
3335                 /* skip undefined placeholder types */
3336                 if (!tinfo[i].isDefined)
3337                         continue;
3338
3339                 /* skip all array types that start w/ underscore */
3340                 if ((tinfo[i].typname[0] == '_') &&
3341                         (strcmp(tinfo[i].typinput, "array_in") == 0))
3342                         continue;
3343
3344                 /* Dump out domains as we run across them */
3345                 if (strcmp(tinfo[i].typtype, "d") == 0) {
3346                         dumpOneDomain(fout, &tinfo[i]);
3347                         continue;
3348                 }
3349
3350
3351                 deps = malloc(sizeof(char *) * 10);
3352                 depIdx = 0;
3353
3354                 /*
3355                  * before we create a type, we need to create the input and output
3356                  * functions for it, if they haven't been created already
3357                  */
3358                 funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput);
3359                 if (funcInd != -1)
3360                 {
3361                         (*deps)[depIdx++] = strdup(finfo[funcInd].oid);
3362                         dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes);
3363                 }
3364
3365                 funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput);
3366                 if (funcInd != -1)
3367                 {
3368                         (*deps)[depIdx++] = strdup(finfo[funcInd].oid);
3369                         dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes);
3370                 }
3371
3372                 resetPQExpBuffer(delq);
3373                 appendPQExpBuffer(delq, "DROP TYPE %s;\n", fmtId(tinfo[i].typname, force_quotes));
3374
3375                 resetPQExpBuffer(q);
3376                 appendPQExpBuffer(q,
3377                                                   "CREATE TYPE %s "
3378                                                   "( internallength = %s, externallength = %s,",
3379                                                   fmtId(tinfo[i].typname, force_quotes),
3380                                                   (strcmp(tinfo[i].typlen, "-1") == 0) ?
3381                                                   "variable" : tinfo[i].typlen,
3382                                                   (strcmp(tinfo[i].typprtlen, "-1") == 0) ?
3383                                                   "variable" : tinfo[i].typprtlen);
3384                 /* cannot combine these because fmtId uses static result area */
3385                 appendPQExpBuffer(q, " input = %s,",
3386                                                   fmtId(tinfo[i].typinput, force_quotes));
3387                 appendPQExpBuffer(q, " output = %s,",
3388                                                   fmtId(tinfo[i].typoutput, force_quotes));
3389                 appendPQExpBuffer(q, " send = %s,",
3390                                                   fmtId(tinfo[i].typsend, force_quotes));
3391                 appendPQExpBuffer(q, " receive = %s",
3392                                                   fmtId(tinfo[i].typreceive, force_quotes));
3393
3394                 if (tinfo[i].typdefault != NULL)
3395                 {
3396                         appendPQExpBuffer(q, ", default = ");
3397                         formatStringLiteral(q, tinfo[i].typdefault, CONV_ALL);
3398                 }
3399
3400                 if (tinfo[i].isArray)
3401                 {
3402                         char       *elemType;
3403
3404                         elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque);
3405                         if (elemType == NULL)
3406                         {
3407                                 write_msg(NULL, "notice: array type %s - type for elements (oid %s) is not dumped\n",
3408                                                   tinfo[i].typname, tinfo[i].typelem);
3409                                 continue;
3410                         }
3411
3412                         appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType);
3413                         formatStringLiteral(q, tinfo[i].typdelim, CONV_ALL);
3414
3415                         (*deps)[depIdx++] = strdup(tinfo[i].typelem);
3416                 }
3417
3418                 if (strcmp(tinfo[i].typalign, "c") == 0)
3419                         appendPQExpBuffer(q, ", alignment = char");
3420                 else if (strcmp(tinfo[i].typalign, "s") == 0)
3421                         appendPQExpBuffer(q, ", alignment = int2");
3422                 else if (strcmp(tinfo[i].typalign, "i") == 0)
3423                         appendPQExpBuffer(q, ", alignment = int4");
3424                 else if (strcmp(tinfo[i].typalign, "d") == 0)
3425                         appendPQExpBuffer(q, ", alignment = double");
3426
3427                 if (strcmp(tinfo[i].typstorage, "p") == 0)
3428                         appendPQExpBuffer(q, ", storage = plain");
3429                 else if (strcmp(tinfo[i].typstorage, "e") == 0)
3430                         appendPQExpBuffer(q, ", storage = external");
3431                 else if (strcmp(tinfo[i].typstorage, "x") == 0)
3432                         appendPQExpBuffer(q, ", storage = extended");
3433                 else if (strcmp(tinfo[i].typstorage, "m") == 0)
3434                         appendPQExpBuffer(q, ", storage = main");
3435
3436                 if (tinfo[i].passedbyvalue)
3437                         appendPQExpBuffer(q, ", passedbyvalue);\n");
3438                 else
3439                         appendPQExpBuffer(q, ");\n");
3440
3441                 (*deps)[depIdx++] = NULL;               /* End of List */
3442
3443                 ArchiveEntry(fout, tinfo[i].oid, tinfo[i].typname, "TYPE", deps,
3444                                   q->data, delq->data, "", tinfo[i].usename, NULL, NULL);
3445
3446
3447
3448                 /*** Dump Type Comments ***/
3449
3450                 resetPQExpBuffer(q);
3451
3452                 appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo[i].typname, force_quotes));
3453                 dumpComment(fout, q->data, tinfo[i].oid, "pg_type", 0, NULL);
3454         }
3455
3456         destroyPQExpBuffer(q);
3457         destroyPQExpBuffer(delq);
3458 }
3459
3460 /*
3461  * dumpProcLangs
3462  *                writes out to fout the queries to recreate user-defined procedural languages
3463  *
3464  */
3465 void
3466 dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs,
3467                           TypeInfo *tinfo, int numTypes)
3468 {
3469         PGresult   *res;
3470         PQExpBuffer query = createPQExpBuffer();
3471         PQExpBuffer defqry = createPQExpBuffer();
3472         PQExpBuffer delqry = createPQExpBuffer();
3473         int                     ntups;
3474         int                     i_oid;
3475         int                     i_lanname;
3476         int                     i_lanpltrusted;
3477         int                     i_lanplcallfoid;
3478         int                     i_lancompiler;
3479         Oid                     lanoid;
3480         char       *lanname;
3481         char       *lancompiler;
3482         const char *lanplcallfoid;
3483         int                     i,
3484                                 fidx;
3485
3486         appendPQExpBuffer(query, "SELECT oid, * FROM pg_language "
3487                                           "WHERE lanispl "
3488                                           "ORDER BY oid");
3489         res = PQexec(g_conn, query->data);
3490         if (!res ||
3491                 PQresultStatus(res) != PGRES_TUPLES_OK)
3492         {
3493                 write_msg(NULL, "query to obtain list of procedural languages failed: %s",
3494                                   PQerrorMessage(g_conn));
3495                 exit_nicely();
3496         }
3497         ntups = PQntuples(res);
3498
3499         i_lanname = PQfnumber(res, "lanname");
3500         i_lanpltrusted = PQfnumber(res, "lanpltrusted");
3501         i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
3502         i_lancompiler = PQfnumber(res, "lancompiler");
3503         i_oid = PQfnumber(res, "oid");
3504
3505         for (i = 0; i < ntups; i++)
3506         {
3507                 lanoid = atooid(PQgetvalue(res, i, i_oid));
3508                 if (lanoid <= g_last_builtin_oid)
3509                         continue;
3510
3511                 lanplcallfoid = PQgetvalue(res, i, i_lanplcallfoid);
3512
3513
3514                 for (fidx = 0; fidx < numFuncs; fidx++)
3515                 {
3516                         if (!strcmp(finfo[fidx].oid, lanplcallfoid))
3517                                 break;
3518                 }
3519                 if (fidx >= numFuncs)
3520                 {
3521                         write_msg(NULL, "handler procedure for procedural language %s not found\n",
3522                                           PQgetvalue(res, i, i_lanname));
3523                         exit_nicely();
3524                 }
3525
3526                 dumpOneFunc(fout, finfo, fidx, tinfo, numTypes);
3527
3528                 lanname = PQgetvalue(res, i, i_lanname);
3529                 lancompiler = PQgetvalue(res, i, i_lancompiler);
3530
3531                 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE ");
3532                 formatStringLiteral(delqry, lanname, CONV_ALL);
3533                 appendPQExpBuffer(delqry, ";\n");
3534
3535                 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE ",
3536                                                   (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
3537                                                   "TRUSTED " : "");
3538                 formatStringLiteral(defqry, lanname, CONV_ALL);
3539                 appendPQExpBuffer(defqry, " HANDLER %s LANCOMPILER ",
3540                                                   fmtId(finfo[fidx].proname, force_quotes));
3541                 formatStringLiteral(defqry, lancompiler, CONV_ALL);
3542                 appendPQExpBuffer(defqry, ";\n");
3543
3544                 ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE",
3545                                    NULL, defqry->data, delqry->data, "", "", NULL, NULL);
3546
3547                 resetPQExpBuffer(defqry);
3548                 resetPQExpBuffer(delqry);
3549         }
3550
3551         PQclear(res);
3552
3553         destroyPQExpBuffer(query);
3554         destroyPQExpBuffer(defqry);
3555         destroyPQExpBuffer(delqry);
3556 }
3557
3558 /*
3559  * dumpFuncs
3560  *        writes out to fout the queries to recreate all the user-defined functions
3561  *
3562  */
3563 void
3564 dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs,
3565                   TypeInfo *tinfo, int numTypes)
3566 {
3567         int                     i;
3568
3569         for (i = 0; i < numFuncs; i++)
3570                 dumpOneFunc(fout, finfo, i, tinfo, numTypes);
3571 }
3572
3573 /*
3574  * dumpOneFunc:
3575  *        dump out only one function,  the index of which is given in the third
3576  *      argument
3577  *
3578  */
3579
3580 static void
3581 dumpOneFunc(Archive *fout, FuncInfo *finfo, int i,
3582                         TypeInfo *tinfo, int numTypes)
3583 {
3584         PQExpBuffer q = createPQExpBuffer();
3585         PQExpBuffer fn = createPQExpBuffer();
3586         PQExpBuffer delqry = createPQExpBuffer();
3587         PQExpBuffer fnlist = createPQExpBuffer();
3588         PQExpBuffer asPart = createPQExpBuffer();
3589         char       *func_lang = NULL;
3590         PGresult   *res;
3591         int                     nlangs;
3592         int                     j;
3593         int                     i_lanname;
3594         char            query[256];
3595
3596         char       *listSep;
3597         char       *listSepComma = ",";
3598         char       *listSepNone = "";
3599         char       *rettypename;
3600
3601         if (finfo[i].dumped)
3602                 goto done;
3603
3604         finfo[i].dumped = 1;
3605
3606         /* becomeUser(fout, finfo[i].usename); */
3607
3608         sprintf(query, "SELECT lanname FROM pg_language WHERE oid = '%u'::oid",
3609                         finfo[i].lang);
3610         res = PQexec(g_conn, query);
3611         if (!res ||
3612                 PQresultStatus(res) != PGRES_TUPLES_OK)
3613         {
3614                 write_msg(NULL, "query to get name of procedural language failed: %s", PQerrorMessage(g_conn));
3615                 exit_nicely();
3616         }
3617         nlangs = PQntuples(res);
3618
3619         if (nlangs != 1)
3620         {
3621                 write_msg(NULL, "procedural language for function %s not found\n", finfo[i].proname);
3622                 exit_nicely();
3623         }
3624
3625         i_lanname = PQfnumber(res, "lanname");
3626
3627         /*
3628          * See backend/commands/define.c for details of how the 'AS' clause is
3629          * used.
3630          */
3631         if (strcmp(finfo[i].probin, "-") != 0)
3632         {
3633                 appendPQExpBuffer(asPart, "AS ");
3634                 formatStringLiteral(asPart, finfo[i].probin, CONV_ALL);
3635                 if (strcmp(finfo[i].prosrc, "-") != 0)
3636                 {
3637                         appendPQExpBuffer(asPart, ", ");
3638                         formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB);
3639                 }
3640         }
3641         else
3642         {
3643                 if (strcmp(finfo[i].prosrc, "-") != 0)
3644                 {
3645                         appendPQExpBuffer(asPart, "AS ");
3646                         formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB);
3647                 }
3648         }
3649
3650         func_lang = strdup(PQgetvalue(res, 0, i_lanname));
3651
3652         PQclear(res);
3653
3654         resetPQExpBuffer(fn);
3655         appendPQExpBuffer(fn, "%s (", fmtId(finfo[i].proname, force_quotes));
3656         for (j = 0; j < finfo[i].nargs; j++)
3657         {
3658                 char       *typname;
3659
3660                 typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j], zeroAsOpaque);
3661                 if (typname == NULL)
3662                 {
3663                         write_msg(NULL, "WARNING: function \"%s\" not dumped\n",
3664                                           finfo[i].proname);
3665
3666                         write_msg(NULL, "reason: data type name of argument %d (oid %s) not found\n",
3667                                           j, finfo[i].argtypes[j]);
3668                         goto done;
3669                 }
3670
3671                 appendPQExpBuffer(fn, "%s%s",
3672                                                   (j > 0) ? "," : "",
3673                                                   typname);
3674                 appendPQExpBuffer(fnlist, "%s%s",
3675                                                   (j > 0) ? "," : "",
3676                                                   typname);
3677         }
3678         appendPQExpBuffer(fn, ")");
3679
3680         resetPQExpBuffer(delqry);
3681         appendPQExpBuffer(delqry, "DROP FUNCTION %s;\n", fn->data);
3682
3683         rettypename = findTypeByOid(tinfo, numTypes, finfo[i].prorettype, zeroAsOpaque);
3684
3685         if (rettypename == NULL)
3686         {
3687                 write_msg(NULL, "WARNING: function \"%s\" not dumped\n",
3688                                   finfo[i].proname);
3689
3690                 write_msg(NULL, "reason: name of return data type (oid %s) not found\n",
3691                                   finfo[i].prorettype);
3692                 goto done;
3693         }
3694
3695         resetPQExpBuffer(q);
3696         appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data);
3697         appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE ",
3698                                           (finfo[i].retset) ? "SETOF " : "",
3699                                           rettypename,
3700                                           asPart->data);
3701         formatStringLiteral(q, func_lang, CONV_ALL);
3702
3703         if (finfo[i].provolatile != PROVOLATILE_VOLATILE ||
3704                 finfo[i].isimplicit ||
3705                 finfo[i].isstrict)              /* OR in new attrs here */
3706         {
3707                 appendPQExpBuffer(q, " WITH (");
3708                 listSep = listSepNone;
3709
3710                 if (finfo[i].provolatile == PROVOLATILE_IMMUTABLE)
3711                 {
3712                         appendPQExpBuffer(q, "%s isImmutable", listSep);
3713                         listSep = listSepComma;
3714                 }
3715                 else if (finfo[i].provolatile == PROVOLATILE_STABLE)
3716                 {
3717                         appendPQExpBuffer(q, "%s isStable", listSep);
3718                         listSep = listSepComma;
3719                 }
3720                 else if (finfo[i].provolatile != PROVOLATILE_VOLATILE)
3721                 {
3722                         write_msg(NULL, "Unexpected provolatile value for function %s\n",
3723                                           finfo[i].proname);
3724                         exit_nicely();
3725                 }
3726
3727                 if (finfo[i].isimplicit)
3728                 {
3729                         appendPQExpBuffer(q, "%s implicitCoercion", listSep);
3730                         listSep = listSepComma;
3731                 }
3732
3733                 if (finfo[i].isstrict)
3734                 {
3735                         appendPQExpBuffer(q, "%s isStrict", listSep);
3736                         listSep = listSepComma;
3737                 }
3738
3739                 appendPQExpBuffer(q, " )");
3740         }
3741
3742         appendPQExpBuffer(q, ";\n");
3743
3744         ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data,
3745                                  "", finfo[i].usename, NULL, NULL);
3746
3747         /*** Dump Function Comments ***/
3748
3749         resetPQExpBuffer(q);
3750         appendPQExpBuffer(q, "FUNCTION %s ",
3751                                           fmtId(finfo[i].proname, force_quotes));
3752         appendPQExpBuffer(q, "( %s )", fnlist->data);
3753         dumpComment(fout, q->data, finfo[i].oid, "pg_proc", 0, NULL);
3754
3755 done:
3756         destroyPQExpBuffer(q);
3757         destroyPQExpBuffer(fn);
3758         destroyPQExpBuffer(delqry);
3759         destroyPQExpBuffer(fnlist);
3760         destroyPQExpBuffer(asPart);
3761         free(func_lang);
3762 }
3763
3764 /*
3765  * dumpOprs
3766  *        writes out to fout the queries to recreate all the user-defined operators
3767  *
3768  */
3769 void
3770 dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators,
3771                  TypeInfo *tinfo, int numTypes)
3772 {
3773         int                     i;
3774         PQExpBuffer q = createPQExpBuffer();
3775         PQExpBuffer delq = createPQExpBuffer();
3776         PQExpBuffer leftarg = createPQExpBuffer();
3777         PQExpBuffer rightarg = createPQExpBuffer();
3778         PQExpBuffer commutator = createPQExpBuffer();
3779         PQExpBuffer negator = createPQExpBuffer();
3780         PQExpBuffer restrictor = createPQExpBuffer();
3781         PQExpBuffer join = createPQExpBuffer();
3782         PQExpBuffer sort1 = createPQExpBuffer();
3783         PQExpBuffer sort2 = createPQExpBuffer();
3784
3785         for (i = 0; i < numOperators; i++)
3786         {
3787                 char       *name;
3788
3789                 resetPQExpBuffer(leftarg);
3790                 resetPQExpBuffer(rightarg);
3791                 resetPQExpBuffer(commutator);
3792                 resetPQExpBuffer(negator);
3793                 resetPQExpBuffer(restrictor);
3794                 resetPQExpBuffer(join);
3795                 resetPQExpBuffer(sort1);
3796                 resetPQExpBuffer(sort2);
3797
3798                 /* skip all the builtin oids */
3799                 if (atooid(oprinfo[i].oid) <= g_last_builtin_oid)
3800                         continue;
3801
3802                 /*
3803                  * some operator are invalid because they were the result of user
3804                  * defining operators before commutators exist
3805                  */
3806                 if (strcmp(oprinfo[i].oprcode, "-") == 0)
3807                         continue;
3808
3809                 /*
3810                  * right unary means there's a left arg and left unary means
3811                  * there's a right arg
3812                  */
3813                 if (strcmp(oprinfo[i].oprkind, "r") == 0 ||
3814                         strcmp(oprinfo[i].oprkind, "b") == 0)
3815                 {
3816                         name = findTypeByOid(tinfo, numTypes,
3817                                                                  oprinfo[i].oprleft, zeroAsOpaque);
3818                         if (name == NULL)
3819                         {
3820                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3821                                                   oprinfo[i].oprname, oprinfo[i].oid);
3822                                 write_msg(NULL, "reason: oprleft (oid %s) not found\n",
3823                                                   oprinfo[i].oprleft);
3824                                 continue;
3825                         }
3826                         appendPQExpBuffer(leftarg, ",\n\tLEFTARG = %s ", name);
3827                 }
3828
3829                 if (strcmp(oprinfo[i].oprkind, "l") == 0 ||
3830                         strcmp(oprinfo[i].oprkind, "b") == 0)
3831                 {
3832                         name = findTypeByOid(tinfo, numTypes,
3833                                                                  oprinfo[i].oprright, zeroAsOpaque);
3834                         if (name == NULL)
3835                         {
3836                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3837                                                   oprinfo[i].oprname, oprinfo[i].oid);
3838                                 write_msg(NULL, "reason: oprright (oid %s) not found\n",
3839                                                   oprinfo[i].oprright);
3840                                 continue;
3841                         }
3842                         appendPQExpBuffer(rightarg, ",\n\tRIGHTARG = %s ", name);
3843                 }
3844
3845                 if (!(strcmp(oprinfo[i].oprcom, "0") == 0))
3846                 {
3847                         name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom);
3848                         if (name == NULL)
3849                         {
3850                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3851                                                   oprinfo[i].oprname, oprinfo[i].oid);
3852                                 write_msg(NULL, "reason: oprcom (oid %s) not found\n",
3853                                                   oprinfo[i].oprcom);
3854                                 continue;
3855                         }
3856                         appendPQExpBuffer(commutator, ",\n\tCOMMUTATOR = %s ", name);
3857                 }
3858
3859                 if (!(strcmp(oprinfo[i].oprnegate, "0") == 0))
3860                 {
3861                         name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate);
3862                         if (name == NULL)
3863                         {
3864                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3865                                                   oprinfo[i].oprname, oprinfo[i].oid);
3866                                 write_msg(NULL, "reason: oprnegate (oid %s) not found\n",
3867                                                   oprinfo[i].oprnegate);
3868                                 continue;
3869                         }
3870                         appendPQExpBuffer(negator, ",\n\tNEGATOR = %s ", name);
3871                 }
3872
3873                 if (!(strcmp(oprinfo[i].oprrest, "-") == 0))
3874                         appendPQExpBuffer(restrictor, ",\n\tRESTRICT = %s ", oprinfo[i].oprrest);
3875
3876                 if (!(strcmp(oprinfo[i].oprjoin, "-") == 0))
3877                         appendPQExpBuffer(join, ",\n\tJOIN = %s ", oprinfo[i].oprjoin);
3878
3879                 if (!(strcmp(oprinfo[i].oprlsortop, "0") == 0))
3880                 {
3881                         name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprlsortop);
3882                         if (name == NULL)
3883                         {
3884                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3885                                                   oprinfo[i].oprname, oprinfo[i].oid);
3886                                 write_msg(NULL, "reason: oprlsortop (oid %s) not found\n",
3887                                                   oprinfo[i].oprlsortop);
3888                                 continue;
3889                         }
3890                         appendPQExpBuffer(sort1, ",\n\tSORT1 = %s ", name);
3891                 }
3892
3893                 if (!(strcmp(oprinfo[i].oprrsortop, "0") == 0))
3894                 {
3895                         name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprrsortop);
3896                         if (name == NULL)
3897                         {
3898                                 write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n",
3899                                                   oprinfo[i].oprname, oprinfo[i].oid);
3900                                 write_msg(NULL, "reason: oprrsortop (oid %s) not found\n",
3901                                                   oprinfo[i].oprrsortop);
3902                                 continue;
3903                         }
3904                         appendPQExpBuffer(sort2, ",\n\tSORT2 = %s ", name);
3905                 }
3906
3907                 resetPQExpBuffer(delq);
3908                 appendPQExpBuffer(delq, "DROP OPERATOR %s (%s",
3909                                                   oprinfo[i].oprname,
3910                                            findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft,
3911                                                                          zeroAsNone));
3912                 appendPQExpBuffer(delq, ", %s);\n",
3913                                           findTypeByOid(tinfo, numTypes, oprinfo[i].oprright,
3914                                                                         zeroAsNone));
3915
3916                 resetPQExpBuffer(q);
3917                 appendPQExpBuffer(q,
3918                                                   "CREATE OPERATOR %s "
3919                                                   "(PROCEDURE = %s %s%s%s%s%s%s%s%s%s);\n",
3920                                                   oprinfo[i].oprname,
3921                                                   oprinfo[i].oprcode,
3922                                                   leftarg->data,
3923                                                   rightarg->data,
3924                                                   commutator->data,
3925                                                   negator->data,
3926                                                   restrictor->data,
3927                   (strcmp(oprinfo[i].oprcanhash, "t") == 0) ? ",\n\tHASHES" : "",
3928                                                   join->data,
3929                                                   sort1->data,
3930                                                   sort2->data);
3931
3932                 ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL,
3933                                 q->data, delq->data, "", oprinfo[i].usename, NULL, NULL);
3934         }
3935
3936         destroyPQExpBuffer(q);
3937         destroyPQExpBuffer(delq);
3938         destroyPQExpBuffer(leftarg);
3939         destroyPQExpBuffer(rightarg);
3940         destroyPQExpBuffer(commutator);
3941         destroyPQExpBuffer(negator);
3942         destroyPQExpBuffer(restrictor);
3943         destroyPQExpBuffer(join);
3944         destroyPQExpBuffer(sort1);
3945         destroyPQExpBuffer(sort2);
3946 }
3947
3948 /*
3949  * dumpAggs
3950  *        writes out to fout the queries to create all the user-defined aggregates
3951  *
3952  */
3953 void
3954 dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
3955                  TypeInfo *tinfo, int numTypes)
3956 {
3957         int                     i;
3958         PQExpBuffer q = createPQExpBuffer();
3959         PQExpBuffer delq = createPQExpBuffer();
3960         PQExpBuffer aggSig = createPQExpBuffer();
3961         PQExpBuffer details = createPQExpBuffer();
3962
3963         for (i = 0; i < numAggs; i++)
3964         {
3965                 char       *name;
3966
3967                 resetPQExpBuffer(details);
3968
3969                 /* skip all the builtin oids */
3970                 if (oidle(atooid(agginfo[i].oid), g_last_builtin_oid))
3971                         continue;
3972
3973                 resetPQExpBuffer(aggSig);
3974                 appendPQExpBuffer(aggSig, "%s(%s)", agginfo[i].aggname,
3975                                                   findTypeByOid(tinfo, numTypes,
3976                                                                                 agginfo[i].aggbasetype,
3977                                                                                 zeroAsStar + useBaseTypeName));
3978
3979                 if (!agginfo[i].convertok)
3980                 {
3981                         write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
3982                                           aggSig->data);
3983
3984                         resetPQExpBuffer(q);
3985                         appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
3986                                                           aggSig->data);
3987                         ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
3988                          q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
3989                         continue;
3990                 }
3991
3992                 name = findTypeByOid(tinfo, numTypes, agginfo[i].aggbasetype,
3993                                                          zeroAsAny + useBaseTypeName);
3994                 if (name == NULL)
3995                 {
3996                         write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n",
3997                                           agginfo[i].aggname, agginfo[i].oid);
3998                         write_msg(NULL, "reason: aggbasetype (oid %s) not found\n",
3999                                           agginfo[i].aggbasetype);
4000
4001                         resetPQExpBuffer(q);
4002                         appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid);
4003                         appendPQExpBuffer(q, "-- reason: aggbasetype (oid %s) not found\n", agginfo[i].aggbasetype);
4004                         ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
4005                          q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
4006                         continue;
4007                 }
4008                 appendPQExpBuffer(details, "BASETYPE = %s, ", name);
4009
4010                 name = findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype,
4011                                                          zeroAsOpaque + useBaseTypeName);
4012                 if (name == NULL)
4013                 {
4014                         write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n",
4015                                           agginfo[i].aggname, agginfo[i].oid);
4016                         write_msg(NULL, "reason: aggtranstype (oid %s) not found\n",
4017                                           agginfo[i].aggtranstype);
4018
4019                         resetPQExpBuffer(q);
4020                         appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid);
4021                         appendPQExpBuffer(q, "-- reason: aggtranstype (oid %s) not found\n", agginfo[i].aggtranstype);
4022                         ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL,
4023                          q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL);
4024                         continue;
4025                 }
4026                 appendPQExpBuffer(details,
4027                                                   "SFUNC = %s, STYPE = %s",
4028                                                   agginfo[i].aggtransfn, name);
4029
4030                 if (agginfo[i].agginitval)
4031                 {
4032                         appendPQExpBuffer(details, ", INITCOND = ");
4033                         formatStringLiteral(details, agginfo[i].agginitval, CONV_ALL);
4034                 }
4035
4036                 if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0))
4037                         appendPQExpBuffer(details, ", FINALFUNC = %s",
4038                                                           agginfo[i].aggfinalfn);
4039
4040                 resetPQExpBuffer(delq);
4041                 appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data);
4042
4043                 resetPQExpBuffer(q);
4044                 appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n",
4045                                                   agginfo[i].aggname,
4046                                                   details->data);
4047
4048                 ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
4049                                 q->data, delq->data, "", agginfo[i].usename, NULL, NULL);
4050
4051                 /*** Dump Aggregate Comments ***/
4052
4053                 resetPQExpBuffer(q);
4054                 appendPQExpBuffer(q, "AGGREGATE %s", aggSig->data);
4055                 if (g_fout->remoteVersion < 70300)
4056                         dumpComment(fout, q->data, agginfo[i].oid, "pg_aggregate",
4057                                                 0, NULL);
4058                 else
4059                         dumpComment(fout, q->data, agginfo[i].oid, "pg_proc",
4060                                                 0, NULL);
4061         }
4062
4063         destroyPQExpBuffer(q);
4064         destroyPQExpBuffer(delq);
4065         destroyPQExpBuffer(aggSig);
4066         destroyPQExpBuffer(details);
4067 }
4068
4069 /*
4070  * These are some support functions to fix the acl problem of pg_dump
4071  *
4072  * Matthew C. Aycock 12/02/97
4073  */
4074
4075 /* Append a keyword to a keyword list, inserting comma if needed.
4076  * Caller must make aclbuf big enough for all possible keywords.
4077  */
4078 static void
4079 AddAcl(char *aclbuf, const char *keyword)
4080 {
4081         if (*aclbuf)
4082                 strcat(aclbuf, ",");
4083         strcat(aclbuf, keyword);
4084 }
4085
4086 /*
4087  * This will take a string of privilege code letters and return a malloced,
4088  * comma delimited string of keywords for GRANT.
4089  *
4090  * Note: for cross-version compatibility, it's important to use ALL when
4091  * appropriate.
4092  */
4093 static char *
4094 GetPrivileges(Archive *AH, const char *s)
4095 {
4096         char            aclbuf[100];
4097         bool            all = true;
4098
4099         aclbuf[0] = '\0';
4100
4101 #define CONVERT_PRIV(code,keywd) \
4102         if (strchr(s, code)) \
4103                 AddAcl(aclbuf, keywd); \
4104         else \
4105                 all = false
4106
4107         CONVERT_PRIV('a', "INSERT");
4108         CONVERT_PRIV('r', "SELECT");
4109         CONVERT_PRIV('R', "RULE");
4110
4111         if (AH->remoteVersion >= 70200)
4112         {
4113                 CONVERT_PRIV('w', "UPDATE");
4114                 CONVERT_PRIV('d', "DELETE");
4115                 CONVERT_PRIV('x', "REFERENCES");
4116                 CONVERT_PRIV('t', "TRIGGER");
4117         }
4118         else
4119         {
4120                 /* 7.0 and 7.1 have a simpler worldview */
4121                 CONVERT_PRIV('w', "UPDATE,DELETE");
4122         }
4123
4124 #undef CONVERT_PRIV
4125
4126         if (all)
4127                 return strdup("ALL");
4128         else
4129                 return strdup(aclbuf);
4130 }
4131
4132 /*
4133  * The name says it all; a function to append a string if the dest
4134  * is big enough. If not, it does a realloc.
4135  */
4136 static void
4137 strcatalloc(char **dest, int *dSize, char *src)
4138 {
4139         int                     dLen = strlen(*dest);
4140         int                     sLen = strlen(src);
4141
4142         if ((dLen + sLen) >= *dSize)
4143         {
4144                 *dSize = (dLen + sLen) * 2;
4145                 *dest = realloc(*dest, *dSize);
4146         }
4147         strcpy(*dest + dLen, src);
4148 }
4149
4150
4151 /*
4152  * dumpACL:
4153  *        Write out grant/revoke information
4154  *        Called for sequences and tables
4155  */
4156
4157 static void
4158 dumpACL(Archive *fout, TableInfo tbinfo)
4159 {
4160         const char *acls = tbinfo.relacl;
4161         char       *aclbuf,
4162                            *tok,
4163                            *eqpos,
4164                            *priv;
4165         char       *objoid;
4166         char       *sql;
4167         char            tmp[1024];
4168         int                     sSize = 4096;
4169
4170         if (strlen(acls) == 0)
4171                 return;                                 /* table has default permissions */
4172
4173         /*
4174          * Allocate a larginsh buffer for the output SQL.
4175          */
4176         sql = (char *) malloc(sSize);
4177
4178         /*
4179          * Revoke Default permissions for PUBLIC. Is this actually necessary,
4180          * or is it just a waste of time?
4181          */
4182         sprintf(sql, "REVOKE ALL on %s from PUBLIC;\n",
4183                         fmtId(tbinfo.relname, force_quotes));
4184
4185         /* Make a working copy of acls so we can use strtok */
4186         aclbuf = strdup(acls);
4187
4188         /* Scan comma-separated ACL items */
4189         for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ","))
4190         {
4191                 /*
4192                  * Token may start with '{' and/or '"'.  Actually only the start
4193                  * of the string should have '{', but we don't verify that.
4194                  */
4195                 if (*tok == '{')
4196                         tok++;
4197                 if (*tok == '"')
4198                         tok++;
4199
4200                 /* User name is string up to = in tok */
4201                 eqpos = strchr(tok, '=');
4202                 if (!eqpos)
4203                 {
4204                         write_msg(NULL, "could not parse ACL list ('%s') for relation %s\n",
4205                                           acls, tbinfo.relname);
4206                         exit_nicely();
4207                 }
4208
4209                 /*
4210                  * Parse the privileges (right-hand side).      Skip if there are
4211                  * none.
4212                  */
4213                 priv = GetPrivileges(fout, eqpos + 1);
4214                 if (*priv)
4215                 {
4216                         sprintf(tmp, "GRANT %s on %s to ",
4217                                         priv, fmtId(tbinfo.relname, force_quotes));
4218                         strcatalloc(&sql, &sSize, tmp);
4219
4220                         /*
4221                          * Note: fmtId() can only be called once per printf, so don't
4222                          * try to merge printing of username into the above printf.
4223                          */
4224                         if (eqpos == tok)
4225                         {
4226                                 /* Empty left-hand side means "PUBLIC" */
4227                                 strcatalloc(&sql, &sSize, "PUBLIC;\n");
4228                         }
4229                         else
4230                         {
4231                                 *eqpos = '\0';  /* it's ok to clobber aclbuf */
4232                                 if (strncmp(tok, "group ", strlen("group ")) == 0)
4233                                         sprintf(tmp, "GROUP %s;\n",
4234                                                         fmtId(tok + strlen("group "), force_quotes));
4235                                 else
4236                                         sprintf(tmp, "%s;\n", fmtId(tok, force_quotes));
4237                                 strcatalloc(&sql, &sSize, tmp);
4238                         }
4239                 }
4240                 free(priv);
4241         }
4242
4243         free(aclbuf);
4244
4245         if (tbinfo.viewdef != NULL)
4246                 objoid = tbinfo.viewoid;
4247         else
4248                 objoid = tbinfo.oid;
4249
4250         ArchiveEntry(fout, objoid, tbinfo.relname, "ACL", NULL, sql, "", "", "", NULL, NULL);
4251 }
4252
4253 static void
4254 _dumpTableAttr70(TableInfo *tblinfo, int i, int j, PQExpBuffer q)
4255 {
4256         int32           tmp_typmod;
4257         int                     precision;
4258         int                     scale;
4259
4260         /* Show lengths on bpchar and varchar */
4261         if (!strcmp(tblinfo[i].typnames[j], "bpchar"))
4262         {
4263                 int                     len = (tblinfo[i].atttypmod[j] - VARHDRSZ);
4264
4265                 appendPQExpBuffer(q, "character");
4266                 if (len > 1)
4267                         appendPQExpBuffer(q, "(%d)",
4268                                                           tblinfo[i].atttypmod[j] - VARHDRSZ);
4269         }
4270         else if (!strcmp(tblinfo[i].typnames[j], "varchar"))
4271         {
4272                 appendPQExpBuffer(q, "character varying");
4273                 if (tblinfo[i].atttypmod[j] != -1)
4274                 {
4275                         appendPQExpBuffer(q, "(%d)",
4276                                                           tblinfo[i].atttypmod[j] - VARHDRSZ);
4277                 }
4278         }
4279         else if (!strcmp(tblinfo[i].typnames[j], "numeric"))
4280         {
4281                 appendPQExpBuffer(q, "numeric");
4282                 if (tblinfo[i].atttypmod[j] != -1)
4283                 {
4284                         tmp_typmod = tblinfo[i].atttypmod[j] - VARHDRSZ;
4285                         precision = (tmp_typmod >> 16) & 0xffff;
4286                         scale = tmp_typmod & 0xffff;
4287                         appendPQExpBuffer(q, "(%d,%d)",
4288                                                           precision, scale);
4289                 }
4290         }
4291
4292         /*
4293          * char is an internal single-byte data type; Let's make sure we force
4294          * it through with quotes. - thomas 1998-12-13
4295          */
4296         else if (!strcmp(tblinfo[i].typnames[j], "char"))
4297         {
4298                 appendPQExpBuffer(q, "%s",
4299                                                   fmtId(tblinfo[i].typnames[j], true));
4300         }
4301         else
4302         {
4303                 appendPQExpBuffer(q, "%s",
4304                                                   fmtId(tblinfo[i].typnames[j], false));
4305         }
4306 }
4307
4308 /*
4309  * dumpTables:
4310  *        write out to fout all the user-define tables
4311  */
4312
4313 void
4314 dumpTables(Archive *fout, TableInfo *tblinfo, int numTables,
4315                    const char *tablename, const bool aclsSkip,
4316                    const bool schemaOnly, const bool dataOnly)
4317 {
4318         int                     i,
4319                                 j,
4320                                 k;
4321         PQExpBuffer q = createPQExpBuffer();
4322         PQExpBuffer delq = createPQExpBuffer();
4323         char       *serialSeq = NULL;           /* implicit sequence name created
4324                                                                                  * by SERIAL datatype */
4325         const char *serialSeqSuffix = "_id_seq";        /* suffix for implicit
4326                                                                                                  * SERIAL sequences */
4327         char      **parentRels;         /* list of names of parent relations */
4328         int                     numParents;
4329         int                     actual_atts;    /* number of attrs in this CREATE statment */
4330         char       *reltypename;
4331         char       *objoid;
4332         const char *((*commentDeps)[]);
4333
4334         /* First - dump SEQUENCEs */
4335         if (tablename && strlen(tablename) > 0)
4336         {
4337                 /* XXX this code only works for serial columns named "id" */
4338                 /* We really need dependency analysis! */
4339                 serialSeq = malloc(strlen(tablename) + strlen(serialSeqSuffix) + 1);
4340                 strcpy(serialSeq, tablename);
4341                 strcat(serialSeq, serialSeqSuffix);
4342         }
4343         for (i = 0; i < numTables; i++)
4344         {
4345                 if (tblinfo[i].relkind != RELKIND_SEQUENCE)
4346                         continue;
4347                 if (!tablename || (!strcmp(tblinfo[i].relname, tablename))
4348                         || (serialSeq && !strcmp(tblinfo[i].relname, serialSeq)))
4349                 {
4350                         /* becomeUser(fout, tblinfo[i].usename); */
4351                         dumpSequence(fout, tblinfo[i], schemaOnly, dataOnly);
4352                         if (!aclsSkip)
4353                                 dumpACL(fout, tblinfo[i]);
4354                 }
4355         }
4356         if (serialSeq)
4357                 free(serialSeq);
4358
4359         for (i = 0; i < numTables; i++)
4360         {
4361                 if (tblinfo[i].relkind == RELKIND_SEQUENCE)             /* already dumped */
4362                         continue;
4363
4364                 if (!tablename || (!strcmp(tblinfo[i].relname, tablename)) || (strlen(tablename) == 0))
4365                 {
4366
4367                         resetPQExpBuffer(delq);
4368                         resetPQExpBuffer(q);
4369
4370                         /* Use the view definition if there is one */
4371                         if (tblinfo[i].viewdef != NULL)
4372                         {
4373                                 reltypename = "VIEW";
4374                                 objoid = tblinfo[i].viewoid;
4375                                 appendPQExpBuffer(delq, "DROP VIEW %s;\n", fmtId(tblinfo[i].relname, force_quotes));
4376                                 appendPQExpBuffer(q, "CREATE VIEW %s as %s\n", fmtId(tblinfo[i].relname, force_quotes), tblinfo[i].viewdef);
4377
4378                                 /*
4379                                  * Views can have default values -- however, they must be
4380                                  * specified in an ALTER TABLE command after the view has
4381                                  * been created, not in the view definition itself.
4382                                  */
4383                                 for (j = 0; j < tblinfo[i].numatts; j++)
4384                                 {
4385                                         if (tblinfo[i].adef_expr[j] != NULL && tblinfo[i].inhAttrDef[j] == 0) {
4386                                                 appendPQExpBuffer(q, "ALTER TABLE %s ", fmtId(tblinfo[i].relname, force_quotes));
4387                                                 appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
4388                                                                                   fmtId(tblinfo[i].attnames[j], force_quotes),
4389                                                                                   tblinfo[i].adef_expr[j]);
4390                                         }
4391                                 }
4392
4393                                 commentDeps = malloc(sizeof(char *) * 2);
4394                                 (*commentDeps)[0] = strdup(objoid);
4395                                 (*commentDeps)[1] = NULL;               /* end of list */
4396                         }
4397                         else
4398                         {
4399                                 reltypename = "TABLE";
4400                                 objoid = tblinfo[i].oid;
4401                                 commentDeps = NULL;
4402                                 parentRels = tblinfo[i].parentRels;
4403                                 numParents = tblinfo[i].numParents;
4404
4405                                 appendPQExpBuffer(delq, "DROP TABLE %s;\n", fmtId(tblinfo[i].relname, force_quotes));
4406
4407                                 appendPQExpBuffer(q, "CREATE TABLE %s (\n\t", fmtId(tblinfo[i].relname, force_quotes));
4408                                 actual_atts = 0;
4409                                 for (j = 0; j < tblinfo[i].numatts; j++)
4410                                 {
4411                                         /* Is this one of the table's own attrs ? */
4412                                         if (tblinfo[i].inhAttrs[j] == 0)
4413                                         {
4414                                                 /* Format properly if not first attr */
4415                                                 if (actual_atts > 0)
4416                                                         appendPQExpBuffer(q, ",\n\t");
4417
4418                                                 /* Attr name & type */
4419                                                 appendPQExpBuffer(q, "%s ", fmtId(tblinfo[i].attnames[j], force_quotes));
4420
4421                                                 if (g_fout->remoteVersion >= 70100)
4422                                                         appendPQExpBuffer(q, "%s", tblinfo[i].atttypedefns[j]);
4423                                                 else
4424                                                         _dumpTableAttr70(tblinfo, i, j, q);
4425
4426                                                 /* Default value */
4427                                                 if (tblinfo[i].adef_expr[j] != NULL && tblinfo[i].inhAttrDef[j] == 0)
4428                                                         appendPQExpBuffer(q, " DEFAULT %s",
4429                                                                                           tblinfo[i].adef_expr[j]);
4430
4431                                                 /* Not Null constraint */
4432                                                 if (tblinfo[i].notnull[j] && tblinfo[i].inhNotNull[j] == 0)
4433                                                         appendPQExpBuffer(q, " NOT NULL");
4434
4435                                                 actual_atts++;
4436                                         }
4437                                 }
4438
4439
4440
4441                                 /* Put the CONSTRAINTS inside the table def */
4442                                 for (k = 0; k < tblinfo[i].ncheck; k++)
4443                                 {
4444                                         if (actual_atts + k > 0)
4445                                                 appendPQExpBuffer(q, ",\n\t");
4446
4447                                         appendPQExpBuffer(q, "%s",
4448                                                                           tblinfo[i].check_expr[k]);
4449                                 }
4450
4451                                 /*
4452                                  * Primary Key: In versions of PostgreSQL prior to 7.2, we
4453                                  * needed to include the primary key in the table definition.
4454                                  * However, this is not ideal because it creates an index
4455                                  * on the table, which makes COPY slower. As of release 7.2,
4456                                  * we can add primary keys to a table after is has been created,
4457                                  * using ALTER TABLE ; see dumpIndexes() for more information.
4458                                  * Therefore, we ignore primary keys in this function.
4459                                  */
4460
4461                                 appendPQExpBuffer(q, "\n)");
4462
4463                                 if (numParents > 0)
4464                                 {
4465                                         appendPQExpBuffer(q, "\nINHERITS (");
4466                                         for (k = 0; k < numParents; k++)
4467                                         {
4468                                                 appendPQExpBuffer(q, "%s%s",
4469                                                                                   (k > 0) ? ", " : "",
4470                                                                          fmtId(parentRels[k], force_quotes));
4471                                         }
4472                                         appendPQExpBuffer(q, ")");
4473                                 }
4474
4475                                 if (!tblinfo[i].hasoids)
4476                                         appendPQExpBuffer(q, " WITHOUT OIDS");
4477
4478                                 appendPQExpBuffer(q, ";\n");
4479                         }
4480
4481                         if (!dataOnly)
4482                         {
4483
4484                                 ArchiveEntry(fout, objoid, tblinfo[i].relname,
4485                                                          reltypename, NULL, q->data, delq->data, "", tblinfo[i].usename,
4486                                                          NULL, NULL);
4487
4488                                 if (!aclsSkip)
4489                                         dumpACL(fout, tblinfo[i]);
4490
4491                         }
4492
4493                         /* Dump Field Comments */
4494
4495                         for (j = 0; j < tblinfo[i].numatts; j++)
4496                         {
4497                                 resetPQExpBuffer(q);
4498                                 appendPQExpBuffer(q, "COLUMN %s", fmtId(tblinfo[i].relname, force_quotes));
4499                                 appendPQExpBuffer(q, ".");
4500                                 appendPQExpBuffer(q, "%s", fmtId(tblinfo[i].attnames[j], force_quotes));
4501                                 dumpComment(fout, q->data, tblinfo[i].oid,
4502                                                         "pg_class", j + 1, commentDeps);
4503                         }
4504
4505                         /* Dump Table Comments */
4506
4507                         resetPQExpBuffer(q);
4508                         appendPQExpBuffer(q, "%s %s", reltypename, fmtId(tblinfo[i].relname, force_quotes));
4509                         dumpComment(fout, q->data, tblinfo[i].oid,
4510                                                 "pg_class", 0, commentDeps);
4511
4512                 }
4513         }
4514
4515         destroyPQExpBuffer(q);
4516         destroyPQExpBuffer(delq);
4517 }
4518
4519 static PQExpBuffer
4520 getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo)
4521 {
4522         PQExpBuffer pkBuf = createPQExpBuffer();
4523         int                     k;
4524
4525         appendPQExpBuffer(pkBuf, "Constraint %s Primary Key (",
4526                                           tblInfo->primary_key_name);
4527
4528         for (k = 0; k < indInfo->indnkeys; k++)
4529         {
4530                 int                     indkey;
4531                 const char *attname;
4532
4533                 indkey = atoi(indInfo->indkey[k]);
4534                 if (indkey == InvalidAttrNumber)
4535                         break;
4536                 attname = getAttrName(indkey, tblInfo);
4537
4538                 appendPQExpBuffer(pkBuf, "%s%s",
4539                                                   (k == 0) ? "" : ", ",
4540                                                   fmtId(attname, force_quotes));
4541         }
4542
4543         appendPQExpBuffer(pkBuf, ")");
4544
4545         return pkBuf;
4546 }
4547
4548 /*
4549  * getAttrName: extract the correct name for an attribute
4550  *
4551  * The array tblInfo->attnames[] only provides names of user attributes;
4552  * if a system attribute number is supplied, we have to fake it.
4553  * We also do a little bit of bounds checking for safety's sake.
4554  */
4555 static const char *
4556 getAttrName(int attrnum, TableInfo *tblInfo)
4557 {
4558         if (attrnum > 0 && attrnum <= tblInfo->numatts)
4559                 return tblInfo->attnames[attrnum - 1];
4560         switch (attrnum)
4561         {
4562                 case SelfItemPointerAttributeNumber:
4563                         return "ctid";
4564                 case ObjectIdAttributeNumber:
4565                         return "oid";
4566                 case MinTransactionIdAttributeNumber:
4567                         return "xmin";
4568                 case MinCommandIdAttributeNumber:
4569                         return "cmin";
4570                 case MaxTransactionIdAttributeNumber:
4571                         return "xmax";
4572                 case MaxCommandIdAttributeNumber:
4573                         return "cmax";
4574                 case TableOidAttributeNumber:
4575                         return "tableoid";
4576         }
4577         write_msg(NULL, "getAttrName(): invalid column number %d for table %s\n",
4578                           attrnum, tblInfo->relname);
4579         exit_nicely();
4580         return NULL;                            /* keep compiler quiet */
4581 }
4582
4583 /*
4584  * dumpIndexes:
4585  *        write out to fout all the user-defined indexes
4586  */
4587 void
4588 dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes,
4589                         TableInfo *tblinfo, int numTables, const char *tablename)
4590 {
4591         int                     i;
4592         int                     tableInd;
4593         PQExpBuffer q = createPQExpBuffer();
4594         PQExpBuffer delq = createPQExpBuffer();
4595         PQExpBuffer id1 = createPQExpBuffer();
4596
4597         for (i = 0; i < numIndexes; i++)
4598         {
4599                 if (tablename && tablename[0] &&
4600                         (strcmp(indinfo[i].indrelname, tablename) != 0))
4601                         continue;
4602
4603                 tableInd = findTableByName(tblinfo, numTables,
4604                                                                    indinfo[i].indrelname);
4605                 if (tableInd < 0)
4606                 {
4607                         write_msg(NULL, "dumpIndexes(): failed sanity check, table %s was not found\n",
4608                                           indinfo[i].indrelname);
4609                         exit_nicely();
4610                 }
4611
4612                 /* Handle PK indexes */
4613                 if (strcmp(indinfo[i].indisprimary, "t") == 0)
4614                 {
4615                         PQExpBuffer consDef = getPKconstraint(&tblinfo[tableInd], &indinfo[i]);
4616
4617                         resetPQExpBuffer(q);
4618
4619                         appendPQExpBuffer(q, "Alter Table %s Add %s;",
4620                                                           fmtId(tblinfo[tableInd].relname, force_quotes),
4621                                                           consDef->data);
4622
4623                         ArchiveEntry(fout, indinfo[i].indexreloid, tblinfo[tableInd].primary_key_name,
4624                                                  "CONSTRAINT", NULL, q->data, "",
4625                                                  "", tblinfo[tableInd].usename, NULL, NULL);
4626
4627                         destroyPQExpBuffer(consDef);
4628
4629                         /*
4630                          * Don't need to do anything else for this system-generated
4631                          * index
4632                          */
4633                         continue;
4634                 }
4635
4636                 resetPQExpBuffer(id1);
4637                 appendPQExpBuffer(id1, fmtId(indinfo[i].indexrelname, force_quotes));
4638
4639                 resetPQExpBuffer(q);
4640                 appendPQExpBuffer(q, "%s;\n", indinfo[i].indexdef);
4641
4642                 resetPQExpBuffer(delq);
4643                 appendPQExpBuffer(delq, "DROP INDEX %s;\n", id1->data);
4644
4645                 /*
4646                  * We make the index belong to the owner of its table, which is
4647                  * not necessarily right but should answer 99% of the time. Would
4648                  * have to add owner name to IndInfo to do it right.
4649                  */
4650                 ArchiveEntry(fout, indinfo[i].indexreloid, id1->data,
4651                                          "INDEX", NULL, q->data, delq->data,
4652                                          "", tblinfo[tableInd].usename, NULL, NULL);
4653
4654                 /* Dump Index Comments */
4655                 resetPQExpBuffer(q);
4656                 appendPQExpBuffer(q, "INDEX %s", id1->data);
4657                 dumpComment(fout, q->data, indinfo[i].indexreloid,
4658                                         "pg_class", 0, NULL);
4659         }
4660
4661         destroyPQExpBuffer(q);
4662         destroyPQExpBuffer(delq);
4663         destroyPQExpBuffer(id1);
4664 }
4665
4666 /*
4667  * dumpTuples
4668  *        prints out the tuples in ASCII representation. The output is a valid
4669  *        input to COPY FROM stdin.
4670  *
4671  *        We only need to do this for POSTGRES 4.2 databases since the
4672  *        COPY TO statment doesn't escape newlines properly. It's been fixed
4673  *        in PostgreSQL.
4674  *
4675  * the attrmap passed in tells how to map the attributes copied in to the
4676  * attributes copied out
4677  */
4678 #ifdef NOT_USED
4679 void
4680 dumpTuples(PGresult *res, FILE *fout, int *attrmap)
4681 {
4682         int                     j,
4683                                 k;
4684         int                     m,
4685                                 n;
4686         char      **outVals = NULL; /* values to copy out */
4687
4688         n = PQntuples(res);
4689         m = PQnfields(res);
4690
4691         if (m > 0)
4692         {
4693                 /*
4694                  * Print out the tuples but only print tuples with at least 1
4695                  * field.
4696                  */
4697                 outVals = (char **) malloc(m * sizeof(char *));
4698
4699                 for (j = 0; j < n; j++)
4700                 {
4701                         for (k = 0; k < m; k++)
4702                                 outVals[attrmap[k]] = PQgetvalue(res, j, k);
4703                         for (k = 0; k < m; k++)
4704                         {
4705                                 char       *pval = outVals[k];
4706
4707                                 if (k != 0)
4708                                         fputc('\t', fout);      /* delimiter for attribute */
4709
4710                                 if (pval)
4711                                 {
4712                                         while (*pval != '\0')
4713                                         {
4714                                                 /* escape tabs, newlines and backslashes */
4715                                                 if (*pval == '\t' || *pval == '\n' || *pval == '\\')
4716                                                         fputc('\\', fout);
4717                                                 fputc(*pval, fout);
4718                                                 pval++;
4719                                         }
4720                                 }
4721                         }
4722                         fputc('\n', fout);      /* delimiter for a tuple */
4723                 }
4724                 free(outVals);
4725         }
4726 }
4727 #endif
4728
4729 /*
4730  * setMaxOid -
4731  * find the maximum oid and generate a COPY statement to set it
4732 */
4733
4734 static void
4735 setMaxOid(Archive *fout)
4736 {
4737         PGresult   *res;
4738         Oid                     max_oid;
4739         char            sql[1024];
4740
4741         res = PQexec(g_conn, "CREATE TEMPORARY TABLE pgdump_oid (dummy int4)");
4742         if (!res ||
4743                 PQresultStatus(res) != PGRES_COMMAND_OK)
4744         {
4745                 write_msg(NULL, "could not create pgdump_oid table: %s", PQerrorMessage(g_conn));
4746                 exit_nicely();
4747         }
4748         PQclear(res);
4749         res = PQexec(g_conn, "INSERT INTO pgdump_oid VALUES (0)");
4750         if (!res ||
4751                 PQresultStatus(res) != PGRES_COMMAND_OK)
4752         {
4753                 write_msg(NULL, "could not insert into pgdump_oid table: %s", PQerrorMessage(g_conn));
4754                 exit_nicely();
4755         }
4756         max_oid = PQoidValue(res);
4757         if (max_oid == 0)
4758         {
4759                 write_msg(NULL, "inserted invalid oid\n");
4760                 exit_nicely();
4761         }
4762         PQclear(res);
4763         res = PQexec(g_conn, "DROP TABLE pgdump_oid;");
4764         if (!res ||
4765                 PQresultStatus(res) != PGRES_COMMAND_OK)
4766         {
4767                 write_msg(NULL, "could not drop pgdump_oid table: %s", PQerrorMessage(g_conn));
4768                 exit_nicely();
4769         }
4770         PQclear(res);
4771         if (g_verbose)
4772                 write_msg(NULL, "maximum system oid is %u\n", max_oid);
4773         snprintf(sql, 1024,
4774                          "CREATE TEMPORARY TABLE pgdump_oid (dummy int4);\n"
4775                          "COPY pgdump_oid WITH OIDS FROM stdin;\n"
4776                          "%u\t0\n"
4777                          "\\.\n"
4778                          "DROP TABLE pgdump_oid;\n",
4779                          max_oid);
4780
4781         ArchiveEntry(fout, "0", "Max OID", "<Init>", NULL, sql, "", "", "", NULL, NULL);
4782 }
4783
4784 /*
4785  * findLastBuiltInOid -
4786  * find the last built in oid
4787  * we do this by retrieving datlastsysoid from the pg_database entry for this database,
4788  */
4789
4790 static Oid
4791 findLastBuiltinOid_V71(const char *dbname)
4792 {
4793         PGresult   *res;
4794         int                     ntups;
4795         Oid                     last_oid;
4796         PQExpBuffer query = createPQExpBuffer();
4797
4798         resetPQExpBuffer(query);
4799         appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
4800         formatStringLiteral(query, dbname, CONV_ALL);
4801
4802         res = PQexec(g_conn, query->data);
4803         if (res == NULL ||
4804                 PQresultStatus(res) != PGRES_TUPLES_OK)
4805         {
4806                 write_msg(NULL, "error in finding the last system oid: %s", PQerrorMessage(g_conn));
4807                 exit_nicely();
4808         }
4809         ntups = PQntuples(res);
4810         if (ntups < 1)
4811         {
4812                 write_msg(NULL, "missing pg_database entry for this database\n");
4813                 exit_nicely();
4814         }
4815         if (ntups > 1)
4816         {
4817                 write_msg(NULL, "found more than one pg_database entry for this database\n");
4818                 exit_nicely();
4819         }
4820         last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
4821         PQclear(res);
4822         destroyPQExpBuffer(query);
4823         return last_oid;
4824 }
4825
4826 /*
4827  * findLastBuiltInOid -
4828  * find the last built in oid
4829  * we do this by looking up the oid of 'template1' in pg_database,
4830  * this is probably not foolproof but comes close
4831 */
4832
4833 static Oid
4834 findLastBuiltinOid_V70(void)
4835 {
4836         PGresult   *res;
4837         int                     ntups;
4838         int                     last_oid;
4839
4840         res = PQexec(g_conn,
4841                           "SELECT oid from pg_database where datname = 'template1'");
4842         if (res == NULL ||
4843                 PQresultStatus(res) != PGRES_TUPLES_OK)
4844         {
4845                 write_msg(NULL, "error in finding the template1 database: %s", PQerrorMessage(g_conn));
4846                 exit_nicely();
4847         }
4848         ntups = PQntuples(res);
4849         if (ntups < 1)
4850         {
4851                 write_msg(NULL, "could not find template1 database entry in the pg_database table\n");
4852                 exit_nicely();
4853         }
4854         if (ntups > 1)
4855         {
4856                 write_msg(NULL, "found more than one template1 database entry in the pg_database table\n");
4857                 exit_nicely();
4858         }
4859         last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
4860         PQclear(res);
4861         return last_oid;
4862 }
4863
4864 static void
4865 dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly)
4866 {
4867         PGresult   *res;
4868         char       *last,
4869                            *incby,
4870                            *maxv,
4871                            *minv,
4872                            *cache;
4873         bool            cycled,
4874                                 called;
4875         PQExpBuffer query = createPQExpBuffer();
4876         PQExpBuffer delqry = createPQExpBuffer();
4877
4878         appendPQExpBuffer(query,
4879                         "SELECT sequence_name, last_value, increment_by, max_value, "
4880                                   "min_value, cache_value, is_cycled, is_called from %s",
4881                                           fmtId(tbinfo.relname, force_quotes));
4882
4883         res = PQexec(g_conn, query->data);
4884         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
4885         {
4886                 write_msg(NULL, "query to get data of sequence \"%s\" failed: %s", tbinfo.relname, PQerrorMessage(g_conn));
4887                 exit_nicely();
4888         }
4889
4890         if (PQntuples(res) != 1)
4891         {
4892                 write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
4893                                   tbinfo.relname, PQntuples(res));
4894                 exit_nicely();
4895         }
4896
4897         /* Disable this check: it fails if sequence has been renamed */
4898 #ifdef NOT_USED
4899         if (strcmp(PQgetvalue(res, 0, 0), tbinfo.relname) != 0)
4900         {
4901                 write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
4902                                   tbinfo.relname, PQgetvalue(res, 0, 0));
4903                 exit_nicely();
4904         }
4905 #endif
4906
4907         last = PQgetvalue(res, 0, 1);
4908         incby = PQgetvalue(res, 0, 2);
4909         maxv = PQgetvalue(res, 0, 3);
4910         minv = PQgetvalue(res, 0, 4);
4911         cache = PQgetvalue(res, 0, 5);
4912         cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
4913         called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
4914
4915         /*
4916          * The logic we use for restoring sequences is as follows: -   Add a
4917          * basic CREATE SEQUENCE statement (use last_val for start if called
4918          * is false, else use min_val for start_val).
4919          *
4920          * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
4921          * data
4922          */
4923
4924         if (!dataOnly)
4925         {
4926                 resetPQExpBuffer(delqry);
4927                 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
4928                                                   fmtId(tbinfo.relname, force_quotes));
4929
4930                 resetPQExpBuffer(query);
4931                 appendPQExpBuffer(query,
4932                                                   "CREATE SEQUENCE %s start %s increment %s "
4933                                                   "maxvalue %s minvalue %s cache %s%s;\n",
4934                                                   fmtId(tbinfo.relname, force_quotes),
4935                                                   (called ? minv : last),
4936                                                   incby, maxv, minv, cache,
4937                                                   (cycled ? " cycle" : ""));
4938
4939                 ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE", NULL,
4940                                          query->data, delqry->data, "", tbinfo.usename,
4941                                          NULL, NULL);
4942         }
4943
4944         if (!schemaOnly)
4945         {
4946                 resetPQExpBuffer(query);
4947                 appendPQExpBuffer(query, "SELECT setval (");
4948                 formatStringLiteral(query, fmtId(tbinfo.relname, force_quotes), CONV_ALL);
4949                 appendPQExpBuffer(query, ", %s, %s);\n",
4950                                                   last, (called ? "true" : "false"));
4951
4952                 ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE SET", NULL,
4953                                          query->data, "" /* Del */ , "", tbinfo.usename,
4954                                          NULL, NULL);
4955         }
4956
4957         if (!dataOnly)
4958         {
4959                 /* Dump Sequence Comments */
4960
4961                 resetPQExpBuffer(query);
4962                 appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo.relname, force_quotes));
4963                 dumpComment(fout, query->data, tbinfo.oid,
4964                                         "pg_class", 0, NULL);
4965         }
4966
4967         PQclear(res);
4968
4969         destroyPQExpBuffer(query);
4970         destroyPQExpBuffer(delqry);
4971 }
4972
4973
4974 static void
4975 dumpTriggers(Archive *fout, const char *tablename,
4976                          TableInfo *tblinfo, int numTables)
4977 {
4978         int                     i,
4979                                 j;
4980
4981         if (g_verbose)
4982                 write_msg(NULL, "dumping out triggers\n");
4983
4984         for (i = 0; i < numTables; i++)
4985         {
4986                 if (tablename && (strcmp(tblinfo[i].relname, tablename) != 0) && (strlen(tablename) > 0))
4987                         continue;
4988
4989                 for (j = 0; j < tblinfo[i].ntrig; j++)
4990                 {
4991                         ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname,
4992                                    "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "",
4993                                                  tblinfo[i].usename, NULL, NULL);
4994                         dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid,
4995                                                 "pg_trigger", 0, NULL);
4996                 }
4997         }
4998 }
4999
5000
5001 static void
5002 dumpRules(Archive *fout, const char *tablename,
5003                   TableInfo *tblinfo, int numTables)
5004 {
5005         PGresult   *res;
5006         int                     nrules;
5007         int                     i,
5008                                 t;
5009         PQExpBuffer query = createPQExpBuffer();
5010
5011         int                     i_definition;
5012         int                     i_oid;
5013         int                     i_owner;
5014         int                     i_rulename;
5015
5016         if (g_verbose)
5017                 write_msg(NULL, "dumping out rules\n");
5018
5019         /*
5020          * For each table we dump
5021          */
5022         for (t = 0; t < numTables; t++)
5023         {
5024                 if (tablename && (strcmp(tblinfo[t].relname, tablename) != 0) && (strlen(tablename) > 0))
5025                         continue;
5026
5027                 /*
5028                  * Get all rules defined for this table, except view select rules
5029                  */
5030                 resetPQExpBuffer(query);
5031
5032                 if (g_fout->remoteVersion < 70300)
5033                 {
5034                         /*
5035                          * We include pg_rules in the cross since it filters out all view
5036                          * rules (pjw 15-Sep-2000).
5037                          */
5038                         appendPQExpBuffer(query, "SELECT definition,"
5039                                                           "   (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, "
5040                                                           "   pg_rewrite.oid, pg_rewrite.rulename "
5041                                                           "FROM pg_rewrite, pg_class, pg_rules "
5042                                                           "WHERE pg_class.relname = ");
5043                         formatStringLiteral(query, tblinfo[t].relname, CONV_ALL);
5044                         appendPQExpBuffer(query,
5045                                                           "    AND pg_rewrite.ev_class = pg_class.oid "
5046                                                           "    AND pg_rules.tablename = pg_class.relname "
5047                                                           "    AND pg_rules.rulename = pg_rewrite.rulename "
5048                                                           "ORDER BY pg_rewrite.oid");
5049                 }
5050                 else
5051                 {
5052                         appendPQExpBuffer(query, "SELECT pg_get_ruledef(pg_rewrite.oid) AS definition,"
5053                                                           " (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, "
5054                                                           " pg_rewrite.oid, pg_rewrite.rulename "
5055                                                           "FROM pg_rewrite, pg_class "
5056                                                           "WHERE pg_class.oid = '%s'::oid "
5057                                                           " AND pg_rewrite.ev_class = pg_class.oid "
5058                                                           " AND pg_rewrite.rulename != '_RETURN' "
5059                                                           "ORDER BY pg_rewrite.oid",
5060                                                           tblinfo[t].oid);
5061                 }
5062
5063                 res = PQexec(g_conn, query->data);
5064                 if (!res ||
5065                         PQresultStatus(res) != PGRES_TUPLES_OK)
5066                 {
5067                         write_msg(NULL, "query to get rules associated with table \"%s\" failed: %s",
5068                                           tblinfo[t].relname, PQerrorMessage(g_conn));
5069                         exit_nicely();
5070                 }
5071
5072                 nrules = PQntuples(res);
5073                 i_definition = PQfnumber(res, "definition");
5074                 i_owner = PQfnumber(res, "viewowner");
5075                 i_oid = PQfnumber(res, "oid");
5076                 i_rulename = PQfnumber(res, "rulename");
5077
5078                 /*
5079                  * Dump them out
5080                  */
5081
5082                 for (i = 0; i < nrules; i++)
5083                 {
5084                         ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename),
5085                                                  "RULE", NULL, PQgetvalue(res, i, i_definition),
5086                                                  "", "", PQgetvalue(res, i, i_owner), NULL, NULL);
5087
5088                         /* Dump rule comments */
5089
5090                         resetPQExpBuffer(query);
5091                         appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename), force_quotes));
5092                         appendPQExpBuffer(query, " ON %s", fmtId(tblinfo[t].relname, force_quotes));
5093                         dumpComment(fout, query->data, PQgetvalue(res, i, i_oid),
5094                                                 "pg_rewrite", 0, NULL);
5095
5096                 }
5097
5098                 PQclear(res);
5099         }
5100
5101         destroyPQExpBuffer(query);
5102 }