]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_dump.c
Don't choke when the handler for a procedural language is located in
[postgresql] / 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-2003, 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 dump out a
11  *      script that reproduces the schema in terms of SQL that is understood
12  *      by PostgreSQL
13  *
14  * IDENTIFICATION
15  *        $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.355 2003/10/28 21:05:29 tgl Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19
20 /*
21  * Although this is not a backend module, we must include postgres.h anyway
22  * so that we can include a bunch of backend include files.  pg_dump has
23  * never pretended to be very independent of the backend anyhow ...
24  */
25 #include "postgres.h"
26
27 #include <unistd.h>
28 #include <ctype.h>
29 #ifdef ENABLE_NLS
30 #include <locale.h>
31 #endif
32 #ifdef HAVE_TERMIOS_H
33 #include <termios.h>
34 #endif
35
36 #ifndef HAVE_STRDUP
37 #include "strdup.h"
38 #endif
39
40 #include "getopt_long.h"
41
42 #ifndef HAVE_OPTRESET
43 int                     optreset;
44 #endif
45
46 #include "access/attnum.h"
47 #include "access/htup.h"
48 #include "catalog/pg_class.h"
49 #include "catalog/pg_proc.h"
50 #include "catalog/pg_trigger.h"
51 #include "catalog/pg_type.h"
52
53 #include "commands/sequence.h"
54
55 #include "libpq-fe.h"
56 #include "libpq/libpq-fs.h"
57
58 #include "pg_dump.h"
59 #include "pg_backup.h"
60 #include "pg_backup_archiver.h"
61 #include "dumputils.h"
62
63 #define _(x) gettext((x))
64
65 typedef struct _dumpContext
66 {
67         TableInfo  *tblinfo;
68         int                     tblidx;
69         bool            oids;
70 } DumpContext;
71
72 static void help(const char *progname);
73 static NamespaceInfo *findNamespace(const char *nsoid, const char *objoid);
74 static void dumpClasses(const TableInfo *tblinfo, const int numTables,
75                         Archive *fout, const bool oids);
76 static void dumpComment(Archive *fout, const char *target,
77                         const char *namespace, const char *owner,
78                         const char *oid, const char *classname, int subid,
79                         const char *((*deps)[]));
80 static void dumpOneBaseType(Archive *fout, TypeInfo *tinfo,
81                                 FuncInfo *g_finfo, int numFuncs,
82                                 TypeInfo *g_tinfo, int numTypes);
83 static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
84 static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
85 static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
86                          TableInfo *g_tblinfo);
87 static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
88                                 const bool schemaOnly, const bool dataOnly);
89
90 static void dumpTableACL(Archive *fout, TableInfo *tbinfo);
91 static void dumpFuncACL(Archive *fout, FuncInfo *finfo);
92 static void dumpAggACL(Archive *fout, AggInfo *finfo);
93 static void dumpACL(Archive *fout, const char *type, const char *name,
94                 const char *tag, const char *nspname,
95                 const char *owner, const char *acl, const char *objoid);
96
97 static void dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables);
98 static void dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables);
99 static void dumpRules(Archive *fout, TableInfo *tblinfo, int numTables);
100 static char *format_function_signature(FuncInfo *finfo, bool honor_quotes);
101 static void dumpOneFunc(Archive *fout, FuncInfo *finfo);
102 static void dumpOneOpr(Archive *fout, OprInfo *oprinfo,
103                    OprInfo *g_oprinfo, int numOperators);
104 static const char *convertRegProcReference(const char *proc);
105 static const char *convertOperatorReference(const char *opr,
106                                                  OprInfo *g_oprinfo, int numOperators);
107 static void dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo);
108 static void dumpOneAgg(Archive *fout, AggInfo *agginfo);
109 static Oid      findLastBuiltinOid_V71(const char *);
110 static Oid      findLastBuiltinOid_V70(void);
111 static void setMaxOid(Archive *fout);
112 static void selectSourceSchema(const char *schemaName);
113 static char *getFormattedTypeName(const char *oid, OidOptions opts);
114 static char *myFormatType(const char *typname, int32 typmod);
115 static const char *fmtQualifiedId(const char *schema, const char *id);
116 static int      dumpBlobs(Archive *AH, char *, void *);
117 static int      dumpDatabase(Archive *AH);
118 static const char *getAttrName(int attrnum, TableInfo *tblInfo);
119 static const char *fmtCopyColumnList(const TableInfo *ti);
120
121 extern char *optarg;
122 extern int      optind,
123                         opterr;
124
125 /* global decls */
126 bool            g_verbose;                      /* User wants verbose narration of our
127                                                                  * activities. */
128 Archive    *g_fout;                             /* the script file */
129 PGconn     *g_conn;                             /* the database connection */
130
131 /* various user-settable parameters */
132 bool            dumpData;                       /* dump data using proper insert strings */
133 bool            attrNames;                      /* put attr names into insert strings */
134 bool            schemaOnly;
135 bool            dataOnly;
136 bool            aclsSkip;
137
138 /* obsolete as of 7.3: */
139 static Oid      g_last_builtin_oid; /* value of the last builtin oid */
140
141 static char *selectTableName = NULL;    /* name of a single table to dump */
142 static char *selectSchemaName = NULL;   /* name of a single schema to dump */
143
144 char            g_opaque_type[10];      /* name for the opaque type */
145
146 /* placeholders for the delimiters for comments */
147 char            g_comment_start[10];
148 char            g_comment_end[10];
149
150 /* these are to avoid passing around info for findNamespace() */
151 static NamespaceInfo *g_namespaces;
152 static int      g_numNamespaces;
153
154
155 int
156 main(int argc, char **argv)
157 {
158         int                     c;
159         const char *filename = NULL;
160         const char *format = "p";
161         const char *dbname = NULL;
162         const char *pghost = NULL;
163         const char *pgport = NULL;
164         const char *username = NULL;
165         bool            oids = false;
166         PGresult   *res;
167         TableInfo  *tblinfo;
168         int                     numTables;
169         bool            force_password = false;
170         int                     compressLevel = -1;
171         bool            ignore_version = false;
172         int                     plainText = 0;
173         int                     outputClean = 0;
174         int                     outputCreate = 0;
175         int                     outputBlobs = 0;
176         int                     outputNoOwner = 0;
177         static int      use_setsessauth = 0;
178         static int      disable_triggers = 0;
179         char       *outputSuperuser = NULL;
180
181         RestoreOptions *ropt;
182
183         static struct option long_options[] = {
184                 {"data-only", no_argument, NULL, 'a'},
185                 {"blobs", no_argument, NULL, 'b'},
186                 {"clean", no_argument, NULL, 'c'},
187                 {"create", no_argument, NULL, 'C'},
188                 {"file", required_argument, NULL, 'f'},
189                 {"format", required_argument, NULL, 'F'},
190                 {"inserts", no_argument, NULL, 'd'},
191                 {"attribute-inserts", no_argument, NULL, 'D'},
192                 {"column-inserts", no_argument, NULL, 'D'},
193                 {"host", required_argument, NULL, 'h'},
194                 {"ignore-version", no_argument, NULL, 'i'},
195                 {"no-reconnect", no_argument, NULL, 'R'},
196                 {"oids", no_argument, NULL, 'o'},
197                 {"no-owner", no_argument, NULL, 'O'},
198                 {"port", required_argument, NULL, 'p'},
199                 {"schema", required_argument, NULL, 'n'},
200                 {"schema-only", no_argument, NULL, 's'},
201                 {"superuser", required_argument, NULL, 'S'},
202                 {"table", required_argument, NULL, 't'},
203                 {"password", no_argument, NULL, 'W'},
204                 {"username", required_argument, NULL, 'U'},
205                 {"verbose", no_argument, NULL, 'v'},
206                 {"no-privileges", no_argument, NULL, 'x'},
207                 {"no-acl", no_argument, NULL, 'x'},
208                 {"compress", required_argument, NULL, 'Z'},
209                 {"help", no_argument, NULL, '?'},
210                 {"version", no_argument, NULL, 'V'},
211
212                 /*
213                  * the following options don't have an equivalent short option
214                  * letter, but are available as '-X long-name'
215                  */
216                 {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
217                 {"disable-triggers", no_argument, &disable_triggers, 1},
218
219                 {NULL, 0, NULL, 0}
220         };
221         int                     optindex;
222
223 #ifdef ENABLE_NLS
224         setlocale(LC_ALL, "");
225         bindtextdomain("pg_dump", LOCALEDIR);
226         textdomain("pg_dump");
227 #endif
228
229         g_verbose = false;
230
231         strcpy(g_comment_start, "-- ");
232         g_comment_end[0] = '\0';
233         strcpy(g_opaque_type, "opaque");
234
235         dataOnly = schemaOnly = dumpData = attrNames = false;
236
237         progname = get_progname(argv[0]);
238
239         /* Set default options based on progname */
240         if (strcmp(progname, "pg_backup") == 0)
241         {
242                 format = "c";
243                 outputBlobs = true;
244         }
245
246         if (argc > 1)
247         {
248                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
249                 {
250                         help(progname);
251                         exit(0);
252                 }
253                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
254                 {
255                         puts("pg_dump (PostgreSQL) " PG_VERSION);
256                         exit(0);
257                 }
258         }
259
260         while ((c = getopt_long(argc, argv, "abcCdDf:F:h:in:oOp:RsS:t:uU:vWxX:Z:",
261                                                         long_options, &optindex)) != -1)
262         {
263                 switch (c)
264                 {
265                         case 'a':                       /* Dump data only */
266                                 dataOnly = true;
267                                 break;
268
269                         case 'b':                       /* Dump blobs */
270                                 outputBlobs = true;
271                                 break;
272
273                         case 'c':                       /* clean (i.e., drop) schema prior to
274                                                                  * create */
275                                 outputClean = 1;
276                                 break;
277
278                         case 'C':                       /* Create DB */
279                                 outputCreate = 1;
280                                 break;
281
282                         case 'd':                       /* dump data as proper insert strings */
283                                 dumpData = true;
284                                 break;
285
286                         case 'D':                       /* dump data as proper insert strings with
287                                                                  * attr names */
288                                 dumpData = true;
289                                 attrNames = true;
290                                 break;
291
292                         case 'f':
293                                 filename = optarg;
294                                 break;
295
296                         case 'F':
297                                 format = optarg;
298                                 break;
299
300                         case 'h':                       /* server host */
301                                 pghost = optarg;
302                                 break;
303
304                         case 'i':                       /* ignore database version mismatch */
305                                 ignore_version = true;
306                                 break;
307
308                         case 'n':                       /* Dump data for this schema only */
309                                 selectSchemaName = strdup(optarg);
310                                 break;
311
312                         case 'o':                       /* Dump oids */
313                                 oids = true;
314                                 break;
315
316                         case 'O':                       /* Don't reconnect to match owner */
317                                 outputNoOwner = 1;
318                                 break;
319
320                         case 'p':                       /* server port */
321                                 pgport = optarg;
322                                 break;
323
324                         case 'R':
325                                 /* no-op, still accepted for backwards compatibility */
326                                 break;
327
328                         case 's':                       /* dump schema only */
329                                 schemaOnly = true;
330                                 break;
331
332                         case 'S':                       /* Username for superuser in plain text
333                                                                  * output */
334                                 outputSuperuser = strdup(optarg);
335                                 break;
336
337                         case 't':                       /* Dump data for this table only */
338                                 selectTableName = strdup(optarg);
339                                 break;
340
341                         case 'u':
342                                 force_password = true;
343                                 username = simple_prompt("User name: ", 100, true);
344                                 break;
345
346                         case 'U':
347                                 username = optarg;
348                                 break;
349
350                         case 'v':                       /* verbose */
351                                 g_verbose = true;
352                                 break;
353
354                         case 'W':
355                                 force_password = true;
356                                 break;
357
358                         case 'x':                       /* skip ACL dump */
359                                 aclsSkip = true;
360                                 break;
361
362                                 /*
363                                  * Option letters were getting scarce, so I invented this
364                                  * new scheme: '-X feature' turns on some feature. Compare
365                                  * to the -f option in GCC.  You should also add an
366                                  * equivalent GNU-style option --feature.  Features that
367                                  * require arguments should use '-X feature=foo'.
368                                  */
369                         case 'X':
370                                 if (strcmp(optarg, "use-set-session-authorization") == 0)
371                                         /* no-op, still allowed for compatibility */ ;
372                                 else if (strcmp(optarg, "disable-triggers") == 0)
373                                         disable_triggers = 1;
374                                 else
375                                 {
376                                         fprintf(stderr,
377                                                         _("%s: invalid -X option -- %s\n"),
378                                                         progname, optarg);
379                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
380                                         exit(1);
381                                 }
382                                 break;
383
384                         case 'Z':                       /* Compression Level */
385                                 compressLevel = atoi(optarg);
386                                 break;
387                                 /* This covers the long options equivalent to -X xxx. */
388
389                         case 0:
390                                 break;
391
392                         default:
393                                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
394                                 exit(1);
395                 }
396         }
397
398         if (optind < (argc - 1))
399         {
400                 fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
401                                 progname, argv[optind + 1]);
402                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
403                                 progname);
404                 exit(1);
405         }
406
407         /* Get database name from command line */
408         if (optind < argc)
409                 dbname = argv[optind];
410
411         if (dataOnly && schemaOnly)
412         {
413                 write_msg(NULL, "options \"schema only\" (-s) and \"data only\" (-a) cannot be used together\n");
414                 exit(1);
415         }
416
417         if (dataOnly && outputClean)
418         {
419                 write_msg(NULL, "options \"clean\" (-c) and \"data only\" (-a) cannot be used together\n");
420                 exit(1);
421         }
422
423         if (outputBlobs && selectTableName != NULL)
424         {
425                 write_msg(NULL, "large-object output not supported for a single table\n");
426                 write_msg(NULL, "use a full dump instead\n");
427                 exit(1);
428         }
429
430         if (outputBlobs && selectSchemaName != NULL)
431         {
432                 write_msg(NULL, "large-object output not supported for a single schema\n");
433                 write_msg(NULL, "use a full dump instead\n");
434                 exit(1);
435         }
436
437         if (dumpData == true && oids == true)
438         {
439                 write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together\n");
440                 write_msg(NULL, "(The INSERT command cannot set OIDs.)\n");
441                 exit(1);
442         }
443
444         if (outputBlobs == true && (format[0] == 'p' || format[0] == 'P'))
445         {
446                 write_msg(NULL, "large-object output is not supported for plain-text dump files\n");
447                 write_msg(NULL, "(Use a different output format.)\n");
448                 exit(1);
449         }
450
451         /* open the output file */
452         switch (format[0])
453         {
454                 case 'c':
455                 case 'C':
456                         g_fout = CreateArchive(filename, archCustom, compressLevel);
457                         break;
458
459                 case 'f':
460                 case 'F':
461                         g_fout = CreateArchive(filename, archFiles, compressLevel);
462                         break;
463
464                 case 'p':
465                 case 'P':
466                         plainText = 1;
467                         g_fout = CreateArchive(filename, archNull, 0);
468                         break;
469
470                 case 't':
471                 case 'T':
472                         g_fout = CreateArchive(filename, archTar, compressLevel);
473                         break;
474
475                 default:
476                         write_msg(NULL, "invalid output format \"%s\" specified\n", format);
477                         exit(1);
478         }
479
480         if (g_fout == NULL)
481         {
482                 write_msg(NULL, "could not open output file \"%s\" for writing\n", filename);
483                 exit(1);
484         }
485
486         /* Let the archiver know how noisy to be */
487         g_fout->verbose = g_verbose;
488
489         g_fout->minRemoteVersion = 70000;       /* we can handle back to 7.0 */
490         g_fout->maxRemoteVersion = parse_version(PG_VERSION);
491         if (g_fout->maxRemoteVersion < 0)
492         {
493                 write_msg(NULL, "could not parse version string \"%s\"\n", PG_VERSION);
494                 exit(1);
495         }
496
497         /*
498          * Open the database using the Archiver, so it knows about it. Errors
499          * mean death.
500          */
501         g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport,
502                                                          username, force_password, ignore_version);
503
504         /*
505          * Start serializable transaction to dump consistent data.
506          */
507         res = PQexec(g_conn, "BEGIN");
508         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
509                 exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
510                                           PQerrorMessage(g_conn));
511         PQclear(res);
512
513         res = PQexec(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
514         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
515                 exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
516                                           PQerrorMessage(g_conn));
517         PQclear(res);
518
519         /* Set the datestyle to ISO to ensure the dump's portability */
520         res = PQexec(g_conn, "SET DATESTYLE = ISO");
521         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
522                 exit_horribly(g_fout, NULL, "could not set datestyle to ISO: %s",
523                                           PQerrorMessage(g_conn));
524         PQclear(res);
525
526         /*
527          * If supported, set extra_float_digits so that we can dump float data
528          * exactly (given correctly implemented float I/O code, anyway)
529          */
530         if (g_fout->remoteVersion >= 70400)
531         {
532                 res = PQexec(g_conn, "SET extra_float_digits TO 2");
533                 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
534                         exit_horribly(g_fout, NULL, "could not set extra_float_digits: %s",
535                                                   PQerrorMessage(g_conn));
536                 PQclear(res);
537         }
538
539         /* Find the last built-in OID, if needed */
540         if (g_fout->remoteVersion < 70300)
541         {
542                 if (g_fout->remoteVersion >= 70100)
543                         g_last_builtin_oid = findLastBuiltinOid_V71(PQdb(g_conn));
544                 else
545                         g_last_builtin_oid = findLastBuiltinOid_V70();
546                 if (g_verbose)
547                         write_msg(NULL, "last built-in OID is %u\n", g_last_builtin_oid);
548         }
549
550         /* Dump the database definition */
551         if (!dataOnly)
552                 dumpDatabase(g_fout);
553
554         if (oids == true)
555                 setMaxOid(g_fout);
556
557         tblinfo = dumpSchema(g_fout, &numTables, aclsSkip, schemaOnly, dataOnly);
558
559         if (!schemaOnly)
560                 dumpClasses(tblinfo, numTables, g_fout, oids);
561
562         if (outputBlobs)
563                 ArchiveEntry(g_fout, "0", "BLOBS", NULL, "",
564                                          "BLOBS", NULL, "", "", NULL, dumpBlobs, NULL);
565
566         if (!dataOnly)                          /* dump indexes and triggers at the end
567                                                                  * for performance */
568         {
569                 dumpConstraints(g_fout, tblinfo, numTables);
570                 dumpTriggers(g_fout, tblinfo, numTables);
571                 dumpRules(g_fout, tblinfo, numTables);
572         }
573
574         /* Now sort the output nicely: by OID within object types */
575         SortTocByOID(g_fout);
576         SortTocByObjectType(g_fout);
577
578         if (plainText)
579         {
580                 ropt = NewRestoreOptions();
581                 ropt->filename = (char *) filename;
582                 ropt->dropSchema = outputClean;
583                 ropt->aclsSkip = aclsSkip;
584                 ropt->superuser = outputSuperuser;
585                 ropt->create = outputCreate;
586                 ropt->noOwner = outputNoOwner;
587                 ropt->disable_triggers = disable_triggers;
588
589                 if (compressLevel == -1)
590                         ropt->compression = 0;
591                 else
592                         ropt->compression = compressLevel;
593
594                 ropt->suppressDumpWarnings = true;              /* We've already shown
595                                                                                                  * them */
596
597                 RestoreArchive(g_fout, ropt);
598         }
599
600         CloseArchive(g_fout);
601
602         PQfinish(g_conn);
603         exit(0);
604 }
605
606
607 static void
608 help(const char *progname)
609 {
610         printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
611         printf(_("Usage:\n"));
612         printf(_("  %s [OPTION]... [DBNAME]\n"), progname);
613
614         printf(_("\nGeneral options:\n"));
615         printf(_("  -f, --file=FILENAME      output file name\n"));
616         printf(_("  -F, --format=c|t|p       output file format (custom, tar, plain text)\n"));
617         printf(_("  -i, --ignore-version     proceed even when server version mismatches\n"
618                          "                           pg_dump version\n"));
619         printf(_("  -v, --verbose            verbose mode\n"));
620         printf(_("  -Z, --compress=0-9       compression level for compressed formats\n"));
621         printf(_("  --help                   show this help, then exit\n"));
622         printf(_("  --version                output version information, then exit\n"));
623
624         printf(_("\nOptions controlling the output content:\n"));
625         printf(_("  -a, --data-only          dump only the data, not the schema\n"));
626         printf(_("  -b, --blobs              include large objects in dump\n"));
627         printf(_("  -c, --clean              clean (drop) schema prior to create\n"));
628         printf(_("  -C, --create             include commands to create database in dump\n"));
629         printf(_("  -d, --inserts            dump data as INSERT, rather than COPY, commands\n"));
630         printf(_("  -D, --column-inserts     dump data as INSERT commands with column names\n"));
631         printf(_("  -n, --schema=SCHEMA      dump the named schema only\n"));
632         printf(_("  -o, --oids               include OIDs in dump\n"));
633         printf(_("  -O, --no-owner           do not output commands to set object ownership\n"
634                          "                           in plain text format\n"));
635         printf(_("  -s, --schema-only        dump only the schema, no data\n"));
636         printf(_("  -S, --superuser=NAME     specify the superuser user name to use in\n"
637                          "                           plain text format\n"));
638         printf(_("  -t, --table=TABLE        dump the named table only\n"));
639         printf(_("  -x, --no-privileges      do not dump privileges (grant/revoke)\n"));
640         printf(_("  -X disable-triggers, --disable-triggers\n"
641                          "                           disable triggers during data-only restore\n"));
642
643         printf(_("\nConnection options:\n"));
644         printf(_("  -h, --host=HOSTNAME      database server host or socket directory\n"));
645         printf(_("  -p, --port=PORT          database server port number\n"));
646         printf(_("  -U, --username=NAME      connect as specified database user\n"));
647         printf(_("  -W, --password           force password prompt (should happen automatically)\n"));
648
649         printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
650                          "variable value is used.\n\n"));
651         printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
652 }
653
654 void
655 exit_nicely(void)
656 {
657         PQfinish(g_conn);
658         if (g_verbose)
659                 write_msg(NULL, "*** aborted because of error\n");
660         exit(1);
661 }
662
663 /*
664  * selectDumpableNamespace: policy-setting subroutine
665  *              Mark a namespace as to be dumped or not
666  */
667 static void
668 selectDumpableNamespace(NamespaceInfo *nsinfo)
669 {
670         /*
671          * If a specific table is being dumped, do not dump any complete
672          * namespaces.  If a specific namespace is being dumped, dump just
673          * that namespace. Otherwise, dump all non-system namespaces.
674          */
675         if (selectTableName != NULL)
676                 nsinfo->dump = false;
677         else if (selectSchemaName != NULL)
678         {
679                 if (strcmp(nsinfo->nspname, selectSchemaName) == 0)
680                         nsinfo->dump = true;
681                 else
682                         nsinfo->dump = false;
683         }
684         else if (strncmp(nsinfo->nspname, "pg_", 3) == 0 ||
685                          strcmp(nsinfo->nspname, "information_schema") == 0)
686                 nsinfo->dump = false;
687         else
688                 nsinfo->dump = true;
689 }
690
691 /*
692  * selectDumpableTable: policy-setting subroutine
693  *              Mark a table as to be dumped or not
694  */
695 static void
696 selectDumpableTable(TableInfo *tbinfo)
697 {
698         /*
699          * Always dump if dumping parent namespace; else, if a particular
700          * tablename has been specified, dump matching table name; else, do
701          * not dump.
702          */
703         tbinfo->dump = false;
704         if (tbinfo->relnamespace->dump)
705                 tbinfo->dump = true;
706         else if (selectTableName != NULL &&
707                          strcmp(tbinfo->relname, selectTableName) == 0)
708         {
709                 /* If both -s and -t specified, must match both to dump */
710                 if (selectSchemaName == NULL)
711                         tbinfo->dump = true;
712                 else if (strcmp(tbinfo->relnamespace->nspname, selectSchemaName) == 0)
713                         tbinfo->dump = true;
714         }
715 }
716
717 /*
718  *      Dump a table's contents for loading using the COPY command
719  *      - this routine is called by the Archiver when it wants the table
720  *        to be dumped.
721  */
722
723 #define COPYBUFSIZ              8192
724
725 static int
726 dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
727 {
728         const DumpContext *dctx = (DumpContext *) dctxv;
729         TableInfo  *tbinfo = &dctx->tblinfo[dctx->tblidx];
730         const char *classname = tbinfo->relname;
731         const bool      hasoids = tbinfo->hasoids;
732         const bool      oids = dctx->oids;
733         PQExpBuffer q = createPQExpBuffer();
734         PGresult   *res;
735         int                     ret;
736         bool            copydone;
737         char            copybuf[COPYBUFSIZ];
738         const char *column_list;
739
740         if (g_verbose)
741                 write_msg(NULL, "dumping contents of table %s\n", classname);
742
743         /*
744          * Make sure we are in proper schema.  We will qualify the table name
745          * below anyway (in case its name conflicts with a pg_catalog table);
746          * but this ensures reproducible results in case the table contains
747          * regproc, regclass, etc columns.
748          */
749         selectSourceSchema(tbinfo->relnamespace->nspname);
750
751         /*
752          * If possible, specify the column list explicitly so that we have no
753          * possibility of retrieving data in the wrong column order.  (The
754          * default column ordering of COPY will not be what we want in certain
755          * corner cases involving ADD COLUMN and inheritance.)
756          */
757         if (g_fout->remoteVersion >= 70300)
758                 column_list = fmtCopyColumnList(tbinfo);
759         else
760                 column_list = "";               /* can't select columns in COPY */
761
762         if (oids && hasoids)
763         {
764                 appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
765                                                   fmtQualifiedId(tbinfo->relnamespace->nspname,
766                                                                                  classname),
767                                                   column_list);
768         }
769         else
770         {
771                 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
772                                                   fmtQualifiedId(tbinfo->relnamespace->nspname,
773                                                                                  classname),
774                                                   column_list);
775         }
776         res = PQexec(g_conn, q->data);
777         if (!res ||
778                 PQresultStatus(res) == PGRES_FATAL_ERROR)
779         {
780                 write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed\n",
781                                   classname);
782                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
783                 write_msg(NULL, "The command was: %s\n", q->data);
784                 exit_nicely();
785         }
786         if (PQresultStatus(res) != PGRES_COPY_OUT)
787         {
788                 write_msg(NULL, "SQL command to dump the contents of table \"%s\" executed abnormally.\n",
789                                   classname);
790                 write_msg(NULL, "The server returned status %d when %d was expected.\n",
791                                   PQresultStatus(res), PGRES_COPY_OUT);
792                 write_msg(NULL, "The command was: %s\n", q->data);
793                 exit_nicely();
794         }
795
796         copydone = false;
797
798         while (!copydone)
799         {
800                 ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
801
802                 if (copybuf[0] == '\\' &&
803                         copybuf[1] == '.' &&
804                         copybuf[2] == '\0')
805                 {
806                         copydone = true;        /* don't print this... */
807                 }
808                 else
809                 {
810                         archputs(copybuf, fout);
811                         switch (ret)
812                         {
813                                 case EOF:
814                                         copydone = true;
815                                         /* FALLTHROUGH */
816                                 case 0:
817                                         archputc('\n', fout);
818                                         break;
819                                 case 1:
820                                         break;
821                         }
822                 }
823
824                 /*
825                  * THROTTLE:
826                  *
827                  * There was considerable discussion in late July, 2000 regarding
828                  * slowing down pg_dump when backing up large tables. Users with
829                  * both slow & fast (muti-processor) machines experienced
830                  * performance degradation when doing a backup.
831                  *
832                  * Initial attempts based on sleeping for a number of ms for each ms
833                  * of work were deemed too complex, then a simple 'sleep in each
834                  * loop' implementation was suggested. The latter failed because
835                  * the loop was too tight. Finally, the following was implemented:
836                  *
837                  * If throttle is non-zero, then See how long since the last sleep.
838                  * Work out how long to sleep (based on ratio). If sleep is more
839                  * than 100ms, then sleep reset timer EndIf EndIf
840                  *
841                  * where the throttle value was the number of ms to sleep per ms of
842                  * work. The calculation was done in each loop.
843                  *
844                  * Most of the hard work is done in the backend, and this solution
845                  * still did not work particularly well: on slow machines, the
846                  * ratio was 50:1, and on medium paced machines, 1:1, and on fast
847                  * multi-processor machines, it had little or no effect, for
848                  * reasons that were unclear.
849                  *
850                  * Further discussion ensued, and the proposal was dropped.
851                  *
852                  * For those people who want this feature, it can be implemented
853                  * using gettimeofday in each loop, calculating the time since
854                  * last sleep, multiplying that by the sleep ratio, then if the
855                  * result is more than a preset 'minimum sleep time' (say 100ms),
856                  * call the 'select' function to sleep for a subsecond period ie.
857                  *
858                  * select(0, NULL, NULL, NULL, &tvi);
859                  *
860                  * This will return after the interval specified in the structure
861                  * tvi. Finally, call gettimeofday again to save the 'last sleep
862                  * time'.
863                  */
864         }
865         archprintf(fout, "\\.\n\n\n");
866
867         ret = PQendcopy(g_conn);
868         if (ret != 0)
869         {
870                 write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname);
871                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
872                 write_msg(NULL, "The command was: %s\n", q->data);
873                 exit_nicely();
874         }
875
876         PQclear(res);
877         destroyPQExpBuffer(q);
878         return 1;
879 }
880
881 static int
882 dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
883 {
884         const DumpContext *dctx = (DumpContext *) dctxv;
885         TableInfo  *tbinfo = &dctx->tblinfo[dctx->tblidx];
886         const char *classname = tbinfo->relname;
887         PQExpBuffer q = createPQExpBuffer();
888         PGresult   *res;
889         int                     tuple;
890         int                     nfields;
891         int                     field;
892
893         /*
894          * Make sure we are in proper schema.  We will qualify the table name
895          * below anyway (in case its name conflicts with a pg_catalog table);
896          * but this ensures reproducible results in case the table contains
897          * regproc, regclass, etc columns.
898          */
899         selectSourceSchema(tbinfo->relnamespace->nspname);
900
901         if (fout->remoteVersion >= 70100)
902         {
903                 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
904                                                   "SELECT * FROM ONLY %s",
905                                                   fmtQualifiedId(tbinfo->relnamespace->nspname,
906                                                                                  classname));
907         }
908         else
909         {
910                 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
911                                                   "SELECT * FROM %s",
912                                                   fmtQualifiedId(tbinfo->relnamespace->nspname,
913                                                                                  classname));
914         }
915
916         res = PQexec(g_conn, q->data);
917         if (!res ||
918                 PQresultStatus(res) != PGRES_COMMAND_OK)
919         {
920                 write_msg(NULL, "dumpClasses(): SQL command failed\n");
921                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
922                 write_msg(NULL, "The command was: %s\n", q->data);
923                 exit_nicely();
924         }
925
926         do
927         {
928                 PQclear(res);
929
930                 res = PQexec(g_conn, "FETCH 100 FROM _pg_dump_cursor");
931                 if (!res ||
932                         PQresultStatus(res) != PGRES_TUPLES_OK)
933                 {
934                         write_msg(NULL, "dumpClasses(): SQL command failed\n");
935                         write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
936                         write_msg(NULL, "The command was: FETCH 100 FROM _pg_dump_cursor\n");
937                         exit_nicely();
938                 }
939
940                 nfields = PQnfields(res);
941                 for (tuple = 0; tuple < PQntuples(res); tuple++)
942                 {
943                         archprintf(fout, "INSERT INTO %s ", fmtId(classname));
944                         if (nfields == 0)
945                         {
946                                 /* corner case for zero-column table */
947                                 archprintf(fout, "DEFAULT VALUES;\n");
948                                 continue;
949                         }
950                         if (attrNames == true)
951                         {
952                                 resetPQExpBuffer(q);
953                                 appendPQExpBuffer(q, "(");
954                                 for (field = 0; field < nfields; field++)
955                                 {
956                                         if (field > 0)
957                                                 appendPQExpBuffer(q, ", ");
958                                         appendPQExpBuffer(q, fmtId(PQfname(res, field)));
959                                 }
960                                 appendPQExpBuffer(q, ") ");
961                                 archprintf(fout, "%s", q->data);
962                         }
963                         archprintf(fout, "VALUES (");
964                         for (field = 0; field < nfields; field++)
965                         {
966                                 if (field > 0)
967                                         archprintf(fout, ", ");
968                                 if (PQgetisnull(res, tuple, field))
969                                 {
970                                         archprintf(fout, "NULL");
971                                         continue;
972                                 }
973
974                                 /* XXX This code is partially duplicated in ruleutils.c */
975                                 switch (PQftype(res, field))
976                                 {
977                                         case INT2OID:
978                                         case INT4OID:
979                                         case INT8OID:
980                                         case OIDOID:
981                                         case FLOAT4OID:
982                                         case FLOAT8OID:
983                                         case NUMERICOID:
984                                                 {
985                                                         /*
986                                                          * These types are printed without quotes
987                                                          * unless they contain values that aren't
988                                                          * accepted by the scanner unquoted (e.g.,
989                                                          * 'NaN').      Note that strtod() and friends
990                                                          * might accept NaN, so we can't use that to
991                                                          * test.
992                                                          *
993                                                          * In reality we only need to defend against
994                                                          * infinity and NaN, so we need not get too
995                                                          * crazy about pattern matching here.
996                                                          */
997                                                         const char *s = PQgetvalue(res, tuple, field);
998
999                                                         if (strspn(s, "0123456789 +-eE.") == strlen(s))
1000                                                                 archprintf(fout, "%s", s);
1001                                                         else
1002                                                                 archprintf(fout, "'%s'", s);
1003                                                 }
1004                                                 break;
1005
1006                                         case BITOID:
1007                                         case VARBITOID:
1008                                                 archprintf(fout, "B'%s'",
1009                                                                    PQgetvalue(res, tuple, field));
1010                                                 break;
1011
1012                                         case BOOLOID:
1013                                                 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
1014                                                         archprintf(fout, "true");
1015                                                 else
1016                                                         archprintf(fout, "false");
1017                                                 break;
1018
1019                                         default:
1020                                                 /* All other types are printed as string literals. */
1021                                                 resetPQExpBuffer(q);
1022                                                 appendStringLiteral(q, PQgetvalue(res, tuple, field), false);
1023                                                 archprintf(fout, "%s", q->data);
1024                                                 break;
1025                                 }
1026                         }
1027                         archprintf(fout, ");\n");
1028                 }
1029         } while (PQntuples(res) > 0);
1030
1031         archprintf(fout, "\n\n");
1032         PQclear(res);
1033
1034         res = PQexec(g_conn, "CLOSE _pg_dump_cursor");
1035         if (!res ||
1036                 PQresultStatus(res) != PGRES_COMMAND_OK)
1037         {
1038                 write_msg(NULL, "dumpClasses(): SQL command failed\n");
1039                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
1040                 write_msg(NULL, "The command was: CLOSE _pg_dump_cursor\n");
1041                 exit_nicely();
1042         }
1043         PQclear(res);
1044
1045         destroyPQExpBuffer(q);
1046         return 1;
1047 }
1048
1049
1050 /*
1051  * DumpClasses -
1052  *        dump the contents of all the classes.
1053  */
1054 static void
1055 dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
1056                         const bool oids)
1057 {
1058         PQExpBuffer copyBuf = createPQExpBuffer();
1059         DataDumperPtr dumpFn;
1060         DumpContext *dumpCtx;
1061         char       *copyStmt;
1062         int                     i;
1063
1064         for (i = 0; i < numTables; i++)
1065         {
1066                 const char *classname = tblinfo[i].relname;
1067
1068                 /* Skip VIEW relations */
1069                 if (tblinfo[i].relkind == RELKIND_VIEW)
1070                         continue;
1071
1072                 if (tblinfo[i].relkind == RELKIND_SEQUENCE)             /* already dumped */
1073                         continue;
1074
1075                 if (tblinfo[i].dump)
1076                 {
1077                         if (g_verbose)
1078                                 write_msg(NULL, "preparing to dump the contents of table %s\n",
1079                                                   classname);
1080
1081                         dumpCtx = (DumpContext *) calloc(1, sizeof(DumpContext));
1082                         dumpCtx->tblinfo = (TableInfo *) tblinfo;
1083                         dumpCtx->tblidx = i;
1084                         dumpCtx->oids = oids;
1085
1086                         if (!dumpData)
1087                         {
1088                                 /* Dump/restore using COPY */
1089                                 dumpFn = dumpClasses_nodumpData;
1090                                 resetPQExpBuffer(copyBuf);
1091                                 /* must use 2 steps here 'cause fmtId is nonreentrant */
1092                                 appendPQExpBuffer(copyBuf, "COPY %s ",
1093                                                                   fmtId(tblinfo[i].relname));
1094                                 appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
1095                                                                   fmtCopyColumnList(&(tblinfo[i])),
1096                                            (oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
1097                                 copyStmt = copyBuf->data;
1098                         }
1099                         else
1100                         {
1101                                 /* Restore using INSERT */
1102                                 dumpFn = dumpClasses_dumpData;
1103                                 copyStmt = NULL;
1104                         }
1105
1106                         ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
1107                                                  tblinfo[i].relnamespace->nspname,
1108                                                  tblinfo[i].usename,
1109                                                  "TABLE DATA", NULL, "", "", copyStmt,
1110                                                  dumpFn, dumpCtx);
1111                 }
1112         }
1113
1114         destroyPQExpBuffer(copyBuf);
1115 }
1116
1117
1118 /*
1119  * dumpDatabase:
1120  *      dump the database definition
1121  */
1122 static int
1123 dumpDatabase(Archive *AH)
1124 {
1125         PQExpBuffer dbQry = createPQExpBuffer();
1126         PQExpBuffer delQry = createPQExpBuffer();
1127         PQExpBuffer creaQry = createPQExpBuffer();
1128         PGresult   *res;
1129         int                     ntups;
1130         int                     i_dba,
1131                                 i_encoding,
1132                                 i_datpath;
1133         const char *datname,
1134                            *dba,
1135                            *encoding,
1136                            *datpath;
1137
1138         datname = PQdb(g_conn);
1139
1140         if (g_verbose)
1141                 write_msg(NULL, "saving database definition\n");
1142
1143         /* Make sure we are in proper schema */
1144         selectSourceSchema("pg_catalog");
1145
1146         /* Get the database owner and parameters from pg_database */
1147         appendPQExpBuffer(dbQry, "select (select usename from pg_user where usesysid = datdba) as dba,"
1148                                           " pg_encoding_to_char(encoding) as encoding,"
1149                                           " datpath from pg_database"
1150                                           " where datname = ");
1151         appendStringLiteral(dbQry, datname, true);
1152
1153         res = PQexec(g_conn, dbQry->data);
1154         if (!res ||
1155                 PQresultStatus(res) != PGRES_TUPLES_OK)
1156         {
1157                 write_msg(NULL, "SQL command failed\n");
1158                 write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
1159                 write_msg(NULL, "The command was: %s\n", dbQry->data);
1160                 exit_nicely();
1161         }
1162
1163         ntups = PQntuples(res);
1164
1165         if (ntups <= 0)
1166         {
1167                 write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
1168                                   datname);
1169                 exit_nicely();
1170         }
1171
1172         if (ntups != 1)
1173         {
1174                 write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
1175                                   ntups, datname);
1176                 exit_nicely();
1177         }
1178
1179         i_dba = PQfnumber(res, "dba");
1180         i_encoding = PQfnumber(res, "encoding");
1181         i_datpath = PQfnumber(res, "datpath");
1182         dba = PQgetvalue(res, 0, i_dba);
1183         encoding = PQgetvalue(res, 0, i_encoding);
1184         datpath = PQgetvalue(res, 0, i_datpath);
1185
1186         appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
1187                                           fmtId(datname));
1188         if (strlen(datpath) > 0)
1189         {
1190                 appendPQExpBuffer(creaQry, " LOCATION = ");
1191                 appendStringLiteral(creaQry, datpath, true);
1192         }
1193         if (strlen(encoding) > 0)
1194         {
1195                 appendPQExpBuffer(creaQry, " ENCODING = ");
1196                 appendStringLiteral(creaQry, encoding, true);
1197         }
1198         appendPQExpBuffer(creaQry, ";\n");
1199
1200         appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
1201                                           fmtId(datname));
1202
1203         ArchiveEntry(AH, "0",           /* OID */
1204                                  datname,               /* Name */
1205                                  NULL,                  /* Namespace */
1206                                  dba,                   /* Owner */
1207                                  "DATABASE",    /* Desc */
1208                                  NULL,                  /* Deps */
1209                                  creaQry->data, /* Create */
1210                                  delQry->data,  /* Del */
1211                                  NULL,                  /* Copy */
1212                                  NULL,                  /* Dumper */
1213                                  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         /* Make sure we are in proper schema */
1250         selectSourceSchema("pg_catalog");
1251
1252         /* Cursor to get all BLOB tables */
1253         if (AH->remoteVersion >= 70100)
1254                 appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT DISTINCT loid FROM pg_largeobject");
1255         else
1256                 appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
1257
1258         res = PQexec(g_conn, oidQry->data);
1259         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1260         {
1261                 write_msg(NULL, "dumpBlobs(): cursor declaration failed: %s", PQerrorMessage(g_conn));
1262                 exit_nicely();
1263         }
1264
1265         /* Fetch for cursor */
1266         appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
1267
1268         do
1269         {
1270                 /* Do a fetch */
1271                 PQclear(res);
1272                 res = PQexec(g_conn, oidFetchQry->data);
1273
1274                 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
1275                 {
1276                         write_msg(NULL, "dumpBlobs(): fetch from cursor failed: %s",
1277                                           PQerrorMessage(g_conn));
1278                         exit_nicely();
1279                 }
1280
1281                 /* Process the tuples, if any */
1282                 for (i = 0; i < PQntuples(res); i++)
1283                 {
1284                         blobOid = atooid(PQgetvalue(res, i, 0));
1285                         /* Open the BLOB */
1286                         loFd = lo_open(g_conn, blobOid, INV_READ);
1287                         if (loFd == -1)
1288                         {
1289                                 write_msg(NULL, "dumpBlobs(): could not open large object: %s",
1290                                                   PQerrorMessage(g_conn));
1291                                 exit_nicely();
1292                         }
1293
1294                         StartBlob(AH, blobOid);
1295
1296                         /* Now read it in chunks, sending data to archive */
1297                         do
1298                         {
1299                                 cnt = lo_read(g_conn, loFd, buf, loBufSize);
1300                                 if (cnt < 0)
1301                                 {
1302                                         write_msg(NULL, "dumpBlobs(): error reading large object: %s",
1303                                                           PQerrorMessage(g_conn));
1304                                         exit_nicely();
1305                                 }
1306
1307                                 WriteData(AH, buf, cnt);
1308
1309                         } while (cnt > 0);
1310
1311                         lo_close(g_conn, loFd);
1312
1313                         EndBlob(AH, blobOid);
1314
1315                 }
1316         } while (PQntuples(res) > 0);
1317
1318         destroyPQExpBuffer(oidQry);
1319         destroyPQExpBuffer(oidFetchQry);
1320
1321         return 1;
1322 }
1323
1324 /*
1325  * getNamespaces:
1326  *        read all namespaces in the system catalogs and return them in the
1327  * NamespaceInfo* structure
1328  *
1329  *      numNamespaces is set to the number of namespaces read in
1330  */
1331 NamespaceInfo *
1332 getNamespaces(int *numNamespaces)
1333 {
1334         PGresult   *res;
1335         int                     ntups;
1336         int                     i;
1337         PQExpBuffer query;
1338         NamespaceInfo *nsinfo;
1339         int                     i_oid;
1340         int                     i_nspname;
1341         int                     i_usename;
1342         int                     i_nspacl;
1343
1344         /*
1345          * Before 7.3, there are no real namespaces; create two dummy entries,
1346          * one for user stuff and one for system stuff.
1347          */
1348         if (g_fout->remoteVersion < 70300)
1349         {
1350                 nsinfo = (NamespaceInfo *) malloc(2 * sizeof(NamespaceInfo));
1351
1352                 nsinfo[0].oid = strdup("0");
1353                 nsinfo[0].nspname = strdup("");
1354                 nsinfo[0].usename = strdup("");
1355                 nsinfo[0].nspacl = strdup("");
1356
1357                 selectDumpableNamespace(&nsinfo[0]);
1358
1359                 nsinfo[1].oid = strdup("1");
1360                 nsinfo[1].nspname = strdup("pg_catalog");
1361                 nsinfo[1].usename = strdup("");
1362                 nsinfo[1].nspacl = strdup("");
1363
1364                 selectDumpableNamespace(&nsinfo[1]);
1365
1366                 g_namespaces = nsinfo;
1367                 g_numNamespaces = *numNamespaces = 2;
1368
1369                 return nsinfo;
1370         }
1371
1372         query = createPQExpBuffer();
1373
1374         /* Make sure we are in proper schema */
1375         selectSourceSchema("pg_catalog");
1376
1377         /*
1378          * we fetch all namespaces including system ones, so that every object
1379          * we read in can be linked to a containing namespace.
1380          */
1381         appendPQExpBuffer(query, "SELECT oid, nspname, "
1382         "(select usename from pg_user where nspowner = usesysid) as usename, "
1383                                           "nspacl "
1384                                           "FROM pg_namespace");
1385
1386         res = PQexec(g_conn, query->data);
1387         if (!res ||
1388                 PQresultStatus(res) != PGRES_TUPLES_OK)
1389         {
1390                 write_msg(NULL, "query to obtain list of schemas failed: %s", PQerrorMessage(g_conn));
1391                 exit_nicely();
1392         }
1393
1394         ntups = PQntuples(res);
1395
1396         nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
1397
1398         i_oid = PQfnumber(res, "oid");
1399         i_nspname = PQfnumber(res, "nspname");
1400         i_usename = PQfnumber(res, "usename");
1401         i_nspacl = PQfnumber(res, "nspacl");
1402
1403         for (i = 0; i < ntups; i++)
1404         {
1405                 nsinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1406                 nsinfo[i].nspname = strdup(PQgetvalue(res, i, i_nspname));
1407                 nsinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1408                 nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
1409
1410                 /* Decide whether to dump this namespace */
1411                 selectDumpableNamespace(&nsinfo[i]);
1412
1413                 if (strlen(nsinfo[i].usename) == 0)
1414                         write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
1415                                           nsinfo[i].nspname);
1416         }
1417
1418         /*
1419          * If the user attempted to dump a specific namespace, check to ensure
1420          * that the specified namespace actually exists.
1421          */
1422         if (selectSchemaName)
1423         {
1424                 for (i = 0; i < ntups; i++)
1425                         if (strcmp(nsinfo[i].nspname, selectSchemaName) == 0)
1426                                 break;
1427
1428                 /* Didn't find a match */
1429                 if (i == ntups)
1430                 {
1431                         write_msg(NULL, "specified schema \"%s\" does not exist\n",
1432                                           selectSchemaName);
1433                         exit_nicely();
1434                 }
1435         }
1436
1437         PQclear(res);
1438         destroyPQExpBuffer(query);
1439
1440         g_namespaces = nsinfo;
1441         g_numNamespaces = *numNamespaces = ntups;
1442
1443         return nsinfo;
1444 }
1445
1446 /*
1447  * findNamespace:
1448  *              given a namespace OID and an object OID, look up the info read by
1449  *              getNamespaces
1450  *
1451  * NB: for pre-7.3 source database, we use object OID to guess whether it's
1452  * a system object or not.      In 7.3 and later there is no guessing.
1453  */
1454 static NamespaceInfo *
1455 findNamespace(const char *nsoid, const char *objoid)
1456 {
1457         int                     i;
1458
1459         if (g_fout->remoteVersion >= 70300)
1460         {
1461                 for (i = 0; i < g_numNamespaces; i++)
1462                 {
1463                         NamespaceInfo *nsinfo = &g_namespaces[i];
1464
1465                         if (strcmp(nsoid, nsinfo->oid) == 0)
1466                                 return nsinfo;
1467                 }
1468                 write_msg(NULL, "schema with OID %s does not exist\n", nsoid);
1469                 exit_nicely();
1470         }
1471         else
1472         {
1473                 /* This code depends on the layout set up by getNamespaces. */
1474                 if (atooid(objoid) > g_last_builtin_oid)
1475                         i = 0;                          /* user object */
1476                 else
1477                         i = 1;                          /* system object */
1478                 return &g_namespaces[i];
1479         }
1480
1481         return NULL;                            /* keep compiler quiet */
1482 }
1483
1484 /*
1485  * getTypes:
1486  *        read all types in the system catalogs and return them in the
1487  * TypeInfo* structure
1488  *
1489  *      numTypes is set to the number of types read in
1490  */
1491 TypeInfo *
1492 getTypes(int *numTypes)
1493 {
1494         PGresult   *res;
1495         int                     ntups;
1496         int                     i;
1497         PQExpBuffer query = createPQExpBuffer();
1498         TypeInfo   *tinfo;
1499         int                     i_oid;
1500         int                     i_typname;
1501         int                     i_typnamespace;
1502         int                     i_usename;
1503         int                     i_typelem;
1504         int                     i_typrelid;
1505         int                     i_typrelkind;
1506         int                     i_typtype;
1507         int                     i_typisdefined;
1508
1509         /*
1510          * we include even the built-in types because those may be used as
1511          * array elements by user-defined types
1512          *
1513          * we filter out the built-in types when we dump out the types
1514          *
1515          * same approach for undefined (shell) types
1516          */
1517
1518         /* Make sure we are in proper schema */
1519         selectSourceSchema("pg_catalog");
1520
1521         if (g_fout->remoteVersion >= 70300)
1522         {
1523                 appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
1524                                                   "typnamespace, "
1525                                                   "(select usename from pg_user where typowner = usesysid) as usename, "
1526                                                   "typelem, typrelid, "
1527                                                   "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
1528                                                   "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
1529                                                   "typtype, typisdefined "
1530                                                   "FROM pg_type");
1531         }
1532         else
1533         {
1534                 appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
1535                                                   "0::oid as typnamespace, "
1536                                                   "(select usename from pg_user where typowner = usesysid) as usename, "
1537                                                   "typelem, typrelid, "
1538                                                   "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
1539                                                   "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
1540                                                   "typtype, typisdefined "
1541                                                   "FROM pg_type");
1542         }
1543
1544         res = PQexec(g_conn, query->data);
1545         if (!res ||
1546                 PQresultStatus(res) != PGRES_TUPLES_OK)
1547         {
1548                 write_msg(NULL, "query to obtain list of data types failed: %s", PQerrorMessage(g_conn));
1549                 exit_nicely();
1550         }
1551
1552         ntups = PQntuples(res);
1553
1554         tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
1555
1556         i_oid = PQfnumber(res, "oid");
1557         i_typname = PQfnumber(res, "typname");
1558         i_typnamespace = PQfnumber(res, "typnamespace");
1559         i_usename = PQfnumber(res, "usename");
1560         i_typelem = PQfnumber(res, "typelem");
1561         i_typrelid = PQfnumber(res, "typrelid");
1562         i_typrelkind = PQfnumber(res, "typrelkind");
1563         i_typtype = PQfnumber(res, "typtype");
1564         i_typisdefined = PQfnumber(res, "typisdefined");
1565
1566         for (i = 0; i < ntups; i++)
1567         {
1568                 tinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1569                 tinfo[i].typname = strdup(PQgetvalue(res, i, i_typname));
1570                 tinfo[i].typnamespace = findNamespace(PQgetvalue(res, i, i_typnamespace),
1571                                                                                           tinfo[i].oid);
1572                 tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1573                 tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
1574                 tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
1575                 tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
1576                 tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
1577
1578                 /*
1579                  * check for user-defined array types, omit system generated ones
1580                  */
1581                 if ((strcmp(tinfo[i].typelem, "0") != 0) &&
1582                         tinfo[i].typname[0] != '_')
1583                         tinfo[i].isArray = true;
1584                 else
1585                         tinfo[i].isArray = false;
1586
1587                 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
1588                         tinfo[i].isDefined = true;
1589                 else
1590                         tinfo[i].isDefined = false;
1591
1592                 if (strlen(tinfo[i].usename) == 0 && tinfo[i].isDefined)
1593                         write_msg(NULL, "WARNING: owner of data type \"%s\" appears to be invalid\n",
1594                                           tinfo[i].typname);
1595         }
1596
1597         *numTypes = ntups;
1598
1599         PQclear(res);
1600
1601         destroyPQExpBuffer(query);
1602
1603         return tinfo;
1604 }
1605
1606 /*
1607  * getOperators:
1608  *        read all operators in the system catalogs and return them in the
1609  * OprInfo* structure
1610  *
1611  *      numOprs is set to the number of operators read in
1612  */
1613 OprInfo *
1614 getOperators(int *numOprs)
1615 {
1616         PGresult   *res;
1617         int                     ntups;
1618         int                     i;
1619         PQExpBuffer query = createPQExpBuffer();
1620         OprInfo    *oprinfo;
1621         int                     i_oid;
1622         int                     i_oprname;
1623         int                     i_oprnamespace;
1624         int                     i_usename;
1625         int                     i_oprcode;
1626
1627         /*
1628          * find all operators, including builtin operators; we filter out
1629          * system-defined operators at dump-out time.
1630          */
1631
1632         /* Make sure we are in proper schema */
1633         selectSourceSchema("pg_catalog");
1634
1635         if (g_fout->remoteVersion >= 70300)
1636         {
1637                 appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, "
1638                                                   "oprnamespace, "
1639                                                   "(select usename from pg_user where oprowner = usesysid) as usename, "
1640                                                   "oprcode::oid as oprcode "
1641                                                   "from pg_operator");
1642         }
1643         else
1644         {
1645                 appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, "
1646                                                   "0::oid as oprnamespace, "
1647                                                   "(select usename from pg_user where oprowner = usesysid) as usename, "
1648                                                   "oprcode::oid as oprcode "
1649                                                   "from pg_operator");
1650         }
1651
1652         res = PQexec(g_conn, query->data);
1653         if (!res ||
1654                 PQresultStatus(res) != PGRES_TUPLES_OK)
1655         {
1656                 write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
1657                 exit_nicely();
1658         }
1659
1660         ntups = PQntuples(res);
1661         *numOprs = ntups;
1662
1663         oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
1664
1665         i_oid = PQfnumber(res, "oid");
1666         i_oprname = PQfnumber(res, "oprname");
1667         i_oprnamespace = PQfnumber(res, "oprnamespace");
1668         i_usename = PQfnumber(res, "usename");
1669         i_oprcode = PQfnumber(res, "oprcode");
1670
1671         for (i = 0; i < ntups; i++)
1672         {
1673                 oprinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1674                 oprinfo[i].oprname = strdup(PQgetvalue(res, i, i_oprname));
1675                 oprinfo[i].oprnamespace = findNamespace(PQgetvalue(res, i, i_oprnamespace),
1676                                                                                                 oprinfo[i].oid);
1677                 oprinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1678                 oprinfo[i].oprcode = strdup(PQgetvalue(res, i, i_oprcode));
1679
1680                 if (strlen(oprinfo[i].usename) == 0)
1681                         write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
1682                                           oprinfo[i].oprname);
1683         }
1684
1685         PQclear(res);
1686
1687         destroyPQExpBuffer(query);
1688
1689         return oprinfo;
1690 }
1691
1692 /*
1693  * getOpclasses:
1694  *        read all opclasses in the system catalogs and return them in the
1695  * OpclassInfo* structure
1696  *
1697  *      numOpclasses is set to the number of opclasses read in
1698  */
1699 OpclassInfo *
1700 getOpclasses(int *numOpclasses)
1701 {
1702         PGresult   *res;
1703         int                     ntups;
1704         int                     i;
1705         PQExpBuffer query = createPQExpBuffer();
1706         OpclassInfo *opcinfo;
1707         int                     i_oid;
1708         int                     i_opcname;
1709         int                     i_opcnamespace;
1710         int                     i_usename;
1711
1712         /*
1713          * find all opclasses, including builtin opclasses; we filter out
1714          * system-defined opclasses at dump-out time.
1715          */
1716
1717         /* Make sure we are in proper schema */
1718         selectSourceSchema("pg_catalog");
1719
1720         if (g_fout->remoteVersion >= 70300)
1721         {
1722                 appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
1723                                                   "opcnamespace, "
1724                                                   "(select usename from pg_user where opcowner = usesysid) as usename "
1725                                                   "from pg_opclass");
1726         }
1727         else
1728         {
1729                 appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
1730                                                   "0::oid as opcnamespace, "
1731                                                   "''::name as usename "
1732                                                   "from pg_opclass");
1733         }
1734
1735         res = PQexec(g_conn, query->data);
1736         if (!res ||
1737                 PQresultStatus(res) != PGRES_TUPLES_OK)
1738         {
1739                 write_msg(NULL, "query to obtain list of operator classes failed: %s", PQerrorMessage(g_conn));
1740                 exit_nicely();
1741         }
1742
1743         ntups = PQntuples(res);
1744         *numOpclasses = ntups;
1745
1746         opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));
1747
1748         i_oid = PQfnumber(res, "oid");
1749         i_opcname = PQfnumber(res, "opcname");
1750         i_opcnamespace = PQfnumber(res, "opcnamespace");
1751         i_usename = PQfnumber(res, "usename");
1752
1753         for (i = 0; i < ntups; i++)
1754         {
1755                 opcinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1756                 opcinfo[i].opcname = strdup(PQgetvalue(res, i, i_opcname));
1757                 opcinfo[i].opcnamespace = findNamespace(PQgetvalue(res, i, i_opcnamespace),
1758                                                                                                 opcinfo[i].oid);
1759                 opcinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1760
1761                 if (g_fout->remoteVersion >= 70300)
1762                 {
1763                         if (strlen(opcinfo[i].usename) == 0)
1764                                 write_msg(NULL, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
1765                                                   opcinfo[i].opcname);
1766                 }
1767         }
1768
1769         PQclear(res);
1770
1771         destroyPQExpBuffer(query);
1772
1773         return opcinfo;
1774 }
1775
1776 /*
1777  * getAggregates:
1778  *        read all the user-defined aggregates in the system catalogs and
1779  * return them in the AggInfo* structure
1780  *
1781  * numAggs is set to the number of aggregates read in
1782  */
1783 AggInfo *
1784 getAggregates(int *numAggs)
1785 {
1786         PGresult   *res;
1787         int                     ntups;
1788         int                     i;
1789         PQExpBuffer query = createPQExpBuffer();
1790         AggInfo    *agginfo;
1791
1792         int                     i_oid;
1793         int                     i_aggname;
1794         int                     i_aggnamespace;
1795         int                     i_aggbasetype;
1796         int                     i_usename;
1797         int                     i_aggacl;
1798
1799         /* Make sure we are in proper schema */
1800         selectSourceSchema("pg_catalog");
1801
1802         /* find all user-defined aggregates */
1803
1804         if (g_fout->remoteVersion >= 70300)
1805         {
1806                 appendPQExpBuffer(query, "SELECT pg_proc.oid, proname as aggname, "
1807                                                   "pronamespace as aggnamespace, "
1808                                                   "proargtypes[0] as aggbasetype, "
1809                                                   "(select usename from pg_user where proowner = usesysid) as usename, "
1810                                                   "proacl as aggacl "
1811                                                   "FROM pg_proc "
1812                                                   "WHERE proisagg "
1813                                                   "AND pronamespace != "
1814                   "(select oid from pg_namespace where nspname = 'pg_catalog')");
1815         }
1816         else
1817         {
1818                 appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, "
1819                                                   "0::oid as aggnamespace, "
1820                                                   "aggbasetype, "
1821                                                   "(select usename from pg_user where aggowner = usesysid) as usename, "
1822                                                   "'{=X}' as aggacl "
1823                                                   "from pg_aggregate "
1824                                                   "where oid > '%u'::oid",
1825                                                   g_last_builtin_oid);
1826         }
1827
1828         res = PQexec(g_conn, query->data);
1829         if (!res ||
1830                 PQresultStatus(res) != PGRES_TUPLES_OK)
1831         {
1832                 write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
1833                                   PQerrorMessage(g_conn));
1834                 exit_nicely();
1835         }
1836
1837         ntups = PQntuples(res);
1838         *numAggs = ntups;
1839
1840         agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
1841
1842         i_oid = PQfnumber(res, "oid");
1843         i_aggname = PQfnumber(res, "aggname");
1844         i_aggnamespace = PQfnumber(res, "aggnamespace");
1845         i_aggbasetype = PQfnumber(res, "aggbasetype");
1846         i_usename = PQfnumber(res, "usename");
1847         i_aggacl = PQfnumber(res, "aggacl");
1848
1849         for (i = 0; i < ntups; i++)
1850         {
1851                 agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1852                 agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname));
1853                 agginfo[i].aggnamespace = findNamespace(PQgetvalue(res, i, i_aggnamespace),
1854                                                                                                 agginfo[i].oid);
1855                 agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype));
1856                 agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1857                 if (strlen(agginfo[i].usename) == 0)
1858                         write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
1859                                           agginfo[i].aggname);
1860                 agginfo[i].aggacl = strdup(PQgetvalue(res, i, i_aggacl));
1861                 agginfo[i].anybasetype = false; /* computed when it's dumped */
1862                 agginfo[i].fmtbasetype = NULL;  /* computed when it's dumped */
1863         }
1864
1865         PQclear(res);
1866
1867         destroyPQExpBuffer(query);
1868
1869         return agginfo;
1870 }
1871
1872 /*
1873  * getFuncs:
1874  *        read all the user-defined functions in the system catalogs and
1875  * return them in the FuncInfo* structure
1876  *
1877  * numFuncs is set to the number of functions read in
1878  */
1879 FuncInfo *
1880 getFuncs(int *numFuncs)
1881 {
1882         PGresult   *res;
1883         int                     ntups;
1884         int                     i;
1885         PQExpBuffer query = createPQExpBuffer();
1886         FuncInfo   *finfo;
1887
1888         int                     i_oid;
1889         int                     i_proname;
1890         int                     i_pronamespace;
1891         int                     i_usename;
1892         int                     i_prolang;
1893         int                     i_pronargs;
1894         int                     i_proargtypes;
1895         int                     i_prorettype;
1896         int                     i_proacl;
1897
1898         /* Make sure we are in proper schema */
1899         selectSourceSchema("pg_catalog");
1900
1901         /* find all user-defined funcs */
1902
1903         if (g_fout->remoteVersion >= 70300)
1904         {
1905                 appendPQExpBuffer(query,
1906                                                   "SELECT pg_proc.oid, proname, prolang, "
1907                                                   "pronargs, proargtypes, prorettype, proacl, "
1908                                                   "pronamespace, "
1909                                                   "(select usename from pg_user where proowner = usesysid) as usename "
1910                                                   "FROM pg_proc "
1911                                                   "WHERE NOT proisagg "
1912                                                   "AND pronamespace != "
1913                   "(select oid from pg_namespace where nspname = 'pg_catalog')");
1914         }
1915         else
1916         {
1917                 appendPQExpBuffer(query,
1918                                                   "SELECT pg_proc.oid, proname, prolang, "
1919                                                   "pronargs, proargtypes, prorettype, "
1920                                                   "'{=X}' as proacl, "
1921                                                   "0::oid as pronamespace, "
1922                                                   "(select usename from pg_user where proowner = usesysid) as usename "
1923                                                   "FROM pg_proc "
1924                                                   "where pg_proc.oid > '%u'::oid",
1925                                                   g_last_builtin_oid);
1926         }
1927
1928         res = PQexec(g_conn, query->data);
1929         if (!res ||
1930                 PQresultStatus(res) != PGRES_TUPLES_OK)
1931         {
1932                 write_msg(NULL, "query to obtain list of functions failed: %s",
1933                                   PQerrorMessage(g_conn));
1934                 exit_nicely();
1935         }
1936
1937         ntups = PQntuples(res);
1938
1939         *numFuncs = ntups;
1940
1941         finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
1942
1943         i_oid = PQfnumber(res, "oid");
1944         i_proname = PQfnumber(res, "proname");
1945         i_pronamespace = PQfnumber(res, "pronamespace");
1946         i_usename = PQfnumber(res, "usename");
1947         i_prolang = PQfnumber(res, "prolang");
1948         i_pronargs = PQfnumber(res, "pronargs");
1949         i_proargtypes = PQfnumber(res, "proargtypes");
1950         i_prorettype = PQfnumber(res, "prorettype");
1951         i_proacl = PQfnumber(res, "proacl");
1952
1953         for (i = 0; i < ntups; i++)
1954         {
1955                 finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
1956                 finfo[i].proname = strdup(PQgetvalue(res, i, i_proname));
1957                 finfo[i].pronamespace = findNamespace(PQgetvalue(res, i, i_pronamespace),
1958                                                                                           finfo[i].oid);
1959                 finfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
1960                 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
1961                 finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
1962                 finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
1963                 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
1964                 if (finfo[i].nargs == 0)
1965                         finfo[i].argtypes = NULL;
1966                 else
1967                 {
1968                         finfo[i].argtypes = malloc(finfo[i].nargs * sizeof(finfo[i].argtypes[0]));
1969                         parseNumericArray(PQgetvalue(res, i, i_proargtypes),
1970                                                           finfo[i].argtypes,
1971                                                           finfo[i].nargs);
1972                 }
1973
1974                 finfo[i].dumped = false;
1975
1976                 if (strlen(finfo[i].usename) == 0)
1977                         write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n",
1978                                           finfo[i].proname);
1979         }
1980
1981         PQclear(res);
1982
1983         destroyPQExpBuffer(query);
1984
1985         return finfo;
1986 }
1987
1988 /*
1989  * getTables
1990  *        read all the user-defined tables (no indexes, no catalogs)
1991  * in the system catalogs return them in the TableInfo* structure
1992  *
1993  * numTables is set to the number of tables read in
1994  */
1995 TableInfo *
1996 getTables(int *numTables)
1997 {
1998         PGresult   *res;
1999         int                     ntups;
2000         int                     i;
2001         PQExpBuffer query = createPQExpBuffer();
2002         PQExpBuffer delqry = createPQExpBuffer();
2003         PQExpBuffer lockquery = createPQExpBuffer();
2004         TableInfo  *tblinfo;
2005
2006         int                     i_reloid;
2007         int                     i_relname;
2008         int                     i_relnamespace;
2009         int                     i_relkind;
2010         int                     i_relacl;
2011         int                     i_usename;
2012         int                     i_relchecks;
2013         int                     i_reltriggers;
2014         int                     i_relhasindex;
2015         int                     i_relhasrules;
2016         int                     i_relhasoids;
2017         int                     i_owning_tab;
2018         int                     i_owning_col;
2019
2020         /* Make sure we are in proper schema */
2021         selectSourceSchema("pg_catalog");
2022
2023         /*
2024          * Find all the tables (including views and sequences).
2025          *
2026          * We include system catalogs, so that we can work if a user table is
2027          * defined to inherit from a system catalog (pretty weird, but...)
2028          *
2029          * We ignore tables that are not type 'r' (ordinary relation) or 'S'
2030          * (sequence) or 'v' (view).
2031          *
2032          * Note: in this phase we should collect only a minimal amount of
2033          * information about each table, basically just enough to decide if it
2034          * is interesting.      We must fetch all tables in this phase because
2035          * otherwise we cannot correctly identify inherited columns, serial
2036          * columns, etc.
2037          */
2038
2039         if (g_fout->remoteVersion >= 70300)
2040         {
2041                 /*
2042                  * Left join to pick up dependency info linking sequences to their
2043                  * serial column, if any
2044                  */
2045                 appendPQExpBuffer(query,
2046                                                   "SELECT c.oid, relname, relacl, relkind, "
2047                                                   "relnamespace, "
2048                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2049                                                   "relchecks, reltriggers, "
2050                                                   "relhasindex, relhasrules, relhasoids, "
2051                                                   "d.refobjid as owning_tab, "
2052                                                   "d.refobjsubid as owning_col "
2053                                                   "from pg_class c "
2054                                                   "left join pg_depend d on "
2055                                                   "(c.relkind = '%c' and "
2056                                                 "d.classid = c.tableoid and d.objid = c.oid and "
2057                                                   "d.objsubid = 0 and "
2058                                                 "d.refclassid = c.tableoid and d.deptype = 'i') "
2059                                                   "where relkind in ('%c', '%c', '%c') "
2060                                                   "order by c.oid",
2061                                                   RELKIND_SEQUENCE,
2062                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2063         }
2064         else if (g_fout->remoteVersion >= 70200)
2065         {
2066                 appendPQExpBuffer(query,
2067                                                 "SELECT pg_class.oid, relname, relacl, relkind, "
2068                                                   "0::oid as relnamespace, "
2069                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2070                                                   "relchecks, reltriggers, "
2071                                                   "relhasindex, relhasrules, relhasoids, "
2072                                                   "NULL::oid as owning_tab, "
2073                                                   "NULL::int4 as owning_col "
2074                                                   "from pg_class "
2075                                                   "where relkind in ('%c', '%c', '%c') "
2076                                                   "order by oid",
2077                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2078         }
2079         else if (g_fout->remoteVersion >= 70100)
2080         {
2081                 /* all tables have oids in 7.1 */
2082                 appendPQExpBuffer(query,
2083                                                 "SELECT pg_class.oid, relname, relacl, relkind, "
2084                                                   "0::oid as relnamespace, "
2085                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2086                                                   "relchecks, reltriggers, "
2087                                                   "relhasindex, relhasrules, "
2088                                                   "'t'::bool as relhasoids, "
2089                                                   "NULL::oid as owning_tab, "
2090                                                   "NULL::int4 as owning_col "
2091                                                   "from pg_class "
2092                                                   "where relkind in ('%c', '%c', '%c') "
2093                                                   "order by oid",
2094                                            RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
2095         }
2096         else
2097         {
2098                 /*
2099                  * Before 7.1, view relkind was not set to 'v', so we must check
2100                  * if we have a view by looking for a rule in pg_rewrite.
2101                  */
2102                 appendPQExpBuffer(query,
2103                                                   "SELECT c.oid, relname, relacl, "
2104                                                   "CASE WHEN relhasrules and relkind = 'r' "
2105                                   "  and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
2106                                   "             r.ev_class = c.oid AND r.ev_type = '1') "
2107                                                   "THEN '%c'::\"char\" "
2108                                                   "ELSE relkind END AS relkind,"
2109                                                   "0::oid as relnamespace, "
2110                                                   "(select usename from pg_user where relowner = usesysid) as usename, "
2111                                                   "relchecks, reltriggers, "
2112                                                   "relhasindex, relhasrules, "
2113                                                   "'t'::bool as relhasoids, "
2114                                                   "NULL::oid as owning_tab, "
2115                                                   "NULL::int4 as owning_col "
2116                                                   "from pg_class c "
2117                                                   "where relkind in ('%c', '%c') "
2118                                                   "order by oid",
2119                                                   RELKIND_VIEW,
2120                                                   RELKIND_RELATION, RELKIND_SEQUENCE);
2121         }
2122
2123         res = PQexec(g_conn, query->data);
2124         if (!res ||
2125                 PQresultStatus(res) != PGRES_TUPLES_OK)
2126         {
2127                 write_msg(NULL, "query to obtain list of tables failed: %s",
2128                                   PQerrorMessage(g_conn));
2129                 exit_nicely();
2130         }
2131
2132         ntups = PQntuples(res);
2133
2134         *numTables = ntups;
2135
2136         /*
2137          * Extract data from result and lock dumpable tables.  We do the
2138          * locking before anything else, to minimize the window wherein a
2139          * table could disappear under us.
2140          *
2141          * Note that we have to save info about all tables here, even when
2142          * dumping only one, because we don't yet know which tables might be
2143          * inheritance ancestors of the target table.
2144          */
2145         tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
2146
2147         i_reloid = PQfnumber(res, "oid");
2148         i_relname = PQfnumber(res, "relname");
2149         i_relnamespace = PQfnumber(res, "relnamespace");
2150         i_relacl = PQfnumber(res, "relacl");
2151         i_relkind = PQfnumber(res, "relkind");
2152         i_usename = PQfnumber(res, "usename");
2153         i_relchecks = PQfnumber(res, "relchecks");
2154         i_reltriggers = PQfnumber(res, "reltriggers");
2155         i_relhasindex = PQfnumber(res, "relhasindex");
2156         i_relhasrules = PQfnumber(res, "relhasrules");
2157         i_relhasoids = PQfnumber(res, "relhasoids");
2158         i_owning_tab = PQfnumber(res, "owning_tab");
2159         i_owning_col = PQfnumber(res, "owning_col");
2160
2161         for (i = 0; i < ntups; i++)
2162         {
2163                 tblinfo[i].oid = strdup(PQgetvalue(res, i, i_reloid));
2164                 tblinfo[i].relname = strdup(PQgetvalue(res, i, i_relname));
2165                 tblinfo[i].relnamespace = findNamespace(PQgetvalue(res, i, i_relnamespace),
2166                                                                                                 tblinfo[i].oid);
2167                 tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
2168                 tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
2169                 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
2170                 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
2171                 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
2172                 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
2173                 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
2174                 tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
2175                 if (PQgetisnull(res, i, i_owning_tab))
2176                 {
2177                         tblinfo[i].owning_tab = NULL;
2178                         tblinfo[i].owning_col = 0;
2179                 }
2180                 else
2181                 {
2182                         tblinfo[i].owning_tab = strdup(PQgetvalue(res, i, i_owning_tab));
2183                         tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
2184                 }
2185
2186                 /* other fields were zeroed above */
2187
2188                 /*
2189                  * Decide whether we want to dump this table.  Sequences owned by
2190                  * serial columns are never dumpable on their own; we will
2191                  * transpose their owning table's dump flag to them below.
2192                  */
2193                 if (tblinfo[i].owning_tab == NULL)
2194                         selectDumpableTable(&tblinfo[i]);
2195                 else
2196                         tblinfo[i].dump = false;
2197                 tblinfo[i].interesting = tblinfo[i].dump;
2198
2199                 /*
2200                  * Read-lock target tables to make sure they aren't DROPPED or
2201                  * altered in schema before we get around to dumping them.
2202                  *
2203                  * Note that we don't explicitly lock parents of the target tables;
2204                  * we assume our lock on the child is enough to prevent schema
2205                  * alterations to parent tables.
2206                  *
2207                  * NOTE: it'd be kinda nice to lock views and sequences too, not only
2208                  * plain tables, but the backend doesn't presently allow that.
2209                  */
2210                 if (tblinfo[i].dump && tblinfo[i].relkind == RELKIND_RELATION)
2211                 {
2212                         PGresult   *lres;
2213
2214                         resetPQExpBuffer(lockquery);
2215                         appendPQExpBuffer(lockquery,
2216                                                           "LOCK TABLE %s IN ACCESS SHARE MODE",
2217                                                  fmtQualifiedId(tblinfo[i].relnamespace->nspname,
2218                                                                                 tblinfo[i].relname));
2219                         lres = PQexec(g_conn, lockquery->data);
2220                         if (!lres || PQresultStatus(lres) != PGRES_COMMAND_OK)
2221                         {
2222                                 write_msg(NULL, "attempt to lock table \"%s\" failed: %s",
2223                                                   tblinfo[i].relname, PQerrorMessage(g_conn));
2224                                 exit_nicely();
2225                         }
2226                         PQclear(lres);
2227                 }
2228
2229                 /* Emit notice if join for owner failed */
2230                 if (strlen(tblinfo[i].usename) == 0)
2231                         write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
2232                                           tblinfo[i].relname);
2233         }
2234
2235         /*
2236          * If the user is attempting to dump a specific table, check to ensure
2237          * that the specified table actually exists.  (This is a bit simplistic
2238          * since we don't fully check the combination of -n and -t switches.)
2239          */
2240         if (selectTableName)
2241         {
2242                 for (i = 0; i < ntups; i++)
2243                         if (strcmp(tblinfo[i].relname, selectTableName) == 0)
2244                                 break;
2245
2246                 /* Didn't find a match */
2247                 if (i == ntups)
2248                 {
2249                         write_msg(NULL, "specified table \"%s\" does not exist\n",
2250                                           selectTableName);
2251                         exit_nicely();
2252                 }
2253         }
2254
2255         PQclear(res);
2256         destroyPQExpBuffer(query);
2257         destroyPQExpBuffer(delqry);
2258         destroyPQExpBuffer(lockquery);
2259
2260         return tblinfo;
2261 }
2262
2263 /*
2264  * getInherits
2265  *        read all the inheritance information
2266  * from the system catalogs return them in the InhInfo* structure
2267  *
2268  * numInherits is set to the number of pairs read in
2269  */
2270 InhInfo *
2271 getInherits(int *numInherits)
2272 {
2273         PGresult   *res;
2274         int                     ntups;
2275         int                     i;
2276         PQExpBuffer query = createPQExpBuffer();
2277         InhInfo    *inhinfo;
2278
2279         int                     i_inhrelid;
2280         int                     i_inhparent;
2281
2282         /* Make sure we are in proper schema */
2283         selectSourceSchema("pg_catalog");
2284
2285         /* find all the inheritance information */
2286
2287         appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
2288
2289         res = PQexec(g_conn, query->data);
2290         if (!res ||
2291                 PQresultStatus(res) != PGRES_TUPLES_OK)
2292         {
2293                 write_msg(NULL, "query to obtain inheritance relationships failed: %s",
2294                                   PQerrorMessage(g_conn));
2295                 exit_nicely();
2296         }
2297
2298         ntups = PQntuples(res);
2299
2300         *numInherits = ntups;
2301
2302         inhinfo = (InhInfo *) malloc(ntups * sizeof(InhInfo));
2303
2304         i_inhrelid = PQfnumber(res, "inhrelid");
2305         i_inhparent = PQfnumber(res, "inhparent");
2306
2307         for (i = 0; i < ntups; i++)
2308         {
2309                 inhinfo[i].inhrelid = strdup(PQgetvalue(res, i, i_inhrelid));
2310                 inhinfo[i].inhparent = strdup(PQgetvalue(res, i, i_inhparent));
2311         }
2312
2313         PQclear(res);
2314
2315         destroyPQExpBuffer(query);
2316
2317         return inhinfo;
2318 }
2319
2320 /*
2321  * getTableAttrs -
2322  *        for each interesting table, read its attributes types and names
2323  *
2324  * this is implemented in a very inefficient way right now, looping
2325  * through the tblinfo and doing a join per table to find the attrs and their
2326  * types
2327  *
2328  *      modifies tblinfo
2329  */
2330 void
2331 getTableAttrs(TableInfo *tblinfo, int numTables)
2332 {
2333         int                     i,
2334                                 j,
2335                                 k;
2336         PQExpBuffer q = createPQExpBuffer();
2337         int                     i_attnum;
2338         int                     i_attname;
2339         int                     i_atttypname;
2340         int                     i_atttypmod;
2341         int                     i_attstattarget;
2342         int                     i_attstorage;
2343         int                     i_typstorage;
2344         int                     i_attnotnull;
2345         int                     i_atthasdef;
2346         int                     i_attisdropped;
2347         int                     i_attislocal;
2348         PGresult   *res;
2349         int                     ntups;
2350         bool            hasdefaults;
2351
2352         for (i = 0; i < numTables; i++)
2353         {
2354                 TableInfo  *tbinfo = &tblinfo[i];
2355
2356                 /* Don't bother to collect info for sequences */
2357                 if (tbinfo->relkind == RELKIND_SEQUENCE)
2358                         continue;
2359
2360                 /* Don't bother with uninteresting tables, either */
2361                 if (!tbinfo->interesting)
2362                         continue;
2363
2364                 /*
2365                  * Make sure we are in proper schema for this table; this allows
2366                  * correct retrieval of formatted type names and default exprs
2367                  */
2368                 selectSourceSchema(tbinfo->relnamespace->nspname);
2369
2370                 /* find all the user attributes and their types */
2371
2372                 /*
2373                  * we must read the attribute names in attribute number order!
2374                  * because we will use the attnum to index into the attnames array
2375                  * later.  We actually ask to order by "attrelid, attnum" because
2376                  * (at least up to 7.3) the planner is not smart enough to realize
2377                  * it needn't re-sort the output of an indexscan on
2378                  * pg_attribute_relid_attnum_index.
2379                  */
2380                 if (g_verbose)
2381                         write_msg(NULL, "finding the columns and types of table \"%s\"\n",
2382                                           tbinfo->relname);
2383
2384                 resetPQExpBuffer(q);
2385
2386                 if (g_fout->remoteVersion >= 70300)
2387                 {
2388                         /* need left join here to not fail on dropped columns ... */
2389                         appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
2390                           "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
2391                            "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname "
2392                                                           "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
2393                                                           "on a.atttypid = t.oid "
2394                                                           "where a.attrelid = '%s'::pg_catalog.oid "
2395                                                           "and a.attnum > 0::pg_catalog.int2 "
2396                                                           "order by a.attrelid, a.attnum",
2397                                                           tbinfo->oid);
2398                 }
2399                 else if (g_fout->remoteVersion >= 70100)
2400                 {
2401                         /*
2402                          * attstattarget doesn't exist in 7.1.  It does exist in 7.2,
2403                          * but we don't dump it because we can't tell whether it's
2404                          * been explicitly set or was just a default.
2405                          */
2406                         appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, -1 as attstattarget, a.attstorage, t.typstorage, "
2407                                                           "a.attnotnull, a.atthasdef, false as attisdropped, null as attislocal, "
2408                                                   "format_type(t.oid,a.atttypmod) as atttypname "
2409                                                           "from pg_attribute a left join pg_type t "
2410                                                           "on a.atttypid = t.oid "
2411                                                           "where a.attrelid = '%s'::oid "
2412                                                           "and a.attnum > 0::int2 "
2413                                                           "order by a.attrelid, a.attnum",
2414                                                           tbinfo->oid);
2415                 }
2416                 else
2417                 {
2418                         /* format_type not available before 7.1 */
2419                         appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, attstorage, attstorage as typstorage, "
2420                                                           "attnotnull, atthasdef, false as attisdropped, null as attislocal, "
2421                                                           "(select typname from pg_type where oid = atttypid) as atttypname "
2422                                                           "from pg_attribute a "
2423                                                           "where attrelid = '%s'::oid "
2424                                                           "and attnum > 0::int2 "
2425                                                           "order by attrelid, attnum",
2426                                                           tbinfo->oid);
2427                 }
2428
2429                 res = PQexec(g_conn, q->data);
2430                 if (!res ||
2431                         PQresultStatus(res) != PGRES_TUPLES_OK)
2432                 {
2433                         write_msg(NULL, "query to get table columns failed: %s", PQerrorMessage(g_conn));
2434                         exit_nicely();
2435                 }
2436
2437                 ntups = PQntuples(res);
2438
2439                 i_attnum = PQfnumber(res, "attnum");
2440                 i_attname = PQfnumber(res, "attname");
2441                 i_atttypname = PQfnumber(res, "atttypname");
2442                 i_atttypmod = PQfnumber(res, "atttypmod");
2443                 i_attstattarget = PQfnumber(res, "attstattarget");
2444                 i_attstorage = PQfnumber(res, "attstorage");
2445                 i_typstorage = PQfnumber(res, "typstorage");
2446                 i_attnotnull = PQfnumber(res, "attnotnull");
2447                 i_atthasdef = PQfnumber(res, "atthasdef");
2448                 i_attisdropped = PQfnumber(res, "attisdropped");
2449                 i_attislocal = PQfnumber(res, "attislocal");
2450
2451                 tbinfo->numatts = ntups;
2452                 tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
2453                 tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
2454                 tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
2455                 tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
2456                 tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
2457                 tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
2458                 tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
2459                 tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
2460                 tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
2461                 tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
2462                 tbinfo->adef_expr = (char **) malloc(ntups * sizeof(char *));
2463                 tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
2464                 tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
2465                 tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
2466                 hasdefaults = false;
2467
2468                 for (j = 0; j < ntups; j++)
2469                 {
2470                         if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
2471                         {
2472                                 write_msg(NULL, "invalid column numbering in table \"%s\"\n",
2473                                                   tbinfo->relname);
2474                                 exit_nicely();
2475                         }
2476                         tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
2477                         tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
2478                         tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
2479                         tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
2480                         tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
2481                         tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
2482                         tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
2483                         tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
2484                         tbinfo->attisserial[j] = false;         /* fix below */
2485                         tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
2486                         tbinfo->adef_expr[j] = NULL;            /* fix below */
2487                         if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
2488                                 hasdefaults = true;
2489                         /* these flags will be set in flagInhAttrs() */
2490                         tbinfo->inhAttrs[j] = false;
2491                         tbinfo->inhAttrDef[j] = false;
2492                         tbinfo->inhNotNull[j] = false;
2493                 }
2494
2495                 PQclear(res);
2496
2497                 if (hasdefaults)
2498                 {
2499                         int                     numDefaults;
2500
2501                         if (g_verbose)
2502                                 write_msg(NULL, "finding default expressions of table \"%s\"\n",
2503                                                   tbinfo->relname);
2504
2505                         resetPQExpBuffer(q);
2506                         if (g_fout->remoteVersion >= 70300)
2507                         {
2508                                 appendPQExpBuffer(q, "SELECT adnum, "
2509                                            "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
2510                                                                   "FROM pg_catalog.pg_attrdef "
2511                                                                   "WHERE adrelid = '%s'::pg_catalog.oid",
2512                                                                   tbinfo->oid);
2513                         }
2514                         else if (g_fout->remoteVersion >= 70200)
2515                         {
2516                                 appendPQExpBuffer(q, "SELECT adnum, "
2517                                                                   "pg_get_expr(adbin, adrelid) AS adsrc "
2518                                                                   "FROM pg_attrdef "
2519                                                                   "WHERE adrelid = '%s'::oid",
2520                                                                   tbinfo->oid);
2521                         }
2522                         else
2523                         {
2524                                 /* no pg_get_expr, so must rely on adsrc */
2525                                 appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef "
2526                                                                   "WHERE adrelid = '%s'::oid",
2527                                                                   tbinfo->oid);
2528                         }
2529                         res = PQexec(g_conn, q->data);
2530                         if (!res ||
2531                                 PQresultStatus(res) != PGRES_TUPLES_OK)
2532                         {
2533                                 write_msg(NULL, "query to get column default values failed: %s",
2534                                                   PQerrorMessage(g_conn));
2535                                 exit_nicely();
2536                         }
2537
2538                         numDefaults = PQntuples(res);
2539                         for (j = 0; j < numDefaults; j++)
2540                         {
2541                                 int                     adnum = atoi(PQgetvalue(res, j, 0));
2542
2543                                 if (adnum <= 0 || adnum > ntups)
2544                                 {
2545                                         write_msg(NULL, "invalid adnum value %d for table \"%s\"\n",
2546                                                           adnum, tbinfo->relname);
2547                                         exit_nicely();
2548                                 }
2549                                 tbinfo->adef_expr[adnum - 1] = strdup(PQgetvalue(res, j, 1));
2550                         }
2551                         PQclear(res);
2552                 }
2553
2554                 /*
2555                  * Check to see if any columns are serial columns.      Our first
2556                  * quick filter is that it must be integer or bigint with a
2557                  * default.  If so, we scan to see if we found a sequence linked
2558                  * to this column. If we did, mark the column and sequence
2559                  * appropriately.
2560                  */
2561                 for (j = 0; j < ntups; j++)
2562                 {
2563                         /*
2564                          * Note assumption that format_type will show these types as
2565                          * exactly "integer" and "bigint" regardless of schema path.
2566                          * This is correct in 7.3 but needs to be watched.
2567                          */
2568                         if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
2569                                 strcmp(tbinfo->atttypnames[j], "bigint") != 0)
2570                                 continue;
2571                         if (tbinfo->adef_expr[j] == NULL)
2572                                 continue;
2573                         for (k = 0; k < numTables; k++)
2574                         {
2575                                 TableInfo  *seqinfo = &tblinfo[k];
2576
2577                                 if (seqinfo->owning_tab != NULL &&
2578                                         strcmp(seqinfo->owning_tab, tbinfo->oid) == 0 &&
2579                                         seqinfo->owning_col == j + 1)
2580                                 {
2581                                         /*
2582                                          * Found a match.  Copy the table's interesting and
2583                                          * dumpable flags to the sequence.
2584                                          */
2585                                         tbinfo->attisserial[j] = true;
2586                                         seqinfo->interesting = tbinfo->interesting;
2587                                         seqinfo->dump = tbinfo->dump;
2588                                         break;
2589                                 }
2590                         }
2591                 }
2592         }
2593
2594         destroyPQExpBuffer(q);
2595 }
2596
2597
2598 /*
2599  * dumpComment --
2600  *
2601  * This routine is used to dump any comments associated with the
2602  * oid handed to this routine. The routine takes a constant character
2603  * string for the target part of the comment-creation command, plus
2604  * the namespace and owner of the object (for labeling the ArchiveEntry),
2605  * plus OID, class name, and subid which are the lookup key for pg_description.
2606  * If a matching pg_description entry is found, it is dumped.
2607  * Additional dependencies can be passed for the comment, too --- this is
2608  * needed for VIEWs, whose comments are filed under the table OID but
2609  * which are dumped in order by their rule OID.
2610  */
2611
2612 static void
2613 dumpComment(Archive *fout, const char *target,
2614                         const char *namespace, const char *owner,
2615                         const char *oid, const char *classname, int subid,
2616                         const char *((*deps)[]))
2617 {
2618         PGresult   *res;
2619         PQExpBuffer query;
2620         int                     i_description;
2621
2622         /* Comments are SCHEMA not data */
2623         if (dataOnly)
2624                 return;
2625
2626         /*
2627          * Note we do NOT change source schema here; preserve the caller's
2628          * setting, instead.
2629          */
2630
2631         /* Build query to find comment */
2632
2633         query = createPQExpBuffer();
2634
2635         if (fout->remoteVersion >= 70300)
2636         {
2637                 appendPQExpBuffer(query, "SELECT description FROM pg_catalog.pg_description "
2638                                         "WHERE objoid = '%s'::pg_catalog.oid and classoid = "
2639                                                   "'pg_catalog.%s'::pg_catalog.regclass "
2640                                                   "and objsubid = %d",
2641                                                   oid, classname, subid);
2642         }
2643         else if (fout->remoteVersion >= 70200)
2644         {
2645                 appendPQExpBuffer(query, "SELECT description FROM pg_description "
2646                                                   "WHERE objoid = '%s'::oid and classoid = "
2647                                            "(SELECT oid FROM pg_class where relname = '%s') "
2648                                                   "and objsubid = %d",
2649                                                   oid, classname, subid);
2650         }
2651         else
2652         {
2653                 /* Note: this will fail to find attribute comments in pre-7.2... */
2654                 appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%s'::oid", oid);
2655         }
2656
2657         /* Execute query */
2658
2659         res = PQexec(g_conn, query->data);
2660         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
2661         {
2662                 write_msg(NULL, "query to get comment on OID %s failed: %s",
2663                                   oid, PQerrorMessage(g_conn));
2664                 exit_nicely();
2665         }
2666
2667         /* If a comment exists, build COMMENT ON statement */
2668
2669         if (PQntuples(res) == 1)
2670         {
2671                 i_description = PQfnumber(res, "description");
2672                 resetPQExpBuffer(query);
2673                 appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
2674                 appendStringLiteral(query, PQgetvalue(res, 0, i_description), false);
2675                 appendPQExpBuffer(query, ";\n");
2676
2677                 ArchiveEntry(fout, oid, target, namespace, owner,
2678                                          "COMMENT", deps,
2679                                          query->data, "", NULL, NULL, NULL);
2680         }
2681
2682         PQclear(res);
2683         destroyPQExpBuffer(query);
2684 }
2685
2686 /*
2687  * dumpTableComment --
2688  *
2689  * As above, but dump comments for both the specified table (or view)
2690  * and its columns.  For speed, we want to do this with only one query.
2691  */
2692 static void
2693 dumpTableComment(Archive *fout, TableInfo *tbinfo,
2694                                  const char *reltypename,
2695                                  const char *((*deps)[]))
2696 {
2697         PGresult   *res;
2698         PQExpBuffer query;
2699         PQExpBuffer target;
2700         int                     i_description;
2701         int                     i_objsubid;
2702         int                     ntups;
2703         int                     i;
2704
2705         /* Comments are SCHEMA not data */
2706         if (dataOnly)
2707                 return;
2708
2709         /*
2710          * Note we do NOT change source schema here; preserve the caller's
2711          * setting, instead.
2712          */
2713
2714         /* Build query to find comments */
2715
2716         query = createPQExpBuffer();
2717         target = createPQExpBuffer();
2718
2719         if (fout->remoteVersion >= 70300)
2720         {
2721                 appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_catalog.pg_description "
2722                                         "WHERE objoid = '%s'::pg_catalog.oid and classoid = "
2723                                                   "'pg_catalog.pg_class'::pg_catalog.regclass "
2724                                                   "ORDER BY objoid, classoid, objsubid",
2725                                                   tbinfo->oid);
2726         }
2727         else if (fout->remoteVersion >= 70200)
2728         {
2729                 appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_description "
2730                                                   "WHERE objoid = '%s'::oid and classoid = "
2731                                  "(SELECT oid FROM pg_class where relname = 'pg_class') "
2732                                                   "ORDER BY objoid, classoid, objsubid",
2733                                                   tbinfo->oid);
2734         }
2735         else
2736         {
2737                 /* Note: this will fail to find attribute comments in pre-7.2... */
2738                 appendPQExpBuffer(query, "SELECT description, 0 as objsubid FROM pg_description WHERE objoid = '%s'::oid", tbinfo->oid);
2739         }
2740
2741         /* Execute query */
2742
2743         res = PQexec(g_conn, query->data);
2744         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
2745         {
2746                 write_msg(NULL, "query to get comments on table %s failed: %s",
2747                                   tbinfo->relname, PQerrorMessage(g_conn));
2748                 exit_nicely();
2749         }
2750         i_description = PQfnumber(res, "description");
2751         i_objsubid = PQfnumber(res, "objsubid");
2752
2753         /* If comments exist, build COMMENT ON statements */
2754
2755         ntups = PQntuples(res);
2756         for (i = 0; i < ntups; i++)
2757         {
2758                 const char *descr = PQgetvalue(res, i, i_description);
2759                 int                     objsubid = atoi(PQgetvalue(res, i, i_objsubid));
2760
2761                 if (objsubid == 0)
2762                 {
2763                         resetPQExpBuffer(target);
2764                         appendPQExpBuffer(target, "%s %s", reltypename,
2765                                                           fmtId(tbinfo->relname));
2766
2767                         resetPQExpBuffer(query);
2768                         appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
2769                         appendStringLiteral(query, descr, false);
2770                         appendPQExpBuffer(query, ";\n");
2771
2772                         ArchiveEntry(fout, tbinfo->oid, target->data,
2773                                                  tbinfo->relnamespace->nspname, tbinfo->usename,
2774                                                  "COMMENT", deps,
2775                                                  query->data, "", NULL, NULL, NULL);
2776                 }
2777                 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
2778                 {
2779                         resetPQExpBuffer(target);
2780                         appendPQExpBuffer(target, "COLUMN %s.",
2781                                                           fmtId(tbinfo->relname));
2782                         appendPQExpBuffer(target, "%s",
2783                                                           fmtId(tbinfo->attnames[objsubid - 1]));
2784
2785                         resetPQExpBuffer(query);
2786                         appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
2787                         appendStringLiteral(query, descr, false);
2788                         appendPQExpBuffer(query, ";\n");
2789
2790                         ArchiveEntry(fout, tbinfo->oid, target->data,
2791                                                  tbinfo->relnamespace->nspname, tbinfo->usename,
2792                                                  "COMMENT", deps,
2793                                                  query->data, "", NULL, NULL, NULL);
2794                 }
2795         }
2796
2797         PQclear(res);
2798         destroyPQExpBuffer(query);
2799         destroyPQExpBuffer(target);
2800 }
2801
2802 /*
2803  * dumpDBComment --
2804  *
2805  * This routine is used to dump any comments associated with the
2806  * database to which we are currently connected.
2807  */
2808 void
2809 dumpDBComment(Archive *fout)
2810 {
2811         PGresult   *res;
2812         PQExpBuffer query;
2813         int                     i_oid;
2814
2815         /* Make sure we are in proper schema */
2816         selectSourceSchema("pg_catalog");
2817
2818         /* Build query to find comment */
2819
2820         query = createPQExpBuffer();
2821         appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
2822         appendStringLiteral(query, PQdb(g_conn), true);
2823
2824         /* Execute query */
2825
2826         res = PQexec(g_conn, query->data);
2827         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
2828         {
2829                 write_msg(NULL, "query to get database OID failed: %s",
2830                                   PQerrorMessage(g_conn));
2831                 exit_nicely();
2832         }
2833
2834         /* If a comment exists, build COMMENT ON statement */
2835
2836         if (PQntuples(res) != 0)
2837         {
2838                 i_oid = PQfnumber(res, "oid");
2839                 resetPQExpBuffer(query);
2840                 appendPQExpBuffer(query, "DATABASE %s", fmtId(PQdb(g_conn)));
2841                 dumpComment(fout, query->data, NULL, "",
2842                                         PQgetvalue(res, 0, i_oid), "pg_database", 0, NULL);
2843         }
2844
2845         PQclear(res);
2846         destroyPQExpBuffer(query);
2847 }
2848
2849 /*
2850  * dumpNamespaces
2851  *        writes out to fout the queries to recreate user-defined namespaces
2852  */
2853 void
2854 dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
2855 {
2856         PQExpBuffer q = createPQExpBuffer();
2857         PQExpBuffer delq = createPQExpBuffer();
2858         int                     i;
2859         char       *qnspname;
2860
2861         for (i = 0; i < numNamespaces; i++)
2862         {
2863                 NamespaceInfo *nspinfo = &nsinfo[i];
2864
2865                 /* skip if not to be dumped */
2866                 if (!nspinfo->dump)
2867                         continue;
2868
2869                 /* don't dump dummy namespace from pre-7.3 source */
2870                 if (strlen(nspinfo->nspname) == 0)
2871                         continue;
2872
2873                 qnspname = strdup(fmtId(nspinfo->nspname));
2874
2875                 /*
2876                  * If it's the PUBLIC namespace, don't emit a CREATE SCHEMA record
2877                  * for it, since we expect PUBLIC to exist already in the
2878                  * destination database.  But do emit ACL in case it's not standard,
2879                  * likewise comment.
2880                  *
2881                  * Note that ownership is shown in the AUTHORIZATION clause,
2882                  * while the archive entry is listed with empty owner (causing
2883                  * it to be emitted with SET SESSION AUTHORIZATION DEFAULT).
2884                  * This seems the best way of dealing with schemas owned by
2885                  * users without CREATE SCHEMA privilege.  Further hacking has
2886                  * to be applied for --no-owner mode, though!
2887                  */
2888                 if (strcmp(nspinfo->nspname, "public") != 0)
2889                 {
2890                         resetPQExpBuffer(q);
2891                         resetPQExpBuffer(delq);
2892
2893                         appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
2894
2895                         appendPQExpBuffer(q, "CREATE SCHEMA %s AUTHORIZATION %s;\n",
2896                                                           qnspname, fmtId(nspinfo->usename));
2897
2898                         ArchiveEntry(fout, nspinfo->oid, nspinfo->nspname,
2899                                                  NULL, "", "SCHEMA", NULL,
2900                                                  q->data, delq->data, NULL, NULL, NULL);
2901                 }
2902
2903                 /* Dump Schema Comments */
2904                 resetPQExpBuffer(q);
2905                 appendPQExpBuffer(q, "SCHEMA %s", qnspname);
2906                 dumpComment(fout, q->data,
2907                                         NULL, nspinfo->usename,
2908                                         nspinfo->oid, "pg_namespace", 0, NULL);
2909
2910                 dumpACL(fout, "SCHEMA", qnspname, nspinfo->nspname, NULL,
2911                                 nspinfo->usename, nspinfo->nspacl,
2912                                 nspinfo->oid);
2913
2914                 free(qnspname);
2915         }
2916
2917         destroyPQExpBuffer(q);
2918         destroyPQExpBuffer(delq);
2919 }
2920
2921 /*
2922  * dumpOneBaseType
2923  *        writes out to fout the queries to recreate a user-defined base type
2924  *        as requested by dumpTypes
2925  */
2926 static void
2927 dumpOneBaseType(Archive *fout, TypeInfo *tinfo,
2928                                 FuncInfo *g_finfo, int numFuncs,
2929                                 TypeInfo *g_tinfo, int numTypes)
2930 {
2931         PQExpBuffer q = createPQExpBuffer();
2932         PQExpBuffer delq = createPQExpBuffer();
2933         PQExpBuffer query = createPQExpBuffer();
2934         PGresult   *res;
2935         int                     ntups;
2936         int                     funcInd;
2937         char       *typlen;
2938         char       *typinput;
2939         char       *typoutput;
2940         char       *typreceive;
2941         char       *typsend;
2942         char       *typinputoid;
2943         char       *typoutputoid;
2944         char       *typreceiveoid;
2945         char       *typsendoid;
2946         char       *typdelim;
2947         char       *typdefault;
2948         char       *typbyval;
2949         char       *typalign;
2950         char       *typstorage;
2951         const char *((*deps)[]);
2952         int                     depIdx = 0;
2953
2954         deps = malloc(sizeof(char *) * 10);
2955
2956         /* Set proper schema search path so regproc references list correctly */
2957         selectSourceSchema(tinfo->typnamespace->nspname);
2958
2959         /* Fetch type-specific details */
2960         if (fout->remoteVersion >= 70400)
2961         {
2962                 appendPQExpBuffer(query, "SELECT typlen, "
2963                                                   "typinput, typoutput, typreceive, typsend, "
2964                                                   "typinput::pg_catalog.oid as typinputoid, "
2965                                                   "typoutput::pg_catalog.oid as typoutputoid, "
2966                                                   "typreceive::pg_catalog.oid as typreceiveoid, "
2967                                                   "typsend::pg_catalog.oid as typsendoid, "
2968                                                   "typdelim, typdefault, typbyval, typalign, "
2969                                                   "typstorage "
2970                                                   "FROM pg_catalog.pg_type "
2971                                                   "WHERE oid = '%s'::pg_catalog.oid",
2972                                                   tinfo->oid);
2973         }
2974         else if (fout->remoteVersion >= 70300)
2975         {
2976                 appendPQExpBuffer(query, "SELECT typlen, "
2977                                                   "typinput, typoutput, "
2978                                                   "'-' as typreceive, '-' as typsend, "
2979                                                   "typinput::pg_catalog.oid as typinputoid, "
2980                                                   "typoutput::pg_catalog.oid as typoutputoid, "
2981                                                   "0 as typreceiveoid, 0 as typsendoid, "
2982                                                   "typdelim, typdefault, typbyval, typalign, "
2983                                                   "typstorage "
2984                                                   "FROM pg_catalog.pg_type "
2985                                                   "WHERE oid = '%s'::pg_catalog.oid",
2986                                                   tinfo->oid);
2987         }
2988         else if (fout->remoteVersion >= 70100)
2989         {
2990                 /*
2991                  * Note: although pre-7.3 catalogs contain typreceive and typsend,
2992                  * ignore them because they are not right.
2993                  */
2994                 appendPQExpBuffer(query, "SELECT typlen, "
2995                                                   "typinput, typoutput, "
2996                                                   "'-' as typreceive, '-' as typsend, "
2997                                                   "typinput::oid as typinputoid, "
2998                                                   "typoutput::oid as typoutputoid, "
2999                                                   "0 as typreceiveoid, 0 as typsendoid, "
3000                                                   "typdelim, typdefault, typbyval, typalign, "
3001                                                   "typstorage "
3002                                                   "FROM pg_type "
3003                                                   "WHERE oid = '%s'::oid",
3004                                                   tinfo->oid);
3005         }
3006         else
3007         {
3008                 appendPQExpBuffer(query, "SELECT typlen, "
3009                                                   "typinput, typoutput, "
3010                                                   "'-' as typreceive, '-' as typsend, "
3011                                                   "typinput::oid as typinputoid, "
3012                                                   "typoutput::oid as typoutputoid, "
3013                                                   "0 as typreceiveoid, 0 as typsendoid, "
3014                                                   "typdelim, typdefault, typbyval, typalign, "
3015                                                   "'p'::char as typstorage "
3016                                                   "FROM pg_type "
3017                                                   "WHERE oid = '%s'::oid",
3018                                                   tinfo->oid);
3019         }
3020
3021         res = PQexec(g_conn, query->data);
3022         if (!res ||
3023                 PQresultStatus(res) != PGRES_TUPLES_OK)
3024         {
3025                 write_msg(NULL, "query to obtain information on data type \"%s\" failed: %s",
3026                                   tinfo->typname, PQerrorMessage(g_conn));
3027                 exit_nicely();
3028         }
3029
3030         /* Expecting a single result only */
3031         ntups = PQntuples(res);
3032         if (ntups != 1)
3033         {
3034                 write_msg(NULL, "Got %d rows instead of one from: %s",
3035                                   ntups, query->data);
3036                 exit_nicely();
3037         }
3038
3039         typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
3040         typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
3041         typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
3042         typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
3043         typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
3044         typinputoid = PQgetvalue(res, 0, PQfnumber(res, "typinputoid"));
3045         typoutputoid = PQgetvalue(res, 0, PQfnumber(res, "typoutputoid"));
3046         typreceiveoid = PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid"));
3047         typsendoid = PQgetvalue(res, 0, PQfnumber(res, "typsendoid"));
3048         typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
3049         if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
3050                 typdefault = NULL;
3051         else
3052                 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
3053         typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
3054         typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
3055         typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
3056
3057         /*
3058          * Before we create a type, we need to create the input and output
3059          * functions for it, if they haven't been created already.  So make
3060          * sure there are dependency entries for this.  But don't include
3061          * dependencies if the functions aren't going to be dumped.
3062          */
3063         funcInd = findFuncByOid(g_finfo, numFuncs, typinputoid);
3064         if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
3065                 (*deps)[depIdx++] = strdup(typinputoid);
3066
3067         funcInd = findFuncByOid(g_finfo, numFuncs, typoutputoid);
3068         if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
3069                 (*deps)[depIdx++] = strdup(typoutputoid);
3070
3071         if (strcmp(typreceiveoid, "0") != 0)
3072         {
3073                 funcInd = findFuncByOid(g_finfo, numFuncs, typreceiveoid);
3074                 if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
3075                         (*deps)[depIdx++] = strdup(typreceiveoid);
3076         }
3077
3078         if (strcmp(typsendoid, "0") != 0)
3079         {
3080                 funcInd = findFuncByOid(g_finfo, numFuncs, typsendoid);
3081                 if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
3082                         (*deps)[depIdx++] = strdup(typsendoid);
3083         }
3084
3085         /*
3086          * DROP must be fully qualified in case same name appears in
3087          * pg_catalog
3088          */
3089         appendPQExpBuffer(delq, "DROP TYPE %s.",
3090                                           fmtId(tinfo->typnamespace->nspname));
3091         appendPQExpBuffer(delq, "%s CASCADE;\n",
3092                                           fmtId(tinfo->typname));
3093
3094         appendPQExpBuffer(q,
3095                                           "CREATE TYPE %s (\n"
3096                                           "    INTERNALLENGTH = %s",
3097                                           fmtId(tinfo->typname),
3098                                           (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
3099
3100         if (fout->remoteVersion >= 70300)
3101         {
3102                 /* regproc result is correctly quoted in 7.3 */
3103                 appendPQExpBuffer(q, ",\n    INPUT = %s", typinput);
3104                 appendPQExpBuffer(q, ",\n    OUTPUT = %s", typoutput);
3105                 if (strcmp(typreceiveoid, "0") != 0)
3106                         appendPQExpBuffer(q, ",\n    RECEIVE = %s", typreceive);
3107                 if (strcmp(typsendoid, "0") != 0)
3108                         appendPQExpBuffer(q, ",\n    SEND = %s", typsend);
3109         }
3110         else
3111         {
3112                 /* regproc delivers an unquoted name before 7.3 */
3113                 /* cannot combine these because fmtId uses static result area */
3114                 appendPQExpBuffer(q, ",\n    INPUT = %s", fmtId(typinput));
3115                 appendPQExpBuffer(q, ",\n    OUTPUT = %s", fmtId(typoutput));
3116                 /* no chance that receive/send need be printed */
3117         }
3118
3119         if (typdefault != NULL)
3120         {
3121                 appendPQExpBuffer(q, ",\n    DEFAULT = ");
3122                 appendStringLiteral(q, typdefault, true);
3123         }
3124
3125         if (tinfo->isArray)
3126         {
3127                 char       *elemType;
3128
3129                 /* reselect schema in case changed by function dump */
3130                 selectSourceSchema(tinfo->typnamespace->nspname);
3131                 elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque);
3132                 appendPQExpBuffer(q, ",\n    ELEMENT = %s", elemType);
3133                 free(elemType);
3134
3135                 (*deps)[depIdx++] = strdup(tinfo->typelem);
3136         }
3137
3138         if (typdelim && strcmp(typdelim, ",") != 0)
3139         {
3140                 appendPQExpBuffer(q, ",\n    DELIMITER = ");
3141                 appendStringLiteral(q, typdelim, true);
3142         }
3143
3144         if (strcmp(typalign, "c") == 0)
3145                 appendPQExpBuffer(q, ",\n    ALIGNMENT = char");
3146         else if (strcmp(typalign, "s") == 0)
3147                 appendPQExpBuffer(q, ",\n    ALIGNMENT = int2");
3148         else if (strcmp(typalign, "i") == 0)
3149                 appendPQExpBuffer(q, ",\n    ALIGNMENT = int4");
3150         else if (strcmp(typalign, "d") == 0)
3151                 appendPQExpBuffer(q, ",\n    ALIGNMENT = double");
3152
3153         if (strcmp(typstorage, "p") == 0)
3154                 appendPQExpBuffer(q, ",\n    STORAGE = plain");
3155         else if (strcmp(typstorage, "e") == 0)
3156                 appendPQExpBuffer(q, ",\n    STORAGE = external");
3157         else if (strcmp(typstorage, "x") == 0)
3158                 appendPQExpBuffer(q, ",\n    STORAGE = extended");
3159         else if (strcmp(typstorage, "m") == 0)
3160                 appendPQExpBuffer(q, ",\n    STORAGE = main");
3161
3162         if (strcmp(typbyval, "t") == 0)
3163                 appendPQExpBuffer(q, ",\n    PASSEDBYVALUE");
3164
3165         appendPQExpBuffer(q, "\n);\n");
3166
3167         (*deps)[depIdx++] = NULL;       /* End of List */
3168
3169         ArchiveEntry(fout, tinfo->oid, tinfo->typname,
3170                                  tinfo->typnamespace->nspname,
3171                                  tinfo->usename, "TYPE", deps,
3172                                  q->data, delq->data, NULL, NULL, NULL);
3173
3174         /* Dump Type Comments */
3175         resetPQExpBuffer(q);
3176
3177         appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname));
3178         dumpComment(fout, q->data,
3179                                 tinfo->typnamespace->nspname, tinfo->usename,
3180                                 tinfo->oid, "pg_type", 0, NULL);
3181
3182         PQclear(res);
3183         destroyPQExpBuffer(q);
3184         destroyPQExpBuffer(delq);
3185         destroyPQExpBuffer(query);
3186 }
3187
3188 /*
3189  * dumpOneDomain
3190  *        writes out to fout the queries to recreate a user-defined domain
3191  *        as requested by dumpTypes
3192  */
3193 static void
3194 dumpOneDomain(Archive *fout, TypeInfo *tinfo)
3195 {
3196         PQExpBuffer q = createPQExpBuffer();
3197         PQExpBuffer delq = createPQExpBuffer();
3198         PQExpBuffer query = createPQExpBuffer();
3199         PQExpBuffer chkquery = createPQExpBuffer();
3200         PGresult   *res;
3201         int                     ntups;
3202         int                     i;
3203         char       *typnotnull;
3204         char       *typdefn;
3205         char       *typdefault;
3206         char       *typbasetype;
3207         const char *((*deps)[]);
3208         int                     depIdx = 0;
3209
3210         deps = malloc(sizeof(char *) * 10);
3211
3212         /* Set proper schema search path so type references list correctly */
3213         selectSourceSchema(tinfo->typnamespace->nspname);
3214
3215         /* Fetch domain specific details */
3216         /* We assume here that remoteVersion must be at least 70300 */
3217         appendPQExpBuffer(query, "SELECT typnotnull, "
3218                         "pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
3219                                           "typdefault, typbasetype "
3220                                           "FROM pg_catalog.pg_type "
3221                                           "WHERE oid = '%s'::pg_catalog.oid",
3222                                           tinfo->oid);
3223
3224         res = PQexec(g_conn, query->data);
3225         if (!res ||
3226                 PQresultStatus(res) != PGRES_TUPLES_OK)
3227         {
3228                 write_msg(NULL, "query to obtain domain information failed: %s", PQerrorMessage(g_conn));
3229                 exit_nicely();
3230         }
3231
3232         /* Expecting a single result only */
3233         ntups = PQntuples(res);
3234         if (ntups != 1)
3235         {
3236                 write_msg(NULL, "Got %d rows instead of one from: %s",
3237                                   ntups, query->data);
3238                 exit_nicely();
3239         }
3240
3241         typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
3242         typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
3243         if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
3244                 typdefault = NULL;
3245         else
3246                 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
3247         typbasetype = PQgetvalue(res, 0, PQfnumber(res, "typbasetype"));
3248
3249         /*
3250          * DROP must be fully qualified in case same name appears in
3251          * pg_catalog
3252          */
3253         appendPQExpBuffer(delq, "DROP DOMAIN %s.",
3254                                           fmtId(tinfo->typnamespace->nspname));
3255         appendPQExpBuffer(delq, "%s;\n",
3256                                           fmtId(tinfo->typname));
3257
3258         appendPQExpBuffer(q,
3259                                           "CREATE DOMAIN %s AS %s",
3260                                           fmtId(tinfo->typname),
3261                                           typdefn);
3262
3263         /* Depends on the base type */
3264         (*deps)[depIdx++] = strdup(typbasetype);
3265
3266         if (typnotnull[0] == 't')
3267                 appendPQExpBuffer(q, " NOT NULL");
3268
3269         if (typdefault)
3270                 appendPQExpBuffer(q, " DEFAULT %s", typdefault);
3271
3272         PQclear(res);
3273
3274         /*
3275          * Fetch and process CHECK constraints for the domain
3276          */
3277         if (g_fout->remoteVersion >= 70400)
3278                 appendPQExpBuffer(chkquery, "SELECT conname, "
3279                                                 "pg_catalog.pg_get_constraintdef(oid) AS consrc "
3280                                                   "FROM pg_catalog.pg_constraint "
3281                                                   "WHERE contypid = '%s'::pg_catalog.oid",
3282                                                   tinfo->oid);
3283         else
3284                 appendPQExpBuffer(chkquery, "SELECT conname, 'CHECK (' || consrc || ')' AS consrc "
3285                                                   "FROM pg_catalog.pg_constraint "
3286                                                   "WHERE contypid = '%s'::pg_catalog.oid",
3287                                                   tinfo->oid);
3288
3289         res = PQexec(g_conn, chkquery->data);
3290         if (!res ||
3291                 PQresultStatus(res) != PGRES_TUPLES_OK)
3292         {
3293                 write_msg(NULL, "query to obtain domain constraint information failed: %s",
3294                                   PQerrorMessage(g_conn));
3295                 exit_nicely();
3296         }
3297
3298         ntups = PQntuples(res);
3299         for (i = 0; i < ntups; i++)
3300         {
3301                 char       *conname;
3302                 char       *consrc;
3303
3304                 conname = PQgetvalue(res, i, PQfnumber(res, "conname"));
3305                 consrc = PQgetvalue(res, i, PQfnumber(res, "consrc"));
3306
3307                 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
3308                                                   fmtId(conname), consrc);
3309         }
3310
3311         appendPQExpBuffer(q, ";\n");
3312
3313         (*deps)[depIdx++] = NULL;       /* End of List */
3314
3315         ArchiveEntry(fout, tinfo->oid, tinfo->typname,
3316                                  tinfo->typnamespace->nspname,
3317                                  tinfo->usename, "DOMAIN", deps,
3318                                  q->data, delq->data, NULL, NULL, NULL);
3319
3320         /* Dump Domain Comments */
3321         resetPQExpBuffer(q);
3322
3323         appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->typname));
3324         dumpComment(fout, q->data,
3325                                 tinfo->typnamespace->nspname, tinfo->usename,
3326                                 tinfo->oid, "pg_type", 0, NULL);
3327
3328         PQclear(res);
3329         destroyPQExpBuffer(q);
3330         destroyPQExpBuffer(delq);
3331         destroyPQExpBuffer(query);
3332         destroyPQExpBuffer(chkquery);
3333 }
3334
3335 /*
3336  * dumpOneCompositeType
3337  *        writes out to fout the queries to recreate a user-defined stand-alone
3338  *        composite type as requested by dumpTypes
3339  */
3340 static void
3341 dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
3342 {
3343         PQExpBuffer q = createPQExpBuffer();
3344         PQExpBuffer delq = createPQExpBuffer();
3345         PQExpBuffer query = createPQExpBuffer();
3346         PGresult   *res;
3347         int                     ntups;
3348         int                     i_attname;
3349         int                     i_atttypdefn;
3350         int                     i;
3351
3352         /* Set proper schema search path so type references list correctly */
3353         selectSourceSchema(tinfo->typnamespace->nspname);
3354
3355         /* Fetch type specific details */
3356         /* We assume here that remoteVersion must be at least 70300 */
3357
3358         appendPQExpBuffer(query, "SELECT a.attname, "
3359                  "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
3360                                   "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
3361                                           "WHERE t.oid = '%s'::pg_catalog.oid "
3362                                           "AND a.attrelid = t.typrelid "
3363                                           "AND NOT a.attisdropped "
3364                                           "ORDER BY a.attnum ",
3365                                           tinfo->oid);
3366
3367         res = PQexec(g_conn, query->data);
3368         if (!res ||
3369                 PQresultStatus(res) != PGRES_TUPLES_OK)
3370         {
3371                 write_msg(NULL, "query to obtain data type information failed: %s", PQerrorMessage(g_conn));
3372                 exit_nicely();
3373         }
3374
3375         /* Expecting at least a single result */
3376         ntups = PQntuples(res);
3377         if (ntups < 1)
3378         {
3379                 write_msg(NULL, "query yielded no rows: %s\n", query->data);
3380                 exit_nicely();
3381         }
3382
3383         i_attname = PQfnumber(res, "attname");
3384         i_atttypdefn = PQfnumber(res, "atttypdefn");
3385
3386         appendPQExpBuffer(q, "CREATE TYPE %s AS (",
3387                                           fmtId(tinfo->typname));
3388
3389         for (i = 0; i < ntups; i++)
3390         {
3391                 char       *attname;
3392                 char       *atttypdefn;
3393
3394                 attname = PQgetvalue(res, i, i_attname);
3395                 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
3396
3397                 appendPQExpBuffer(q, "\n\t%s %s", fmtId(attname), atttypdefn);
3398                 if (i < ntups - 1)
3399                         appendPQExpBuffer(q, ",");
3400         }
3401         appendPQExpBuffer(q, "\n);\n");
3402
3403         /*
3404          * DROP must be fully qualified in case same name appears in
3405          * pg_catalog
3406          */
3407         appendPQExpBuffer(delq, "DROP TYPE %s.",
3408                                           fmtId(tinfo->typnamespace->nspname));
3409         appendPQExpBuffer(delq, "%s;\n",
3410                                           fmtId(tinfo->typname));
3411
3412         ArchiveEntry(fout, tinfo->oid, tinfo->typname,
3413                                  tinfo->typnamespace->nspname,
3414                                  tinfo->usename, "TYPE", NULL,
3415                                  q->data, delq->data, NULL, NULL, NULL);
3416
3417         /* Dump Type Comments */
3418         resetPQExpBuffer(q);
3419
3420         appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname));
3421         dumpComment(fout, q->data,
3422                                 tinfo->typnamespace->nspname, tinfo->usename,
3423                                 tinfo->oid, "pg_type", 0, NULL);
3424
3425         PQclear(res);
3426         destroyPQExpBuffer(q);
3427         destroyPQExpBuffer(delq);
3428         destroyPQExpBuffer(query);
3429 }
3430
3431 /*
3432  * dumpTypes
3433  *        writes out to fout the queries to recreate all the user-defined types
3434  */
3435 void
3436 dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
3437                   TypeInfo *tinfo, int numTypes)
3438 {
3439         int                     i;
3440
3441         for (i = 0; i < numTypes; i++)
3442         {
3443                 /* Dump only types in dumpable namespaces */
3444                 if (!tinfo[i].typnamespace->dump)
3445                         continue;
3446
3447                 /* skip complex types, except for standalone composite types */
3448                 if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
3449                         continue;
3450
3451                 /* skip undefined placeholder types */
3452                 if (!tinfo[i].isDefined)
3453                         continue;
3454
3455                 /* skip all array types that start w/ underscore */
3456                 if ((tinfo[i].typname[0] == '_') &&
3457                         atooid(tinfo[i].typelem) != 0)
3458                         continue;
3459
3460                 /* Dump out in proper style */
3461                 if (tinfo[i].typtype == 'b')
3462                         dumpOneBaseType(fout, &tinfo[i],
3463                                                         finfo, numFuncs, tinfo, numTypes);
3464                 else if (tinfo[i].typtype == 'd')
3465                         dumpOneDomain(fout, &tinfo[i]);
3466                 else if (tinfo[i].typtype == 'c')
3467                         dumpOneCompositeType(fout, &tinfo[i]);
3468         }
3469 }
3470
3471 /*
3472  * dumpProcLangs
3473  *                writes out to fout the queries to recreate user-defined procedural languages
3474  */
3475 void
3476 dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
3477 {
3478         PGresult   *res;
3479         PQExpBuffer query = createPQExpBuffer();
3480         PQExpBuffer defqry = createPQExpBuffer();
3481         PQExpBuffer delqry = createPQExpBuffer();
3482         int                     ntups;
3483         int                     i_oid;
3484         int                     i_lanname;
3485         int                     i_lanpltrusted;
3486         int                     i_lanplcallfoid;
3487         int                     i_lanvalidator = -1;
3488         int                     i_lanacl = -1;
3489         char       *lanoid;
3490         char       *lanname;
3491         char       *lanacl;
3492         const char *lanplcallfoid;
3493         const char *lanvalidator;
3494         const char *((*deps)[]);
3495         int                     depIdx;
3496         int                     i,
3497                                 fidx,
3498                                 vidx = -1;
3499
3500         /* Make sure we are in proper schema */
3501         selectSourceSchema("pg_catalog");
3502
3503         appendPQExpBuffer(query, "SELECT oid, * FROM pg_language "
3504                                           "WHERE lanispl "
3505                                           "ORDER BY oid");
3506         res = PQexec(g_conn, query->data);
3507         if (!res ||
3508                 PQresultStatus(res) != PGRES_TUPLES_OK)
3509         {
3510                 write_msg(NULL, "query to obtain list of procedural languages failed: %s",
3511                                   PQerrorMessage(g_conn));
3512                 exit_nicely();
3513         }
3514         ntups = PQntuples(res);
3515
3516         i_lanname = PQfnumber(res, "lanname");
3517         i_lanpltrusted = PQfnumber(res, "lanpltrusted");
3518         i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
3519         i_oid = PQfnumber(res, "oid");
3520         if (fout->remoteVersion >= 70300)
3521         {
3522                 i_lanvalidator = PQfnumber(res, "lanvalidator");
3523                 i_lanacl = PQfnumber(res, "lanacl");
3524         }
3525
3526         for (i = 0; i < ntups; i++)
3527         {
3528                 lanoid = PQgetvalue(res, i, i_oid);
3529                 lanplcallfoid = PQgetvalue(res, i, i_lanplcallfoid);
3530                 lanname = PQgetvalue(res, i, i_lanname);
3531                 if (fout->remoteVersion >= 70300)
3532                 {
3533                         lanvalidator = PQgetvalue(res, i, i_lanvalidator);
3534                         lanacl = PQgetvalue(res, i, i_lanacl);
3535                 }
3536                 else
3537                 {
3538                         lanvalidator = "0";
3539                         lanacl = "{=U}";
3540                 }
3541
3542                 /*
3543                  * Current theory is to dump PLs iff their underlying functions
3544                  * will be dumped (are in a dumpable namespace, or have a
3545                  * non-system OID in pre-7.3 databases).  Actually, we treat the
3546                  * PL itself as being in the underlying function's namespace,
3547                  * though it isn't really.  This avoids searchpath problems for
3548                  * the HANDLER clause.
3549                  *
3550                  * If the underlying function is in the pg_catalog namespace,
3551                  * we won't have loaded it into finfo[] at all; therefore,
3552                  * treat failure to find it in finfo[] as indicating we shouldn't
3553                  * dump it, not as an error condition.  Ditto for the validator.
3554                  */
3555
3556                 fidx = findFuncByOid(finfo, numFuncs, lanplcallfoid);
3557                 if (fidx < 0)
3558                         continue;
3559
3560                 if (!finfo[fidx].pronamespace->dump)
3561                         continue;
3562
3563                 if (strcmp(lanvalidator, "0") != 0)
3564                 {
3565                         vidx = findFuncByOid(finfo, numFuncs, lanvalidator);
3566                         if (vidx < 0)
3567                                 continue;
3568                 }
3569
3570                 resetPQExpBuffer(defqry);
3571                 resetPQExpBuffer(delqry);
3572
3573                 /* Make a dependency to ensure function is dumped first */
3574                 deps = malloc(sizeof(char *) * 10);
3575                 depIdx = 0;
3576
3577                 (*deps)[depIdx++] = strdup(lanplcallfoid);
3578
3579                 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
3580                                                   fmtId(lanname));
3581
3582                 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
3583                                                   (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
3584                                                   "TRUSTED " : "",
3585                                                   fmtId(lanname));
3586                 appendPQExpBuffer(defqry, " HANDLER %s",
3587                                                   fmtId(finfo[fidx].proname));
3588                 if (strcmp(lanvalidator, "0") != 0)
3589                 {
3590                         appendPQExpBuffer(defqry, " VALIDATOR ");
3591                         /* Cope with possibility that validator is in different schema */
3592                         if (finfo[vidx].pronamespace != finfo[fidx].pronamespace)
3593                                 appendPQExpBuffer(defqry, "%s.",
3594                                                            fmtId(finfo[vidx].pronamespace->nspname));
3595                         appendPQExpBuffer(defqry, "%s",
3596                                                           fmtId(finfo[vidx].proname));
3597                         (*deps)[depIdx++] = strdup(lanvalidator);
3598                 }
3599                 appendPQExpBuffer(defqry, ";\n");
3600
3601                 (*deps)[depIdx++] = NULL;               /* End of List */
3602
3603                 ArchiveEntry(fout, lanoid, lanname,
3604                                          finfo[fidx].pronamespace->nspname, "",
3605                                          "PROCEDURAL LANGUAGE", deps,
3606                                          defqry->data, delqry->data, NULL, NULL, NULL);
3607
3608                 if (!aclsSkip)
3609                 {
3610                         char       *tmp = strdup(fmtId(lanname));
3611
3612                         dumpACL(fout, "ACL LANGUAGE", tmp, lanname,
3613                                         finfo[fidx].pronamespace->nspname,
3614                                         NULL, lanacl, lanoid);
3615                         free(tmp);
3616                 }
3617         }
3618
3619         PQclear(res);
3620
3621         destroyPQExpBuffer(query);
3622         destroyPQExpBuffer(defqry);
3623         destroyPQExpBuffer(delqry);
3624 }
3625
3626 /*
3627  * dumpFuncs
3628  *        writes out to fout the queries to recreate all the user-defined functions
3629  */
3630 void
3631 dumpFuncs(Archive *fout, FuncInfo finfo[], int numFuncs)
3632 {
3633         int                     i;
3634
3635         for (i = 0; i < numFuncs; i++)
3636         {
3637                 /* Dump only funcs in dumpable namespaces */
3638                 if (!finfo[i].pronamespace->dump)
3639                         continue;
3640
3641                 dumpOneFunc(fout, &finfo[i]);
3642                 if (!aclsSkip)
3643                         dumpFuncACL(fout, &finfo[i]);
3644         }
3645 }
3646
3647 /*
3648  * format_function_signature: generate function name and argument list
3649  *
3650  * The argument type names are qualified if needed.  The function name
3651  * is never qualified.
3652  */
3653 static char *
3654 format_function_signature(FuncInfo *finfo, bool honor_quotes)
3655 {
3656         PQExpBufferData fn;
3657         int                     j;
3658
3659         initPQExpBuffer(&fn);
3660         if (honor_quotes)
3661                 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->proname));
3662         else
3663                 appendPQExpBuffer(&fn, "%s(", finfo->proname);
3664         for (j = 0; j < finfo->nargs; j++)
3665         {
3666                 char       *typname;
3667
3668                 typname = getFormattedTypeName(finfo->argtypes[j], zeroAsOpaque);
3669                 appendPQExpBuffer(&fn, "%s%s",
3670                                                   (j > 0) ? ", " : "",
3671                                                   typname);
3672                 free(typname);
3673         }
3674         appendPQExpBuffer(&fn, ")");
3675         return fn.data;
3676 }
3677
3678
3679 static void
3680 dumpFuncACL(Archive *fout, FuncInfo *finfo)
3681 {
3682         char       *funcsig,
3683                            *funcsig_tag;
3684
3685         funcsig = format_function_signature(finfo, true);
3686         funcsig_tag = format_function_signature(finfo, false);
3687         dumpACL(fout, "FUNCTION", funcsig, funcsig_tag,
3688                         finfo->pronamespace->nspname,
3689                         finfo->usename, finfo->proacl, finfo->oid);
3690         free(funcsig);
3691         free(funcsig_tag);
3692 }
3693
3694
3695 /*
3696  * dumpOneFunc:
3697  *        dump out only one function
3698  */
3699 static void
3700 dumpOneFunc(Archive *fout, FuncInfo *finfo)
3701 {
3702         PQExpBuffer query = createPQExpBuffer();
3703         PQExpBuffer q = createPQExpBuffer();
3704         PQExpBuffer delqry = createPQExpBuffer();
3705         PQExpBuffer asPart = createPQExpBuffer();
3706         PGresult   *res = NULL;
3707         char       *funcsig = NULL;
3708         char       *funcsig_tag = NULL;
3709         int                     ntups;
3710         char       *proretset;
3711         char       *prosrc;
3712         char       *probin;
3713         char       *provolatile;
3714         char       *proisstrict;
3715         char       *prosecdef;
3716         char       *lanname;
3717         char       *rettypename;
3718         char       *funcproclang;       /* Boolean : is this function a PLang
3719                                                                  * handler ? */
3720
3721         if (finfo->dumped)
3722                 goto done;
3723
3724         finfo->dumped = true;
3725
3726         /* Set proper schema search path so type references list correctly */
3727         selectSourceSchema(finfo->pronamespace->nspname);
3728
3729         /* Fetch function-specific details */
3730         if (g_fout->remoteVersion >= 70300)
3731         {
3732                 appendPQExpBuffer(query,
3733                                                   "SELECT proretset, prosrc, probin, "
3734                                                   "provolatile, proisstrict, prosecdef, "
3735                                                   "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname, "
3736                                                   "exists (SELECT 'x' FROM pg_catalog.pg_language WHERE lanplcallfoid = pg_catalog.pg_proc.oid) as funcproclang "
3737                                                   "FROM pg_catalog.pg_proc "
3738                                                   "WHERE oid = '%s'::pg_catalog.oid",
3739                                                   finfo->oid);
3740         }
3741         else if (g_fout->remoteVersion >= 70100)
3742         {
3743                 appendPQExpBuffer(query,
3744                                                   "SELECT proretset, prosrc, probin, "
3745                  "case when proiscachable then 'i' else 'v' end as provolatile, "
3746                                                   "proisstrict, "
3747                                                   "'f'::boolean as prosecdef, "
3748                                                   "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname, "
3749                                                   "exists (SELECT 'x' FROM pg_language WHERE lanplcallfoid = pg_proc.oid) as funcproclang "
3750                                                   "FROM pg_proc "
3751                                                   "WHERE oid = '%s'::oid",
3752                                                   finfo->oid);
3753         }
3754         else
3755         {
3756                 appendPQExpBuffer(query,
3757                                                   "SELECT proretset, prosrc, probin, "
3758                  "case when proiscachable then 'i' else 'v' end as provolatile, "
3759                                                   "'f'::boolean as proisstrict, "
3760                                                   "'f'::boolean as prosecdef, "
3761                                                   "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname, "
3762                                                   "exists (SELECT 'x' FROM pg_language WHERE lanplcallfoid = pg_proc.oid) as funcproclang "
3763                                                   "FROM pg_proc "
3764                                                   "WHERE oid = '%s'::oid",
3765                                                   finfo->oid);
3766         }
3767
3768         res = PQexec(g_conn, query->data);
3769         if (!res ||
3770                 PQresultStatus(res) != PGRES_TUPLES_OK)
3771         {
3772                 write_msg(NULL, "query to obtain information on function \"%s\" failed: %s",
3773                                   finfo->proname, PQerrorMessage(g_conn));
3774                 exit_nicely();
3775         }
3776
3777         /* Expecting a single result only */
3778         ntups = PQntuples(res);
3779         if (ntups != 1)
3780         {
3781                 write_msg(NULL, "Got %d rows instead of one from: %s",
3782                                   ntups, query->data);
3783                 exit_nicely();
3784         }
3785
3786         proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
3787         prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
3788         probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
3789         provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
3790         proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
3791         prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
3792         lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
3793         funcproclang = PQgetvalue(res, 0, PQfnumber(res, "funcproclang"));
3794
3795         /*
3796          * See backend/commands/define.c for details of how the 'AS' clause is
3797          * used.
3798          */
3799         if (strcmp(probin, "-") != 0)
3800         {
3801                 appendPQExpBuffer(asPart, "AS ");
3802                 appendStringLiteral(asPart, probin, true);
3803                 if (strcmp(prosrc, "-") != 0)
3804                 {
3805                         appendPQExpBuffer(asPart, ", ");
3806                         appendStringLiteral(asPart, prosrc, false);
3807                 }
3808         }
3809         else
3810         {
3811                 if (strcmp(prosrc, "-") != 0)
3812                 {
3813                         appendPQExpBuffer(asPart, "AS ");
3814                         appendStringLiteral(asPart, prosrc, false);
3815                 }
3816         }
3817
3818         funcsig = format_function_signature(finfo, true);
3819         funcsig_tag = format_function_signature(finfo, false);
3820
3821         /*
3822          * DROP must be fully qualified in case same name appears in
3823          * pg_catalog
3824          */
3825         appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
3826                                           fmtId(finfo->pronamespace->nspname),
3827                                           funcsig);
3828
3829         rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
3830
3831         appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig);
3832         appendPQExpBuffer(q, "RETURNS %s%s\n    %s\n    LANGUAGE %s",
3833                                           (proretset[0] == 't') ? "SETOF " : "",
3834                                           rettypename,
3835                                           asPart->data,
3836                                           fmtId(lanname));
3837
3838         free(rettypename);
3839
3840         if (provolatile[0] != PROVOLATILE_VOLATILE)
3841         {
3842                 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
3843                         appendPQExpBuffer(q, " IMMUTABLE");
3844                 else if (provolatile[0] == PROVOLATILE_STABLE)
3845                         appendPQExpBuffer(q, " STABLE");
3846                 else if (provolatile[0] != PROVOLATILE_VOLATILE)
3847                 {
3848                         write_msg(NULL, "unrecognized provolatile value for function \"%s\"\n",
3849                                           finfo->proname);
3850                         exit_nicely();
3851                 }
3852         }
3853
3854         if (proisstrict[0] == 't')
3855                 appendPQExpBuffer(q, " STRICT");
3856
3857         if (prosecdef[0] == 't')
3858                 appendPQExpBuffer(q, " SECURITY DEFINER");
3859
3860         appendPQExpBuffer(q, ";\n");
3861
3862         ArchiveEntry(fout, finfo->oid, funcsig_tag,
3863                                  finfo->pronamespace->nspname,
3864                                  finfo->usename, strcmp(funcproclang, "t") ? "FUNCTION" : "FUNC PROCEDURAL LANGUAGE", NULL,
3865                                  q->data, delqry->data,
3866                                  NULL, NULL, NULL);
3867
3868         /* Dump Function Comments */
3869
3870         resetPQExpBuffer(q);
3871         appendPQExpBuffer(q, "FUNCTION %s", funcsig);
3872         dumpComment(fout, q->data,
3873                                 finfo->pronamespace->nspname, finfo->usename,
3874                                 finfo->oid, "pg_proc", 0, NULL);
3875
3876 done:
3877         PQclear(res);
3878
3879         destroyPQExpBuffer(query);
3880         destroyPQExpBuffer(q);
3881         destroyPQExpBuffer(delqry);
3882         destroyPQExpBuffer(asPart);
3883         free(funcsig);
3884         free(funcsig_tag);
3885 }
3886
3887
3888 /*
3889  * Dump all casts
3890  */
3891 void
3892 dumpCasts(Archive *fout,
3893                   FuncInfo *finfo, int numFuncs,
3894                   TypeInfo *tinfo, int numTypes)
3895 {
3896         PGresult   *res;
3897         PQExpBuffer query = createPQExpBuffer();
3898         PQExpBuffer defqry = createPQExpBuffer();
3899         PQExpBuffer delqry = createPQExpBuffer();
3900         PQExpBuffer castsig = createPQExpBuffer();
3901         int                     ntups;
3902         int                     i;
3903
3904         /* Make sure we are in proper schema */
3905         selectSourceSchema("pg_catalog");
3906
3907         if (fout->remoteVersion >= 70300)
3908                 appendPQExpBuffer(query, "SELECT oid, castsource, casttarget, castfunc, castcontext FROM pg_cast ORDER BY 1,2,3;");
3909         else
3910                 appendPQExpBuffer(query, "SELECT p.oid, t1.oid, t2.oid, p.oid, true FROM pg_type t1, pg_type t2, pg_proc p WHERE p.pronargs = 1 AND p.proargtypes[0] = t1.oid AND p.prorettype = t2.oid AND p.proname = t2.typname ORDER BY 1,2,3;");
3911
3912         res = PQexec(g_conn, query->data);
3913         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
3914         {
3915                 write_msg(NULL, "query to obtain list of casts failed: %s",
3916                                   PQerrorMessage(g_conn));
3917                 exit_nicely();
3918         }
3919         ntups = PQntuples(res);
3920
3921         for (i = 0; i < ntups; i++)
3922         {
3923                 char       *castoid = PQgetvalue(res, i, 0);
3924                 char       *castsource = PQgetvalue(res, i, 1);
3925                 char       *casttarget = PQgetvalue(res, i, 2);
3926                 char       *castfunc = PQgetvalue(res, i, 3);
3927                 char       *castcontext = PQgetvalue(res, i, 4);
3928                 int                     fidx = -1;
3929                 const char *((*deps)[]);
3930                 int                     source_idx;
3931                 int                     target_idx;
3932
3933                 if (strcmp(castfunc, "0") != 0)
3934                         fidx = findFuncByOid(finfo, numFuncs, castfunc);
3935
3936                 /*
3937                  * As per discussion we dump casts if one or more of the underlying
3938                  * objects (the conversion function and the two data types) are not
3939                  * builtin AND if all of the non-builtin objects namespaces are
3940                  * included in the dump. Builtin meaning, the namespace name does
3941                  * not start with "pg_".
3942                  */
3943                 source_idx = findTypeByOid(tinfo, numTypes, castsource);
3944                 target_idx = findTypeByOid(tinfo, numTypes, casttarget);
3945
3946                 /*
3947                  * Skip this cast if all objects are from pg_
3948                  */
3949                 if ((fidx < 0 || strncmp(finfo[fidx].pronamespace->nspname, "pg_", 3) == 0) &&
3950                                 strncmp(tinfo[source_idx].typnamespace->nspname, "pg_", 3) == 0 &&
3951                                 strncmp(tinfo[target_idx].typnamespace->nspname, "pg_", 3) == 0)
3952                         continue;
3953
3954                 /*
3955                  * Skip cast if function isn't from pg_ and that namespace is
3956                  * not dumped.
3957                  */
3958                 if (fidx >= 0 && 
3959                                 strncmp(finfo[fidx].pronamespace->nspname, "pg_", 3) != 0 &&
3960                                 !finfo[fidx].pronamespace->dump)
3961                         continue;
3962
3963                 /*
3964                  * Same for the Source type
3965                  */
3966                 if (strncmp(tinfo[source_idx].typnamespace->nspname, "pg_", 3) != 0 &&
3967                                 !tinfo[source_idx].typnamespace->dump)
3968                         continue;
3969
3970                 /*
3971                  * and the target type.
3972                  */
3973                 if (strncmp(tinfo[target_idx].typnamespace->nspname, "pg_", 3) != 0 &&
3974                                 !tinfo[target_idx].typnamespace->dump)
3975                         continue;
3976
3977                 /* Make a dependency to ensure function is dumped first */
3978                 if (fidx >= 0)
3979                 {
3980                         deps = malloc(sizeof(char *) * 2);
3981
3982                         (*deps)[0] = strdup(castfunc);
3983                         (*deps)[1] = NULL;      /* End of List */
3984                 }
3985                 else
3986                         deps = NULL;
3987
3988                 resetPQExpBuffer(defqry);
3989                 resetPQExpBuffer(delqry);
3990                 resetPQExpBuffer(castsig);
3991
3992                 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
3993                                                   getFormattedTypeName(castsource, zeroAsNone),
3994                                                   getFormattedTypeName(casttarget, zeroAsNone));
3995
3996                 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
3997                                                   getFormattedTypeName(castsource, zeroAsNone),
3998                                                   getFormattedTypeName(casttarget, zeroAsNone));
3999
4000                 if (strcmp(castfunc, "0") == 0)
4001                         appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
4002                 else
4003                         appendPQExpBuffer(defqry, "WITH FUNCTION %s",
4004                                                   format_function_signature(&finfo[fidx], true));
4005
4006                 if (strcmp(castcontext, "a") == 0)
4007                         appendPQExpBuffer(defqry, " AS ASSIGNMENT");
4008                 else if (strcmp(castcontext, "i") == 0)
4009                         appendPQExpBuffer(defqry, " AS IMPLICIT");
4010                 appendPQExpBuffer(defqry, ";\n");
4011
4012                 appendPQExpBuffer(castsig, "CAST (%s AS %s)",
4013                                                   getFormattedTypeName(castsource, zeroAsNone),
4014                                                   getFormattedTypeName(casttarget, zeroAsNone));
4015
4016                 ArchiveEntry(fout, castoid,
4017                                          castsig->data,
4018                                          tinfo[source_idx].typnamespace->nspname, "",
4019                                          "CAST", deps,
4020                                          defqry->data, delqry->data,
4021                                          NULL, NULL, NULL);
4022         }
4023
4024         PQclear(res);
4025
4026         destroyPQExpBuffer(query);
4027         destroyPQExpBuffer(defqry);
4028         destroyPQExpBuffer(delqry);
4029         destroyPQExpBuffer(castsig);
4030 }
4031
4032
4033 /*
4034  * dumpOprs
4035  *        writes out to fout the queries to recreate all the user-defined operators
4036  */
4037 void
4038 dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators)
4039 {
4040         int                     i;
4041
4042         for (i = 0; i < numOperators; i++)
4043         {
4044                 /* Dump only operators in dumpable namespaces */
4045                 if (!oprinfo[i].oprnamespace->dump)
4046                         continue;
4047
4048                 /*
4049                  * some operators are invalid because they were the result of user
4050                  * defining operators before commutators exist
4051                  */
4052                 if (strcmp(oprinfo[i].oprcode, "0") == 0)
4053                         continue;
4054
4055                 /* OK, dump it */
4056                 dumpOneOpr(fout, &oprinfo[i],
4057                                    oprinfo, numOperators);
4058         }
4059 }
4060
4061 /*
4062  * dumpOneOpr
4063  *        write out a single operator definition
4064  */
4065 static void
4066 dumpOneOpr(Archive *fout, OprInfo *oprinfo,
4067                    OprInfo *g_oprinfo, int numOperators)
4068 {
4069         PQExpBuffer query = createPQExpBuffer();
4070         PQExpBuffer q = createPQExpBuffer();
4071         PQExpBuffer delq = createPQExpBuffer();
4072         PQExpBuffer oprid = createPQExpBuffer();
4073         PQExpBuffer details = createPQExpBuffer();
4074         const char *name;
4075         PGresult   *res;
4076         int                     ntups;
4077         int                     i_oprkind;
4078         int                     i_oprcode;
4079         int                     i_oprleft;
4080         int                     i_oprright;
4081         int                     i_oprcom;
4082         int                     i_oprnegate;
4083         int                     i_oprrest;
4084         int                     i_oprjoin;
4085         int                     i_oprcanhash;
4086         int                     i_oprlsortop;
4087         int                     i_oprrsortop;
4088         int                     i_oprltcmpop;
4089         int                     i_oprgtcmpop;
4090         char       *oprkind;
4091         char       *oprcode;
4092         char       *oprleft;
4093         char       *oprright;
4094         char       *oprcom;
4095         char       *oprnegate;
4096         char       *oprrest;
4097         char       *oprjoin;
4098         char       *oprcanhash;
4099         char       *oprlsortop;
4100         char       *oprrsortop;
4101         char       *oprltcmpop;
4102         char       *oprgtcmpop;
4103
4104         /* Make sure we are in proper schema so regoperator works correctly */
4105         selectSourceSchema(oprinfo->oprnamespace->nspname);
4106
4107         if (g_fout->remoteVersion >= 70300)
4108         {
4109                 appendPQExpBuffer(query, "SELECT oprkind, "
4110                                                   "oprcode::pg_catalog.regprocedure, "
4111                                                   "oprleft::pg_catalog.regtype, "
4112                                                   "oprright::pg_catalog.regtype, "
4113                                                   "oprcom::pg_catalog.regoperator, "
4114                                                   "oprnegate::pg_catalog.regoperator, "
4115                                                   "oprrest::pg_catalog.regprocedure, "
4116                                                   "oprjoin::pg_catalog.regprocedure, "
4117                                                   "oprcanhash, "
4118                                                   "oprlsortop::pg_catalog.regoperator, "
4119                                                   "oprrsortop::pg_catalog.regoperator, "
4120                                                   "oprltcmpop::pg_catalog.regoperator, "
4121                                                   "oprgtcmpop::pg_catalog.regoperator "
4122                                                   "from pg_catalog.pg_operator "
4123                                                   "where oid = '%s'::pg_catalog.oid",
4124                                                   oprinfo->oid);
4125         }
4126         else if (g_fout->remoteVersion >= 70100)
4127         {
4128                 appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
4129                                                   "CASE WHEN oprleft = 0 THEN '-' "
4130                                            "ELSE format_type(oprleft, NULL) END as oprleft, "
4131                                                   "CASE WHEN oprright = 0 THEN '-' "
4132                                          "ELSE format_type(oprright, NULL) END as oprright, "
4133                                                   "oprcom, oprnegate, oprrest, oprjoin, "
4134                                                   "oprcanhash, oprlsortop, oprrsortop, "
4135                                                   "0 as oprltcmpop, 0 as oprgtcmpop "
4136                                                   "from pg_operator "
4137                                                   "where oid = '%s'::oid",
4138                                                   oprinfo->oid);
4139         }
4140         else
4141         {
4142                 appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
4143                                                   "CASE WHEN oprleft = 0 THEN '-'::name "
4144                                                   "ELSE (select typname from pg_type where oid = oprleft) END as oprleft, "
4145                                                   "CASE WHEN oprright = 0 THEN '-'::name "
4146                                                   "ELSE (select typname from pg_type where oid = oprright) END as oprright, "
4147                                                   "oprcom, oprnegate, oprrest, oprjoin, "
4148                                                   "oprcanhash, oprlsortop, oprrsortop, "
4149                                                   "0 as oprltcmpop, 0 as oprgtcmpop "
4150                                                   "from pg_operator "
4151                                                   "where oid = '%s'::oid",
4152                                                   oprinfo->oid);
4153         }
4154
4155         res = PQexec(g_conn, query->data);
4156         if (!res ||
4157                 PQresultStatus(res) != PGRES_TUPLES_OK)
4158         {
4159                 write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
4160                 exit_nicely();
4161         }
4162
4163         /* Expecting a single result only */
4164         ntups = PQntuples(res);
4165         if (ntups != 1)
4166         {
4167                 write_msg(NULL, "Got %d rows instead of one from: %s",
4168                                   ntups, query->data);
4169                 exit_nicely();
4170         }
4171
4172         i_oprkind = PQfnumber(res, "oprkind");
4173         i_oprcode = PQfnumber(res, "oprcode");
4174         i_oprleft = PQfnumber(res, "oprleft");
4175         i_oprright = PQfnumber(res, "oprright");
4176         i_oprcom = PQfnumber(res, "oprcom");
4177         i_oprnegate = PQfnumber(res, "oprnegate");
4178         i_oprrest = PQfnumber(res, "oprrest");
4179         i_oprjoin = PQfnumber(res, "oprjoin");
4180         i_oprcanhash = PQfnumber(res, "oprcanhash");
4181         i_oprlsortop = PQfnumber(res, "oprlsortop");
4182         i_oprrsortop = PQfnumber(res, "oprrsortop");
4183         i_oprltcmpop = PQfnumber(res, "oprltcmpop");
4184         i_oprgtcmpop = PQfnumber(res, "oprgtcmpop");
4185
4186         oprkind = PQgetvalue(res, 0, i_oprkind);
4187         oprcode = PQgetvalue(res, 0, i_oprcode);
4188         oprleft = PQgetvalue(res, 0, i_oprleft);
4189         oprright = PQgetvalue(res, 0, i_oprright);
4190         oprcom = PQgetvalue(res, 0, i_oprcom);
4191         oprnegate = PQgetvalue(res, 0, i_oprnegate);
4192         oprrest = PQgetvalue(res, 0, i_oprrest);
4193         oprjoin = PQgetvalue(res, 0, i_oprjoin);
4194         oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
4195         oprlsortop = PQgetvalue(res, 0, i_oprlsortop);
4196         oprrsortop = PQgetvalue(res, 0, i_oprrsortop);
4197         oprltcmpop = PQgetvalue(res, 0, i_oprltcmpop);
4198         oprgtcmpop = PQgetvalue(res, 0, i_oprgtcmpop);
4199
4200         appendPQExpBuffer(details, "    PROCEDURE = %s",
4201                                           convertRegProcReference(oprcode));
4202
4203         appendPQExpBuffer(oprid, "%s (",
4204                                           oprinfo->oprname);
4205
4206         /*
4207          * right unary means there's a left arg and left unary means there's a
4208          * right arg
4209          */
4210         if (strcmp(oprkind, "r") == 0 ||
4211                 strcmp(oprkind, "b") == 0)
4212         {
4213                 if (g_fout->remoteVersion >= 70100)
4214                         name = oprleft;
4215                 else
4216                         name = fmtId(oprleft);
4217                 appendPQExpBuffer(details, ",\n    LEFTARG = %s", name);
4218                 appendPQExpBuffer(oprid, "%s", name);
4219         }
4220         else
4221                 appendPQExpBuffer(oprid, "NONE");
4222
4223         if (strcmp(oprkind, "l") == 0 ||
4224                 strcmp(oprkind, "b") == 0)
4225         {
4226                 if (g_fout->remoteVersion >= 70100)
4227                         name = oprright;
4228                 else
4229                         name = fmtId(oprright);
4230                 appendPQExpBuffer(details, ",\n    RIGHTARG = %s", name);
4231                 appendPQExpBuffer(oprid, ", %s)", name);
4232         }
4233         else
4234                 appendPQExpBuffer(oprid, ", NONE)");
4235
4236         name = convertOperatorReference(oprcom, g_oprinfo, numOperators);
4237         if (name)
4238                 appendPQExpBuffer(details, ",\n    COMMUTATOR = %s", name);
4239
4240         name = convertOperatorReference(oprnegate, g_oprinfo, numOperators);
4241         if (name)
4242                 appendPQExpBuffer(details, ",\n    NEGATOR = %s", name);
4243
4244         if (strcmp(oprcanhash, "t") == 0)
4245                 appendPQExpBuffer(details, ",\n    HASHES");
4246
4247         name = convertRegProcReference(oprrest);
4248         if (name)
4249                 appendPQExpBuffer(details, ",\n    RESTRICT = %s", name);
4250
4251         name = convertRegProcReference(oprjoin);
4252         if (name)
4253                 appendPQExpBuffer(details, ",\n    JOIN = %s", name);
4254
4255         name = convertOperatorReference(oprlsortop, g_oprinfo, numOperators);
4256         if (name)
4257                 appendPQExpBuffer(details, ",\n    SORT1 = %s", name);
4258
4259         name = convertOperatorReference(oprrsortop, g_oprinfo, numOperators);
4260         if (name)
4261                 appendPQExpBuffer(details, ",\n    SORT2 = %s", name);
4262
4263         name = convertOperatorReference(oprltcmpop, g_oprinfo, numOperators);
4264         if (name)
4265                 appendPQExpBuffer(details, ",\n    LTCMP = %s", name);
4266
4267         name = convertOperatorReference(oprgtcmpop, g_oprinfo, numOperators);
4268         if (name)
4269                 appendPQExpBuffer(details, ",\n    GTCMP = %s", name);
4270
4271         /*
4272          * DROP must be fully qualified in case same name appears in
4273          * pg_catalog
4274          */
4275         appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
4276                                           fmtId(oprinfo->oprnamespace->nspname),
4277                                           oprid->data);
4278
4279         appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n",
4280                                           oprinfo->oprname, details->data);
4281
4282         ArchiveEntry(fout, oprinfo->oid, oprinfo->oprname,
4283                                  oprinfo->oprnamespace->nspname, oprinfo->usename,
4284                                  "OPERATOR", NULL,
4285                                  q->data, delq->data,
4286                                  NULL, NULL, NULL);
4287
4288         /* Dump Operator Comments */
4289
4290         resetPQExpBuffer(q);
4291         appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
4292         dumpComment(fout, q->data,
4293                                 oprinfo->oprnamespace->nspname, oprinfo->usename,
4294                                 oprinfo->oid, "pg_operator", 0, NULL);
4295
4296         PQclear(res);
4297
4298         destroyPQExpBuffer(query);
4299         destroyPQExpBuffer(q);
4300         destroyPQExpBuffer(delq);
4301         destroyPQExpBuffer(oprid);
4302         destroyPQExpBuffer(details);
4303 }
4304
4305 /*
4306  * Convert a function reference obtained from pg_operator
4307  *
4308  * Returns what to print, or NULL if function references is InvalidOid
4309  *
4310  * In 7.3 the input is a REGPROCEDURE display; we have to strip the
4311  * argument-types part.  In prior versions, the input is a REGPROC display.
4312  */
4313 static const char *
4314 convertRegProcReference(const char *proc)
4315 {
4316         /* In all cases "-" means a null reference */
4317         if (strcmp(proc, "-") == 0)
4318                 return NULL;
4319
4320         if (g_fout->remoteVersion >= 70300)
4321         {
4322                 char       *name;
4323                 char       *paren;
4324                 bool            inquote;
4325
4326                 name = strdup(proc);
4327                 /* find non-double-quoted left paren */
4328                 inquote = false;
4329                 for (paren = name; *paren; paren++)
4330                 {
4331                         if (*paren == '(' && !inquote)
4332                         {
4333                                 *paren = '\0';
4334                                 break;
4335                         }
4336                         if (*paren == '"')
4337                                 inquote = !inquote;
4338                 }
4339                 return name;
4340         }
4341
4342         /* REGPROC before 7.3 does not quote its result */
4343         return fmtId(proc);
4344 }
4345
4346 /*
4347  * Convert an operator cross-reference obtained from pg_operator
4348  *
4349  * Returns what to print, or NULL to print nothing
4350  *
4351  * In 7.3 the input is a REGOPERATOR display; we have to strip the
4352  * argument-types part.  In prior versions, the input is just a
4353  * numeric OID, which we search our operator list for.
4354  */
4355 static const char *
4356 convertOperatorReference(const char *opr,
4357                                                  OprInfo *g_oprinfo, int numOperators)
4358 {
4359         char       *name;
4360
4361         /* In all cases "0" means a null reference */
4362         if (strcmp(opr, "0") == 0)
4363                 return NULL;
4364
4365         if (g_fout->remoteVersion >= 70300)
4366         {
4367                 char       *paren;
4368                 bool            inquote;
4369
4370                 name = strdup(opr);
4371                 /* find non-double-quoted left paren */
4372                 inquote = false;
4373                 for (paren = name; *paren; paren++)
4374                 {
4375                         if (*paren == '(' && !inquote)
4376                         {
4377                                 *paren = '\0';
4378                                 break;
4379                         }
4380                         if (*paren == '"')
4381                                 inquote = !inquote;
4382                 }
4383                 return name;
4384         }
4385
4386         name = findOprByOid(g_oprinfo, numOperators, opr);
4387         if (name == NULL)
4388                 write_msg(NULL, "WARNING: could not find operator with OID %s\n",
4389                                   opr);
4390         return name;
4391 }
4392
4393
4394 /*
4395  * dumpOpclasses
4396  *        writes out to fout the queries to recreate all the user-defined
4397  *        operator classes
4398  */
4399 void
4400 dumpOpclasses(Archive *fout, OpclassInfo *opcinfo, int numOpclasses)
4401 {
4402         int                     i;
4403
4404         for (i = 0; i < numOpclasses; i++)
4405         {
4406                 /* Dump only opclasses in dumpable namespaces */
4407                 if (!opcinfo[i].opcnamespace->dump)
4408                         continue;
4409
4410                 /* OK, dump it */
4411                 dumpOneOpclass(fout, &opcinfo[i]);
4412         }
4413 }
4414
4415 /*
4416  * dumpOneOpclass
4417  *        write out a single operator class definition
4418  */
4419 static void
4420 dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo)
4421 {
4422         PQExpBuffer query = createPQExpBuffer();
4423         PQExpBuffer q = createPQExpBuffer();
4424         PQExpBuffer delq = createPQExpBuffer();
4425         PGresult   *res;
4426         int                     ntups;
4427         int                     i_opcintype;
4428         int                     i_opckeytype;
4429         int                     i_opcdefault;
4430         int                     i_amname;
4431         int                     i_amopstrategy;
4432         int                     i_amopreqcheck;
4433         int                     i_amopopr;
4434         int                     i_amprocnum;
4435         int                     i_amproc;
4436         char       *opcintype;
4437         char       *opckeytype;
4438         char       *opcdefault;
4439         char       *amname;
4440         char       *amopstrategy;
4441         char       *amopreqcheck;
4442         char       *amopopr;
4443         char       *amprocnum;
4444         char       *amproc;
4445         bool            needComma;
4446         int                     i;
4447
4448         /*
4449          * XXX currently we do not implement dumping of operator classes from
4450          * pre-7.3 databases.  This could be done but it seems not worth the
4451          * trouble.
4452          */
4453         if (g_fout->remoteVersion < 70300)
4454                 return;
4455
4456         /* Make sure we are in proper schema so regoperator works correctly */
4457         selectSourceSchema(opcinfo->opcnamespace->nspname);
4458
4459         /* Get additional fields from the pg_opclass row */
4460         appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
4461                                           "opckeytype::pg_catalog.regtype, "
4462                                           "opcdefault, "
4463         "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
4464                                           "FROM pg_catalog.pg_opclass "
4465                                           "WHERE oid = '%s'::pg_catalog.oid",
4466                                           opcinfo->oid);
4467
4468         res = PQexec(g_conn, query->data);
4469         if (!res ||
4470                 PQresultStatus(res) != PGRES_TUPLES_OK)
4471         {
4472                 write_msg(NULL, "query to obtain operator class details failed: %s", PQerrorMessage(g_conn));
4473                 exit_nicely();
4474         }
4475
4476         /* Expecting a single result only */
4477         ntups = PQntuples(res);
4478         if (ntups != 1)
4479         {
4480                 write_msg(NULL, "Got %d rows instead of one from: %s",
4481                                   ntups, query->data);
4482                 exit_nicely();
4483         }
4484
4485         i_opcintype = PQfnumber(res, "opcintype");
4486         i_opckeytype = PQfnumber(res, "opckeytype");
4487         i_opcdefault = PQfnumber(res, "opcdefault");
4488         i_amname = PQfnumber(res, "amname");
4489
4490         opcintype = PQgetvalue(res, 0, i_opcintype);
4491         opckeytype = PQgetvalue(res, 0, i_opckeytype);
4492         opcdefault = PQgetvalue(res, 0, i_opcdefault);
4493         amname = PQgetvalue(res, 0, i_amname);
4494
4495         /*
4496          * DROP must be fully qualified in case same name appears in
4497          * pg_catalog
4498          */
4499         appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
4500                                           fmtId(opcinfo->opcnamespace->nspname));
4501         appendPQExpBuffer(delq, ".%s",
4502                                           fmtId(opcinfo->opcname));
4503         appendPQExpBuffer(delq, " USING %s;\n",
4504                                           fmtId(amname));
4505
4506         /* Build the fixed portion of the CREATE command */
4507         appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n    ",
4508                                           fmtId(opcinfo->opcname));
4509         if (strcmp(opcdefault, "t") == 0)
4510                 appendPQExpBuffer(q, "DEFAULT ");
4511         appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n    ",
4512                                           opcintype,
4513                                           fmtId(amname));
4514
4515         needComma = false;
4516
4517         if (strcmp(opckeytype, "-") != 0)
4518         {
4519                 appendPQExpBuffer(q, "STORAGE %s",
4520                                                   opckeytype);
4521                 needComma = true;
4522         }
4523
4524         PQclear(res);
4525
4526         /*
4527          * Now fetch and print the OPERATOR entries (pg_amop rows).
4528          */
4529         resetPQExpBuffer(query);
4530
4531         appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
4532                                           "amopopr::pg_catalog.regoperator "
4533                                           "FROM pg_catalog.pg_amop "
4534                                           "WHERE amopclaid = '%s'::pg_catalog.oid "
4535                                           "ORDER BY amopstrategy",
4536                                           opcinfo->oid);
4537
4538         res = PQexec(g_conn, query->data);
4539         if (!res ||
4540                 PQresultStatus(res) != PGRES_TUPLES_OK)
4541         {
4542                 write_msg(NULL, "query to obtain operator class operators failed: %s", PQerrorMessage(g_conn));
4543                 exit_nicely();
4544         }
4545
4546         ntups = PQntuples(res);
4547
4548         i_amopstrategy = PQfnumber(res, "amopstrategy");
4549         i_amopreqcheck = PQfnumber(res, "amopreqcheck");
4550         i_amopopr = PQfnumber(res, "amopopr");
4551
4552         for (i = 0; i < ntups; i++)
4553         {
4554                 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
4555                 amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
4556                 amopopr = PQgetvalue(res, i, i_amopopr);
4557
4558                 if (needComma)
4559                         appendPQExpBuffer(q, " ,\n    ");
4560
4561                 appendPQExpBuffer(q, "OPERATOR %s %s",
4562                                                   amopstrategy, amopopr);
4563                 if (strcmp(amopreqcheck, "t") == 0)
4564                         appendPQExpBuffer(q, " RECHECK");
4565
4566                 needComma = true;
4567         }
4568
4569         PQclear(res);
4570
4571         /*
4572          * Now fetch and print the FUNCTION entries (pg_amproc rows).
4573          */
4574         resetPQExpBuffer(query);
4575
4576         appendPQExpBuffer(query, "SELECT amprocnum, "
4577                                           "amproc::pg_catalog.regprocedure "
4578                                           "FROM pg_catalog.pg_amproc "
4579                                           "WHERE amopclaid = '%s'::pg_catalog.oid "
4580                                           "ORDER BY amprocnum",
4581                                           opcinfo->oid);
4582
4583         res = PQexec(g_conn, query->data);
4584         if (!res ||
4585                 PQresultStatus(res) != PGRES_TUPLES_OK)
4586         {
4587                 write_msg(NULL, "query to obtain operator class functions failed: %s", PQerrorMessage(g_conn));
4588                 exit_nicely();
4589         }
4590
4591         ntups = PQntuples(res);
4592
4593         i_amprocnum = PQfnumber(res, "amprocnum");
4594         i_amproc = PQfnumber(res, "amproc");
4595
4596         for (i = 0; i < ntups; i++)
4597         {
4598                 amprocnum = PQgetvalue(res, i, i_amprocnum);
4599                 amproc = PQgetvalue(res, i, i_amproc);
4600
4601                 if (needComma)
4602                         appendPQExpBuffer(q, " ,\n    ");
4603
4604                 appendPQExpBuffer(q, "FUNCTION %s %s",
4605                                                   amprocnum, amproc);
4606
4607                 needComma = true;
4608         }
4609
4610         PQclear(res);
4611
4612         appendPQExpBuffer(q, ";\n");
4613
4614         ArchiveEntry(fout, opcinfo->oid, opcinfo->opcname,
4615                                  opcinfo->opcnamespace->nspname, opcinfo->usename,
4616                                  "OPERATOR CLASS", NULL,
4617                                  q->data, delq->data,
4618                                  NULL, NULL, NULL);
4619
4620         destroyPQExpBuffer(query);
4621         destroyPQExpBuffer(q);
4622         destroyPQExpBuffer(delq);
4623 }
4624
4625
4626 /*
4627  * dumpAggs
4628  *        writes out to fout the queries to create all the user-defined aggregates
4629  */
4630 void
4631 dumpAggs(Archive *fout, AggInfo agginfo[], int numAggs)
4632 {
4633         int                     i;
4634
4635         for (i = 0; i < numAggs; i++)
4636         {
4637                 /* Dump only aggs in dumpable namespaces */
4638                 if (!agginfo[i].aggnamespace->dump)
4639                         continue;
4640
4641                 dumpOneAgg(fout, &agginfo[i]);
4642                 if (!aclsSkip)
4643                         dumpAggACL(fout, &agginfo[i]);
4644         }
4645 }
4646
4647
4648 /*
4649  * format_aggregate_signature: generate aggregate name and argument list
4650  *
4651  * The argument type names are qualified if needed.  The aggregate name
4652  * is never qualified.
4653  */
4654 static char *
4655 format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
4656 {
4657         PQExpBufferData buf;
4658
4659         initPQExpBuffer(&buf);
4660         if (honor_quotes)
4661                 appendPQExpBuffer(&buf, "%s",
4662                                                   fmtId(agginfo->aggname));
4663         else
4664                 appendPQExpBuffer(&buf, "%s", agginfo->aggname);
4665
4666         /* If using regtype or format_type, fmtbasetype is already quoted */
4667         if (fout->remoteVersion >= 70100)
4668         {
4669                 if (agginfo->anybasetype)
4670                         appendPQExpBuffer(&buf, "(*)");
4671                 else
4672                         appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
4673         }
4674         else
4675         {
4676                 if (agginfo->anybasetype)
4677                         appendPQExpBuffer(&buf, "(*)");
4678                 else
4679                         appendPQExpBuffer(&buf, "(%s)",
4680                                                           fmtId(agginfo->fmtbasetype));
4681         }
4682
4683         return buf.data;
4684 }
4685
4686
4687 static void
4688 dumpAggACL(Archive *fout, AggInfo *finfo)
4689 {
4690         char       *aggsig,
4691                            *aggsig_tag;
4692
4693         aggsig = format_aggregate_signature(finfo, fout, true);
4694         aggsig_tag = format_aggregate_signature(finfo, fout, false);
4695         dumpACL(fout, "FUNCTION", aggsig, aggsig_tag,
4696                         finfo->aggnamespace->nspname,
4697                         finfo->usename, finfo->aggacl, finfo->oid);
4698         free(aggsig);
4699         free(aggsig_tag);
4700 }
4701
4702
4703 /*
4704  * dumpOneAgg
4705  *        write out a single aggregate definition
4706  */
4707 static void
4708 dumpOneAgg(Archive *fout, AggInfo *agginfo)
4709 {
4710         PQExpBuffer query = createPQExpBuffer();
4711         PQExpBuffer q = createPQExpBuffer();
4712         PQExpBuffer delq = createPQExpBuffer();
4713         PQExpBuffer details = createPQExpBuffer();
4714         char       *aggsig;
4715         char       *aggsig_tag;
4716         PGresult   *res;
4717         int                     ntups;
4718         int                     i_aggtransfn;
4719         int                     i_aggfinalfn;
4720         int                     i_aggtranstype;
4721         int                     i_agginitval;
4722         int                     i_anybasetype;
4723         int                     i_fmtbasetype;
4724         int                     i_convertok;
4725         const char *aggtransfn;
4726         const char *aggfinalfn;
4727         const char *aggtranstype;
4728         const char *agginitval;
4729         bool            convertok;
4730
4731         /* Make sure we are in proper schema */
4732         selectSourceSchema(agginfo->aggnamespace->nspname);
4733
4734         /* Get aggregate-specific details */
4735         if (g_fout->remoteVersion >= 70300)
4736         {
4737                 appendPQExpBuffer(query, "SELECT aggtransfn, "
4738                                                   "aggfinalfn, aggtranstype::pg_catalog.regtype, "
4739                                                   "agginitval, "
4740                                                   "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
4741                                         "proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
4742                                                   "'t'::boolean as convertok "
4743                                   "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
4744                                                   "where a.aggfnoid = p.oid "
4745                                                   "and p.oid = '%s'::pg_catalog.oid",
4746                                                   agginfo->oid);
4747         }
4748         else if (g_fout->remoteVersion >= 70100)
4749         {
4750                 appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
4751                                           "format_type(aggtranstype, NULL) as aggtranstype, "
4752                                                   "agginitval, "
4753                                                   "aggbasetype = 0 as anybasetype, "
4754                                                   "CASE WHEN aggbasetype = 0 THEN '-' "
4755                            "ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
4756                                                   "'t'::boolean as convertok "
4757                                                   "from pg_aggregate "
4758                                                   "where oid = '%s'::oid",
4759                                                   agginfo->oid);
4760         }
4761         else
4762         {
4763                 appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
4764                                                   "aggfinalfn, "
4765                                                   "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
4766                                                   "agginitval1 as agginitval, "
4767                                                   "aggbasetype = 0 as anybasetype, "
4768                                                   "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
4769                                                   "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
4770                                                   "from pg_aggregate "
4771                                                   "where oid = '%s'::oid",
4772                                                   agginfo->oid);
4773         }
4774
4775         res = PQexec(g_conn, query->data);
4776         if (!res ||
4777                 PQresultStatus(res) != PGRES_TUPLES_OK)
4778         {
4779                 write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
4780                                   PQerrorMessage(g_conn));
4781                 exit_nicely();
4782         }
4783
4784         /* Expecting a single result only */
4785         ntups = PQntuples(res);
4786         if (ntups != 1)
4787         {
4788                 write_msg(NULL, "Got %d rows instead of one from: %s",
4789                                   ntups, query->data);
4790                 exit_nicely();
4791         }
4792
4793         i_aggtransfn = PQfnumber(res, "aggtransfn");
4794         i_aggfinalfn = PQfnumber(res, "aggfinalfn");
4795         i_aggtranstype = PQfnumber(res, "aggtranstype");
4796         i_agginitval = PQfnumber(res, "agginitval");
4797         i_anybasetype = PQfnumber(res, "anybasetype");
4798         i_fmtbasetype = PQfnumber(res, "fmtbasetype");
4799         i_convertok = PQfnumber(res, "convertok");
4800
4801         aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
4802         aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
4803         aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
4804         agginitval = PQgetvalue(res, 0, i_agginitval);
4805         /* we save anybasetype so that dumpAggACL can use it later */
4806         agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
4807         /* we save fmtbasetype so that dumpAggACL can use it later */
4808         agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
4809         convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
4810
4811         aggsig = format_aggregate_signature(agginfo, g_fout, true);
4812         aggsig_tag = format_aggregate_signature(agginfo, g_fout, false);
4813
4814         if (!convertok)
4815         {
4816                 write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
4817                                   aggsig);
4818
4819                 appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
4820                                                   aggsig);
4821                 ArchiveEntry(fout, agginfo->oid, aggsig_tag,
4822                                          agginfo->aggnamespace->nspname, agginfo->usename,
4823                                          "WARNING", NULL,
4824                                          q->data, "" /* Del */ ,
4825                                          NULL, NULL, NULL);
4826                 return;
4827         }
4828
4829         if (g_fout->remoteVersion >= 70300)
4830         {
4831                 /* If using 7.3's regproc or regtype, data is already quoted */
4832                 appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
4833                                                   agginfo->anybasetype ? "'any'" :
4834                                                   agginfo->fmtbasetype,
4835                                                   aggtransfn,
4836                                                   aggtranstype);
4837         }
4838         else if (g_fout->remoteVersion >= 70100)
4839         {
4840                 /* format_type quotes, regproc does not */
4841                 appendPQExpBuffer(details, "    BASETYPE = %s,\n    SFUNC = %s,\n    STYPE = %s",
4842                                                   agginfo->anybasetype ? "'any'" :
4843                                                   agginfo->fmtbasetype,
4844                                                   fmtId(aggtransfn),
4845                                                   aggtranstype);
4846         }
4847         else
4848         {
4849                 /* need quotes all around */
4850                 appendPQExpBuffer(details, "    BASETYPE = %s,\n",
4851                                                   agginfo->anybasetype ? "'any'" :
4852                                                   fmtId(agginfo->fmtbasetype));
4853                 appendPQExpBuffer(details, "    SFUNC = %s,\n",
4854                                                   fmtId(aggtransfn));
4855                 appendPQExpBuffer(details, "    STYPE = %s",
4856                                                   fmtId(aggtranstype));
4857         }
4858
4859         if (!PQgetisnull(res, 0, i_agginitval))
4860         {
4861                 appendPQExpBuffer(details, ",\n    INITCOND = ");
4862                 appendStringLiteral(details, agginitval, true);
4863         }
4864
4865         if (strcmp(aggfinalfn, "-") != 0)
4866         {
4867                 appendPQExpBuffer(details, ",\n    FINALFUNC = %s",
4868                                                   aggfinalfn);
4869         }
4870
4871         /*
4872          * DROP must be fully qualified in case same name appears in
4873          * pg_catalog
4874          */
4875         appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
4876                                           fmtId(agginfo->aggnamespace->nspname),
4877                                           aggsig);
4878
4879         appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
4880                                           fmtId(agginfo->aggname),
4881                                           details->data);
4882
4883         ArchiveEntry(fout, agginfo->oid, aggsig_tag,
4884                                  agginfo->aggnamespace->nspname, agginfo->usename,
4885                                  "AGGREGATE", NULL,
4886                                  q->data, delq->data,
4887                                  NULL, NULL, NULL);
4888
4889         /* Dump Aggregate Comments */
4890
4891         resetPQExpBuffer(q);
4892         appendPQExpBuffer(q, "AGGREGATE %s", aggsig);
4893         if (g_fout->remoteVersion >= 70300)
4894                 dumpComment(fout, q->data,
4895                                         agginfo->aggnamespace->nspname, agginfo->usename,
4896                                         agginfo->oid, "pg_proc", 0, NULL);
4897         else
4898                 dumpComment(fout, q->data,
4899                                         agginfo->aggnamespace->nspname, agginfo->usename,
4900                                         agginfo->oid, "pg_aggregate", 0, NULL);
4901
4902         PQclear(res);
4903
4904         destroyPQExpBuffer(query);
4905         destroyPQExpBuffer(q);
4906         destroyPQExpBuffer(delq);
4907         destroyPQExpBuffer(details);
4908         free(aggsig);
4909         free(aggsig_tag);
4910 }
4911
4912
4913 /*----------
4914  * Write out grant/revoke information
4915  *
4916  * 'type' must be TABLE, FUNCTION, LANGUAGE, or SCHEMA.
4917  * 'name' is the formatted name of the object.  Must be quoted etc. already.
4918  * 'tag' is the tag for the archive entry (typ. unquoted name of object).
4919  * 'nspname' is the namespace the object is in (NULL if none).
4920  * 'owner' is the owner, NULL if there is no owner (for languages).
4921  * 'acls' is the string read out of the fooacl system catalog field;
4922  * it will be parsed here.
4923  * 'objoid' is the OID of the object for purposes of ordering.
4924  *----------
4925  */
4926 static void
4927 dumpACL(Archive *fout, const char *type, const char *name,
4928                 const char *tag, const char *nspname, const char *owner,
4929                 const char *acls, const char *objoid)
4930 {
4931         PQExpBuffer sql;
4932
4933         /*
4934          * acl_lang is a flag only true if we are dumping language's ACL, so
4935          * we can set 'type' to a value that is suitable to build SQL requests
4936          * as for other types.
4937          */
4938         bool            acl_lang = false;
4939
4940         if (!strcmp(type, "ACL LANGUAGE"))
4941         {
4942                 type = "LANGUAGE";
4943                 acl_lang = true;
4944         }
4945
4946         sql = createPQExpBuffer();
4947
4948         if (!buildACLCommands(name, type, acls, owner, fout->remoteVersion, sql))
4949         {
4950                 write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
4951                                   acls, name, type);
4952                 exit_nicely();
4953         }
4954
4955         if (sql->len > 0)
4956                 ArchiveEntry(fout, objoid, tag, nspname,
4957                                          owner ? owner : "",
4958                                          acl_lang ? "ACL LANGUAGE" : "ACL",
4959                                          NULL, sql->data, "", NULL, NULL, NULL);
4960
4961         destroyPQExpBuffer(sql);
4962 }
4963
4964
4965 static void
4966 dumpTableACL(Archive *fout, TableInfo *tbinfo)
4967 {
4968         char       *namecopy = strdup(fmtId(tbinfo->relname));
4969         char       *dumpoid;
4970
4971         /*
4972          * Choose OID to use for sorting ACL into position.  For a view, sort
4973          * by the view OID; for a serial sequence, sort by the owning table's
4974          * OID; otherwise use the table's own OID.
4975          */
4976         if (tbinfo->viewoid != NULL)
4977                 dumpoid = tbinfo->viewoid;
4978         else if (tbinfo->owning_tab != NULL)
4979                 dumpoid = tbinfo->owning_tab;
4980         else
4981                 dumpoid = tbinfo->oid;
4982
4983         dumpACL(fout, "TABLE", namecopy, tbinfo->relname,
4984                   tbinfo->relnamespace->nspname, tbinfo->usename, tbinfo->relacl,
4985                         dumpoid);
4986
4987         free(namecopy);
4988 }
4989
4990
4991 /*
4992  * dumpTables:
4993  *        write out to fout the declarations (not data) of all user-defined tables
4994  */
4995 void
4996 dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
4997                  const bool aclsSkip, const bool schemaOnly, const bool dataOnly)
4998 {
4999         int                     i;
5000
5001         /*
5002          * Dump non-serial sequences first, in case they are referenced in
5003          * table defn's
5004          */
5005         for (i = 0; i < numTables; i++)
5006         {
5007                 TableInfo  *tbinfo = &tblinfo[i];
5008
5009                 if (tbinfo->relkind != RELKIND_SEQUENCE)
5010                         continue;
5011
5012                 if (tbinfo->dump && tbinfo->owning_tab == NULL)
5013                 {
5014                         dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
5015                         if (!dataOnly && !aclsSkip)
5016                                 dumpTableACL(fout, tbinfo);
5017                 }
5018         }
5019
5020         if (!dataOnly)
5021         {
5022                 for (i = 0; i < numTables; i++)
5023                 {
5024                         TableInfo  *tbinfo = &tblinfo[i];
5025
5026                         if (tbinfo->relkind == RELKIND_SEQUENCE)        /* already dumped */
5027                                 continue;
5028
5029                         if (tbinfo->dump)
5030                         {
5031                                 dumpOneTable(fout, tbinfo, tblinfo);
5032                                 if (!aclsSkip)
5033                                         dumpTableACL(fout, tbinfo);
5034                         }
5035                 }
5036         }
5037
5038         /*
5039          * Dump serial sequences last (we will not emit any CREATE commands,
5040          * but we do have to think about ACLs and setval operations).
5041          */
5042         for (i = 0; i < numTables; i++)
5043         {
5044                 TableInfo  *tbinfo = &tblinfo[i];
5045
5046                 if (tbinfo->relkind != RELKIND_SEQUENCE)
5047                         continue;
5048
5049                 if (tbinfo->dump && tbinfo->owning_tab != NULL)
5050                 {
5051                         dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
5052                         if (!dataOnly && !aclsSkip)
5053                                 dumpTableACL(fout, tbinfo);
5054                 }
5055         }
5056 }
5057
5058 /*
5059  * dumpOneTable
5060  *        write the declaration (not data) of one user-defined table or view
5061  */
5062 static void
5063 dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
5064 {
5065         PQExpBuffer query = createPQExpBuffer();
5066         PQExpBuffer q = createPQExpBuffer();
5067         PQExpBuffer delq = createPQExpBuffer();
5068         PGresult   *res;
5069         int                     numParents;
5070         int                *parentIndexes;
5071         int                     actual_atts;    /* number of attrs in this CREATE statment */
5072         char       *reltypename;
5073         char       *storage;
5074         char       *objoid;
5075         const char *((*commentDeps)[]);
5076         int                     j,
5077                                 k;
5078
5079         /* Make sure we are in proper schema */
5080         selectSourceSchema(tbinfo->relnamespace->nspname);
5081
5082         /* Is it a table or a view? */
5083         if (tbinfo->relkind == RELKIND_VIEW)
5084         {
5085                 char       *viewdef;
5086
5087                 reltypename = "VIEW";
5088
5089                 /* Fetch the view definition */
5090                 if (g_fout->remoteVersion >= 70300)
5091                 {
5092                         /* Beginning in 7.3, viewname is not unique; use OID */
5093                         appendPQExpBuffer(query, "SELECT pg_catalog.pg_get_viewdef(ev_class) as viewdef, "
5094                                                           "oid as view_oid"
5095                                                           " from pg_catalog.pg_rewrite where"
5096                                                           " ev_class = '%s'::pg_catalog.oid and"
5097                                                           " rulename = '_RETURN';",
5098                                                           tbinfo->oid);
5099                 }
5100                 else
5101                 {
5102                         appendPQExpBuffer(query, "SELECT definition as viewdef, "
5103                                                           "(select oid from pg_rewrite where "
5104                                           " rulename=('_RET' || viewname)::name) as view_oid"
5105                                                           " from pg_views where viewname = ");
5106                         appendStringLiteral(query, tbinfo->relname, true);
5107                         appendPQExpBuffer(query, ";");
5108                 }
5109
5110                 res = PQexec(g_conn, query->data);
5111                 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
5112                 {
5113                         write_msg(NULL, "query to obtain definition of view \"%s\" failed: %s",
5114                                           tbinfo->relname, PQerrorMessage(g_conn));
5115                         exit_nicely();
5116                 }
5117
5118                 if (PQntuples(res) != 1)
5119                 {
5120                         if (PQntuples(res) < 1)
5121                                 write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
5122                                                   tbinfo->relname);
5123                         else
5124                                 write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
5125                                                   tbinfo->relname);
5126                         exit_nicely();
5127                 }
5128
5129                 if (PQgetisnull(res, 0, 1))
5130                 {
5131                         write_msg(NULL, "query to obtain definition of view \"%s\" returned null OID\n",
5132                                           tbinfo->relname);
5133                         exit_nicely();
5134                 }
5135
5136                 viewdef = PQgetvalue(res, 0, 0);
5137
5138                 if (strlen(viewdef) == 0)
5139                 {
5140                         write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
5141                                           tbinfo->relname);
5142                         exit_nicely();
5143                 }
5144
5145                 /* We use the OID of the view rule as the object OID */
5146                 objoid = strdup(PQgetvalue(res, 0, 1));
5147                 /* Save it for use by dumpACL, too */
5148                 tbinfo->viewoid = objoid;
5149
5150                 /*
5151                  * DROP must be fully qualified in case same name appears in
5152                  * pg_catalog
5153                  */
5154                 appendPQExpBuffer(delq, "DROP VIEW %s.",
5155                                                   fmtId(tbinfo->relnamespace->nspname));
5156                 appendPQExpBuffer(delq, "%s;\n",
5157                                                   fmtId(tbinfo->relname));
5158
5159                 appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
5160                                                   fmtId(tbinfo->relname), viewdef);
5161
5162                 PQclear(res);
5163
5164                 /*
5165                  * Views can have default values -- however, they must be
5166                  * specified in an ALTER TABLE command after the view has been
5167                  * created, not in the view definition itself.
5168                  */
5169                 for (j = 0; j < tbinfo->numatts; j++)
5170                 {
5171                         if (tbinfo->adef_expr[j] != NULL && !tbinfo->inhAttrDef[j])
5172                         {
5173                                 appendPQExpBuffer(q, "ALTER TABLE %s ",
5174                                                                   fmtId(tbinfo->relname));
5175                                 appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
5176                                                                   fmtId(tbinfo->attnames[j]),
5177                                                                   tbinfo->adef_expr[j]);
5178                         }
5179                 }
5180
5181                 commentDeps = malloc(sizeof(char *) * 2);
5182                 (*commentDeps)[0] = strdup(objoid);
5183                 (*commentDeps)[1] = NULL;               /* end of list */
5184         }
5185         else
5186         {
5187                 reltypename = "TABLE";
5188                 objoid = tbinfo->oid;
5189                 commentDeps = NULL;
5190                 numParents = tbinfo->numParents;
5191                 parentIndexes = tbinfo->parentIndexes;
5192
5193                 /*
5194                  * DROP must be fully qualified in case same name appears in
5195                  * pg_catalog
5196                  */
5197                 appendPQExpBuffer(delq, "DROP TABLE %s.",
5198                                                   fmtId(tbinfo->relnamespace->nspname));
5199                 appendPQExpBuffer(delq, "%s;\n",
5200                                                   fmtId(tbinfo->relname));
5201
5202                 appendPQExpBuffer(q, "CREATE TABLE %s (",
5203                                                   fmtId(tbinfo->relname));
5204                 actual_atts = 0;
5205                 for (j = 0; j < tbinfo->numatts; j++)
5206                 {
5207                         /* Is this one of the table's own attrs, and not dropped ? */
5208                         if (!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j])
5209                         {
5210                                 /* Format properly if not first attr */
5211                                 if (actual_atts > 0)
5212                                         appendPQExpBuffer(q, ",");
5213                                 appendPQExpBuffer(q, "\n    ");
5214
5215                                 /* Attribute name */
5216                                 appendPQExpBuffer(q, "%s ",
5217                                                                   fmtId(tbinfo->attnames[j]));
5218
5219                                 /* Attribute type */
5220                                 if (g_fout->remoteVersion >= 70100)
5221                                 {
5222                                         char       *typname = tbinfo->atttypnames[j];
5223
5224                                         if (tbinfo->attisserial[j])
5225                                         {
5226                                                 if (strcmp(typname, "integer") == 0)
5227                                                         typname = "serial";
5228                                                 else if (strcmp(typname, "bigint") == 0)
5229                                                         typname = "bigserial";
5230                                         }
5231                                         appendPQExpBuffer(q, "%s", typname);
5232                                 }
5233                                 else
5234                                 {
5235                                         /* If no format_type, fake it */
5236                                         appendPQExpBuffer(q, "%s",
5237                                                                           myFormatType(tbinfo->atttypnames[j],
5238                                                                                                    tbinfo->atttypmod[j]));
5239                                 }
5240
5241                                 /* Default value --- suppress if inherited or serial */
5242                                 if (tbinfo->adef_expr[j] != NULL &&
5243                                         !tbinfo->inhAttrDef[j] &&
5244                                         !tbinfo->attisserial[j])
5245                                         appendPQExpBuffer(q, " DEFAULT %s",
5246                                                                           tbinfo->adef_expr[j]);
5247
5248                                 /*
5249                                  * Not Null constraint --- suppress if inherited
5250                                  *
5251                                  * Note: we could suppress this for serial columns since
5252                                  * SERIAL implies NOT NULL.  We choose not to for forward
5253                                  * compatibility, since there has been some talk of making
5254                                  * SERIAL not imply NOT NULL, in which case the explicit
5255                                  * specification would be needed.
5256                                  */
5257                                 if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
5258                                         appendPQExpBuffer(q, " NOT NULL");
5259
5260                                 actual_atts++;
5261                         }
5262                 }
5263
5264                 /*
5265                  * Add non-inherited CHECK constraints, if any. If a constraint
5266                  * matches by name and condition with a constraint belonging to a
5267                  * parent class (OR conditions match and both names start with
5268                  * '$'), we assume it was inherited.
5269                  */
5270                 if (tbinfo->ncheck > 0)
5271                 {
5272                         PGresult   *res2;
5273                         int                     i_conname,
5274                                                 i_consrc;
5275                         int                     ntups2;
5276
5277                         if (g_verbose)
5278                                 write_msg(NULL, "finding check constraints for table \"%s\"\n",
5279                                                   tbinfo->relname);
5280
5281                         resetPQExpBuffer(query);
5282                         if (g_fout->remoteVersion >= 70400)
5283                                 appendPQExpBuffer(query, "SELECT conname, "
5284                                         " pg_catalog.pg_get_constraintdef(c1.oid) AS consrc "
5285                                                                   " from pg_catalog.pg_constraint c1 "
5286                                                                 " where conrelid = '%s'::pg_catalog.oid "
5287                                                                   "   and contype = 'c' "
5288                                                                   "   and not exists "
5289                                                                   "  (select 1 from "
5290                                                                   "    pg_catalog.pg_constraint c2, "
5291                                                                   "    pg_catalog.pg_inherits i "
5292                                                                   "    where i.inhrelid = c1.conrelid "
5293                                                                   "      and (c2.conname = c1.conname "
5294                                                                   "          or (c2.conname[0] = '$' "
5295                                                                   "              and c1.conname[0] = '$')"
5296                                                                   "          )"
5297                                          "      and pg_catalog.pg_get_constraintdef(c2.oid) "
5298                                                                   "                  = pg_catalog.pg_get_constraintdef(c1.oid) "
5299                                                                   "      and c2.conrelid = i.inhparent) "
5300                                                                   " order by conname ",
5301                                                                   tbinfo->oid);
5302                         else if (g_fout->remoteVersion >= 70300)
5303                                 appendPQExpBuffer(query, "SELECT conname, "
5304                                                                   " 'CHECK (' || consrc || ')' AS consrc"
5305                                                                   " from pg_catalog.pg_constraint c1"
5306                                                                 " where conrelid = '%s'::pg_catalog.oid "
5307                                                                   "   and contype = 'c' "
5308                                                                   "   and not exists "
5309                                                                   "  (select 1 from "
5310                                                                   "    pg_catalog.pg_constraint c2, "
5311                                                                   "    pg_catalog.pg_inherits i "
5312                                                                   "    where i.inhrelid = c1.conrelid "
5313                                                                   "      and (c2.conname = c1.conname "
5314                                                                   "          or (c2.conname[0] = '$' "
5315                                                                   "              and c1.conname[0] = '$')"
5316                                                                   "          )"
5317                                                                   "      and c2.consrc = c1.consrc "
5318                                                                   "      and c2.conrelid = i.inhparent) "
5319                                                                   " order by conname ",
5320                                                                   tbinfo->oid);
5321                         else
5322                                 appendPQExpBuffer(query, "SELECT rcname as conname,"
5323                                                                   " 'CHECK (' || rcsrc || ')' as consrc"
5324                                                                   " from pg_relcheck c1"
5325                                                                   " where rcrelid = '%s'::oid "
5326                                                                   "   and not exists "
5327                                                                   "  (select 1 from pg_relcheck c2, "
5328                                                                   "    pg_inherits i "
5329                                                                   "    where i.inhrelid = c1.rcrelid "
5330                                                                   "      and (c2.rcname = c1.rcname "
5331                                                                   "          or (c2.rcname[0] = '$' "
5332                                                                   "              and c1.rcname[0] = '$')"
5333                                                                   "          )"
5334                                                                   "      and c2.rcsrc = c1.rcsrc "
5335                                                                   "      and c2.rcrelid = i.inhparent) "
5336                                                                   " order by rcname ",
5337                                                                   tbinfo->oid);
5338                         res2 = PQexec(g_conn, query->data);
5339                         if (!res2 ||
5340                                 PQresultStatus(res2) != PGRES_TUPLES_OK)
5341                         {
5342                                 write_msg(NULL, "query to obtain check constraints failed: %s", PQerrorMessage(g_conn));
5343                                 exit_nicely();
5344                         }
5345                         ntups2 = PQntuples(res2);
5346                         if (ntups2 > tbinfo->ncheck)
5347                         {
5348                                 write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
5349                                                   tbinfo->ncheck, tbinfo->relname, ntups2);
5350                                 write_msg(NULL, "(The system catalogs might be corrupted.)\n");
5351                                 exit_nicely();
5352                         }
5353
5354                         i_conname = PQfnumber(res2, "conname");
5355                         i_consrc = PQfnumber(res2, "consrc");
5356
5357                         for (j = 0; j < ntups2; j++)
5358                         {
5359                                 const char *name = PQgetvalue(res2, j, i_conname);
5360                                 const char *expr = PQgetvalue(res2, j, i_consrc);
5361
5362                                 if (actual_atts + j > 0)
5363                                         appendPQExpBuffer(q, ",\n    ");
5364
5365                                 appendPQExpBuffer(q, "CONSTRAINT %s ",
5366                                                                   fmtId(name));
5367                                 appendPQExpBuffer(q, "%s", expr);
5368                         }
5369                         PQclear(res2);
5370                 }
5371
5372                 /*
5373                  * Primary Key: In versions of PostgreSQL prior to 7.2, we needed
5374                  * to include the primary key in the table definition. However,
5375                  * this is not ideal because it creates an index on the table,
5376                  * which makes COPY slower. As of release 7.2, we can add primary
5377                  * keys to a table after it has been created, using ALTER TABLE;
5378                  * see dumpIndexes() for more information. Therefore, we ignore
5379                  * primary keys in this function.
5380                  */
5381
5382                 appendPQExpBuffer(q, "\n)");
5383
5384                 if (numParents > 0)
5385                 {
5386                         appendPQExpBuffer(q, "\nINHERITS (");
5387                         for (k = 0; k < numParents; k++)
5388                         {
5389                                 TableInfo  *parentRel = &g_tblinfo[parentIndexes[k]];
5390
5391                                 if (k > 0)
5392                                         appendPQExpBuffer(q, ", ");
5393                                 if (parentRel->relnamespace != tbinfo->relnamespace)
5394                                         appendPQExpBuffer(q, "%s.",
5395                                                                 fmtId(parentRel->relnamespace->nspname));
5396                                 appendPQExpBuffer(q, "%s",
5397                                                                   fmtId(parentRel->relname));
5398                         }
5399                         appendPQExpBuffer(q, ")");
5400                 }
5401
5402                 if (!tbinfo->hasoids)
5403                         appendPQExpBuffer(q, " WITHOUT OIDS");
5404
5405                 appendPQExpBuffer(q, ";\n");
5406
5407                 /* Loop dumping statistics and storage statements */
5408                 for (j = 0; j < tbinfo->numatts; j++)
5409                 {
5410                         /*
5411                          * Dump per-column statistics information. We only issue an
5412                          * ALTER TABLE statement if the attstattarget entry for this
5413                          * column is non-negative (i.e. it's not the default value)
5414                          */
5415                         if (tbinfo->attstattarget[j] >= 0 &&
5416                                 !tbinfo->attisdropped[j])
5417                         {
5418                                 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
5419                                                                   fmtId(tbinfo->relname));
5420                                 appendPQExpBuffer(q, "ALTER COLUMN %s ",
5421                                                                   fmtId(tbinfo->attnames[j]));
5422                                 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
5423                                                                   tbinfo->attstattarget[j]);
5424                         }
5425
5426                         /*
5427                          * Dump per-column storage information.  The statement is only
5428                          * dumped if the storage has been changed from the type's
5429                          * default.
5430                          */
5431                         if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
5432                         {
5433                                 switch (tbinfo->attstorage[j])
5434                                 {
5435                                         case 'p':
5436                                                 storage = "PLAIN";
5437                                                 break;
5438                                         case 'e':
5439                                                 storage = "EXTERNAL";
5440                                                 break;
5441                                         case 'm':
5442                                                 storage = "MAIN";
5443                                                 break;
5444                                         case 'x':
5445                                                 storage = "EXTENDED";
5446                                                 break;
5447                                         default:
5448                                                 storage = NULL;
5449                                 }
5450
5451                                 /*
5452                                  * Only dump the statement if it's a storage type we
5453                                  * recognize
5454                                  */
5455                                 if (storage != NULL)
5456                                 {
5457                                         appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
5458                                                                           fmtId(tbinfo->relname));
5459                                         appendPQExpBuffer(q, "ALTER COLUMN %s ",
5460                                                                           fmtId(tbinfo->attnames[j]));
5461                                         appendPQExpBuffer(q, "SET STORAGE %s;\n",
5462                                                                           storage);
5463                                 }
5464                         }
5465                 }
5466         }
5467
5468         ArchiveEntry(fout, objoid, tbinfo->relname,
5469                                  tbinfo->relnamespace->nspname, tbinfo->usename,
5470                                  reltypename, NULL, q->data, delq->data,
5471                                  NULL, NULL, NULL);
5472
5473         /* Dump Table Comments */
5474         dumpTableComment(fout, tbinfo, reltypename, commentDeps);
5475
5476         /* commentDeps now belongs to the archive entry ... don't free it! */
5477
5478         destroyPQExpBuffer(query);
5479         destroyPQExpBuffer(q);
5480         destroyPQExpBuffer(delq);
5481 }
5482
5483 /*
5484  * getAttrName: extract the correct name for an attribute
5485  *
5486  * The array tblInfo->attnames[] only provides names of user attributes;
5487  * if a system attribute number is supplied, we have to fake it.
5488  * We also do a little bit of bounds checking for safety's sake.
5489  */
5490 static const char *
5491 getAttrName(int attrnum, TableInfo *tblInfo)
5492 {
5493         if (attrnum > 0 && attrnum <= tblInfo->numatts)
5494                 return tblInfo->attnames[attrnum - 1];
5495         switch (attrnum)
5496         {
5497                 case SelfItemPointerAttributeNumber:
5498                         return "ctid";
5499                 case ObjectIdAttributeNumber:
5500                         return "oid";
5501                 case MinTransactionIdAttributeNumber:
5502                         return "xmin";
5503                 case MinCommandIdAttributeNumber:
5504                         return "cmin";
5505                 case MaxTransactionIdAttributeNumber:
5506                         return "xmax";
5507                 case MaxCommandIdAttributeNumber:
5508                         return "cmax";
5509                 case TableOidAttributeNumber:
5510                         return "tableoid";
5511         }
5512         write_msg(NULL, "invalid column number %d for table \"%s\"\n",
5513                           attrnum, tblInfo->relname);
5514         exit_nicely();
5515         return NULL;                            /* keep compiler quiet */
5516 }
5517
5518 /*
5519  * dumpIndexes:
5520  *        write out to fout all the user-defined indexes for dumpable tables
5521  */
5522 void
5523 dumpIndexes(Archive *fout, TableInfo *tblinfo, int numTables)
5524 {
5525         int                     i,
5526                                 j;
5527         PQExpBuffer query = createPQExpBuffer();
5528         PQExpBuffer q = createPQExpBuffer();
5529         PQExpBuffer delq = createPQExpBuffer();
5530         PGresult   *res;
5531         int                     ntups;
5532         int                     i_indexreloid;
5533         int                     i_indexrelname;
5534         int                     i_indexdef;
5535         int                     i_contype;
5536         int                     i_conoid;
5537         int                     i_indkey;
5538         int                     i_indisclustered;
5539         int                     i_indnkeys;
5540
5541         for (i = 0; i < numTables; i++)
5542         {
5543                 TableInfo  *tbinfo = &tblinfo[i];
5544
5545                 /* Only plain tables have indexes */
5546                 if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex)
5547                         continue;
5548
5549                 if (!tbinfo->dump)
5550                         continue;
5551
5552                 /* Make sure we are in proper schema so indexdef is right */
5553                 selectSourceSchema(tbinfo->relnamespace->nspname);
5554
5555                 /*
5556                  * The point of the messy-looking outer join is to find a
5557                  * constraint that is related by an internal dependency link to
5558                  * the index. If we find one, we emit an ADD CONSTRAINT command
5559                  * instead of a CREATE INDEX command.  We assume an index won't
5560                  * have more than one internal dependency.
5561                  */
5562                 resetPQExpBuffer(query);
5563                 if (g_fout->remoteVersion >= 70300)
5564                         appendPQExpBuffer(query,
5565                                                           "SELECT i.indexrelid as indexreloid, "
5566                                            "coalesce(c.conname, t.relname) as indexrelname, "
5567                                  "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
5568                                                           "i.indkey, i.indisclustered, "
5569                                                           "t.relnatts as indnkeys, "
5570                                                           "coalesce(c.contype, '0') as contype, "
5571                                                           "coalesce(c.oid, '0') as conoid "
5572                                                           "FROM pg_catalog.pg_index i "
5573                                   "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
5574                                                           "LEFT JOIN pg_catalog.pg_depend d "
5575                                                           "ON (d.classid = t.tableoid "
5576                                                           "AND d.objid = t.oid "
5577                                                           "AND d.deptype = 'i') "
5578                                                           "LEFT JOIN pg_catalog.pg_constraint c "
5579                                                           "ON (d.refclassid = c.tableoid "
5580                                                           "AND d.refobjid = c.oid) "
5581                                                           "WHERE i.indrelid = '%s'::pg_catalog.oid "
5582                                                           "ORDER BY indexrelname",
5583                                                           tbinfo->oid);
5584                 else
5585                         appendPQExpBuffer(query,
5586                                                           "SELECT i.indexrelid as indexreloid, "
5587                                                           "t.relname as indexrelname, "
5588                                                         "pg_get_indexdef(i.indexrelid) as indexdef, "
5589                                                           "i.indkey, false as indisclustered, "
5590                                                           "t.relnatts as indnkeys, "
5591                                                           "CASE WHEN i.indisprimary THEN 'p'::char "
5592                                                           "ELSE '0'::char END as contype, "
5593                                                           "0::oid as conoid "
5594                                                           "FROM pg_index i, pg_class t "
5595                                                           "WHERE t.oid = i.indexrelid "
5596                                                           "AND i.indrelid = '%s'::oid "
5597                                                           "ORDER BY indexrelname",
5598                                                           tbinfo->oid);
5599
5600                 res = PQexec(g_conn, query->data);
5601                 if (!res ||
5602                         PQresultStatus(res) != PGRES_TUPLES_OK)
5603                 {
5604                         write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn));
5605                         exit_nicely();
5606                 }
5607
5608                 ntups = PQntuples(res);
5609
5610                 i_indexreloid = PQfnumber(res, "indexreloid");
5611                 i_indexrelname = PQfnumber(res, "indexrelname");
5612                 i_indexdef = PQfnumber(res, "indexdef");
5613                 i_contype = PQfnumber(res, "contype");
5614                 i_conoid = PQfnumber(res, "conoid");
5615                 i_indkey = PQfnumber(res, "indkey");
5616                 i_indisclustered = PQfnumber(res, "indisclustered");
5617                 i_indnkeys = PQfnumber(res, "indnkeys");
5618
5619                 for (j = 0; j < ntups; j++)
5620                 {
5621                         const char *indexreloid = PQgetvalue(res, j, i_indexreloid);
5622                         const char *indexrelname = PQgetvalue(res, j, i_indexrelname);
5623                         const char *indexdef = PQgetvalue(res, j, i_indexdef);
5624                         char            contype = *(PQgetvalue(res, j, i_contype));
5625                         const char *conoid = PQgetvalue(res, j, i_conoid);
5626                         bool            indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
5627
5628                         resetPQExpBuffer(q);
5629                         resetPQExpBuffer(delq);
5630
5631                         if (contype == 'p' || contype == 'u')
5632                         {
5633                                 /*
5634                                  * If we found a constraint matching the index, emit ADD
5635                                  * CONSTRAINT not CREATE INDEX.
5636                                  *
5637                                  * In a pre-7.3 database, we take this path iff the index was
5638                                  * marked indisprimary.
5639                                  */
5640                                 int                     indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
5641                                 char      **indkeys = (char **) malloc(indnkeys * sizeof(char *));
5642                                 int                     k;
5643
5644                                 parseNumericArray(PQgetvalue(res, j, i_indkey),
5645                                                                   indkeys, indnkeys);
5646
5647                                 appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
5648                                                                   fmtId(tbinfo->relname));
5649                                 appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s (",
5650                                                                   fmtId(indexrelname),
5651                                                           contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
5652
5653                                 for (k = 0; k < indnkeys; k++)
5654                                 {
5655                                         int                     indkey = atoi(indkeys[k]);
5656                                         const char *attname;
5657
5658                                         if (indkey == InvalidAttrNumber)
5659                                                 break;
5660                                         attname = getAttrName(indkey, tbinfo);
5661
5662                                         appendPQExpBuffer(q, "%s%s",
5663                                                                           (k == 0) ? "" : ", ",
5664                                                                           fmtId(attname));
5665                                 }
5666
5667                                 appendPQExpBuffer(q, ");\n");
5668
5669                                 /* If the index is clustered, we need to record that. */
5670                                 if (indisclustered)
5671                                 {
5672                                         appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
5673                                                                           fmtId(tbinfo->relname));
5674                                         appendPQExpBuffer(q, " ON %s;\n",
5675                                                                           fmtId(indexrelname));
5676                                 }
5677
5678                                 /*
5679                                  * DROP must be fully qualified in case same name appears
5680                                  * in pg_catalog
5681                                  */
5682                                 appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
5683                                                                   fmtId(tbinfo->relnamespace->nspname));
5684                                 appendPQExpBuffer(delq, "%s ",
5685                                                                   fmtId(tbinfo->relname));
5686                                 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
5687                                                                   fmtId(indexrelname));
5688
5689                                 ArchiveEntry(fout, indexreloid,
5690                                                          indexrelname,
5691                                                          tbinfo->relnamespace->nspname,
5692                                                          tbinfo->usename,
5693                                                          "CONSTRAINT", NULL,
5694                                                          q->data, delq->data,
5695                                                          NULL, NULL, NULL);
5696
5697                                 for (k = 0; k < indnkeys; k++)
5698                                         free(indkeys[k]);
5699                                 free(indkeys);
5700
5701                                 /* Dump Constraint Comments */
5702                                 resetPQExpBuffer(q);
5703                                 appendPQExpBuffer(q, "CONSTRAINT %s ",
5704                                                                   fmtId(indexrelname));
5705                                 appendPQExpBuffer(q, "ON %s",
5706                                                                   fmtId(tbinfo->relname));
5707                                 dumpComment(fout, q->data,
5708                                                         tbinfo->relnamespace->nspname,
5709                                                         tbinfo->usename,
5710                                                         conoid, "pg_constraint", 0, NULL);
5711                         }
5712                         else
5713                         {
5714                                 /* Plain secondary index */
5715                                 appendPQExpBuffer(q, "%s;\n", indexdef);
5716
5717                                 /* If the index is clustered, we need to record that. */
5718                                 if (indisclustered)
5719                                 {
5720                                         appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
5721                                                                           fmtId(tbinfo->relname));
5722                                         appendPQExpBuffer(q, " ON %s;\n",
5723                                                                           fmtId(indexrelname));
5724                                 }
5725
5726                                 /*
5727                                  * DROP must be fully qualified in case same name appears
5728                                  * in pg_catalog
5729                                  */
5730                                 appendPQExpBuffer(delq, "DROP INDEX %s.",
5731                                                                   fmtId(tbinfo->relnamespace->nspname));
5732                                 appendPQExpBuffer(delq, "%s;\n",
5733                                                                   fmtId(indexrelname));
5734
5735                                 ArchiveEntry(fout, indexreloid,
5736                                                          indexrelname,
5737                                                          tbinfo->relnamespace->nspname,
5738                                                          tbinfo->usename,
5739                                                          "INDEX", NULL,
5740                                                          q->data, delq->data,
5741                                                          NULL, NULL, NULL);
5742                         }
5743
5744                         /* Dump Index Comments */
5745                         resetPQExpBuffer(q);
5746                         appendPQExpBuffer(q, "INDEX %s",
5747                                                           fmtId(indexrelname));
5748                         dumpComment(fout, q->data,
5749                                                 tbinfo->relnamespace->nspname,
5750                                                 tbinfo->usename,
5751                                                 indexreloid, "pg_class", 0, NULL);
5752                 }
5753
5754                 PQclear(res);
5755         }
5756
5757         destroyPQExpBuffer(query);
5758         destroyPQExpBuffer(q);
5759         destroyPQExpBuffer(delq);
5760 }
5761
5762 /*
5763  * setMaxOid -
5764  * find the maximum oid and generate a COPY statement to set it
5765 */
5766
5767 static void
5768 setMaxOid(Archive *fout)
5769 {
5770         PGresult   *res;
5771         Oid                     max_oid;
5772         char            sql[1024];
5773
5774         res = PQexec(g_conn, "CREATE TEMPORARY TABLE pgdump_oid (dummy integer)");
5775         if (!res ||
5776                 PQresultStatus(res) != PGRES_COMMAND_OK)
5777         {
5778                 write_msg(NULL, "could not create pgdump_oid table: %s", PQerrorMessage(g_conn));
5779                 exit_nicely();
5780         }
5781         PQclear(res);
5782         res = PQexec(g_conn, "INSERT INTO pgdump_oid VALUES (0)");
5783         if (!res ||
5784                 PQresultStatus(res) != PGRES_COMMAND_OK)
5785         {
5786                 write_msg(NULL, "could not insert into pgdump_oid table: %s", PQerrorMessage(g_conn));
5787                 exit_nicely();
5788         }
5789         max_oid = PQoidValue(res);
5790         if (max_oid == 0)
5791         {
5792                 write_msg(NULL, "inserted invalid OID\n");
5793                 exit_nicely();
5794         }
5795         PQclear(res);
5796         res = PQexec(g_conn, "DROP TABLE pgdump_oid;");
5797         if (!res ||
5798                 PQresultStatus(res) != PGRES_COMMAND_OK)
5799         {
5800                 write_msg(NULL, "could not drop pgdump_oid table: %s", PQerrorMessage(g_conn));
5801                 exit_nicely();
5802         }
5803         PQclear(res);
5804         if (g_verbose)
5805                 write_msg(NULL, "maximum system OID is %u\n", max_oid);
5806         snprintf(sql, sizeof(sql),
5807                          "CREATE TEMPORARY TABLE pgdump_oid (dummy integer);\n"
5808                          "COPY pgdump_oid WITH OIDS FROM stdin;\n"
5809                          "%u\t0\n"
5810                          "\\.\n"
5811                          "DROP TABLE pgdump_oid;\n",
5812                          max_oid);
5813
5814         ArchiveEntry(fout, "0", "Max OID", NULL, "",
5815                                  "<Init>", NULL,
5816                                  sql, "",
5817                                  NULL, NULL, NULL);
5818 }
5819
5820 /*
5821  * findLastBuiltInOid -
5822  * find the last built in oid
5823  *
5824  * For 7.1 and 7.2, we do this by retrieving datlastsysoid from the
5825  * pg_database entry for the current database
5826  */
5827 static Oid
5828 findLastBuiltinOid_V71(const char *dbname)
5829 {
5830         PGresult   *res;
5831         int                     ntups;
5832         Oid                     last_oid;
5833         PQExpBuffer query = createPQExpBuffer();
5834
5835         resetPQExpBuffer(query);
5836         appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
5837         appendStringLiteral(query, dbname, true);
5838
5839         res = PQexec(g_conn, query->data);
5840         if (res == NULL ||
5841                 PQresultStatus(res) != PGRES_TUPLES_OK)
5842         {
5843                 write_msg(NULL, "error in finding the last system OID: %s", PQerrorMessage(g_conn));
5844                 exit_nicely();
5845         }
5846         ntups = PQntuples(res);
5847         if (ntups < 1)
5848         {
5849                 write_msg(NULL, "missing pg_database entry for this database\n");
5850                 exit_nicely();
5851         }
5852         if (ntups > 1)
5853         {
5854                 write_msg(NULL, "found more than one pg_database entry for this database\n");
5855                 exit_nicely();
5856         }
5857         last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
5858         PQclear(res);
5859         destroyPQExpBuffer(query);
5860         return last_oid;
5861 }
5862
5863 /*
5864  * findLastBuiltInOid -
5865  * find the last built in oid
5866  *
5867  * For 7.0, we do this by assuming that the last thing that initdb does is to
5868  * create the pg_indexes view.  This sucks in general, but seeing that 7.0.x
5869  * initdb won't be changing anymore, it'll do.
5870  */
5871 static Oid
5872 findLastBuiltinOid_V70(void)
5873 {
5874         PGresult   *res;
5875         int                     ntups;
5876         int                     last_oid;
5877
5878         res = PQexec(g_conn,
5879                                  "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'");
5880         if (res == NULL ||
5881                 PQresultStatus(res) != PGRES_TUPLES_OK)
5882         {
5883                 write_msg(NULL, "error in finding the template1 database: %s", PQerrorMessage(g_conn));
5884                 exit_nicely();
5885         }
5886         ntups = PQntuples(res);
5887         if (ntups < 1)
5888         {
5889                 write_msg(NULL, "could not find entry for database template1 in table pg_database\n");
5890                 exit_nicely();
5891         }
5892         if (ntups > 1)
5893         {
5894                 write_msg(NULL, "found more than one entry for database template1 in table pg_database\n");
5895                 exit_nicely();
5896         }
5897         last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5898         PQclear(res);
5899         return last_oid;
5900 }
5901
5902 static void
5903 dumpOneSequence(Archive *fout, TableInfo *tbinfo,
5904                                 const bool schemaOnly, const bool dataOnly)
5905 {
5906         PGresult   *res;
5907         char       *last,
5908                            *incby,
5909                            *maxv = NULL,
5910                            *minv = NULL,
5911                            *cache;
5912         char            bufm[100],
5913                                 bufx[100];
5914         bool            cycled,
5915                                 called;
5916         PQExpBuffer query = createPQExpBuffer();
5917         PQExpBuffer delqry = createPQExpBuffer();
5918
5919         /* Make sure we are in proper schema */
5920         selectSourceSchema(tbinfo->relnamespace->nspname);
5921
5922         snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
5923         snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
5924
5925         appendPQExpBuffer(query,
5926                                           "SELECT sequence_name, last_value, increment_by, "
5927                            "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
5928                            "     WHEN increment_by < 0 AND max_value = -1 THEN NULL "
5929                                           "     ELSE max_value "
5930                                           "END AS max_value, "
5931                                 "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
5932                            "     WHEN increment_by < 0 AND min_value = %s THEN NULL "
5933                                           "     ELSE min_value "
5934                                           "END AS min_value, "
5935                                           "cache_value, is_cycled, is_called from %s",
5936                                           bufx, bufm,
5937                                           fmtId(tbinfo->relname));
5938
5939         res = PQexec(g_conn, query->data);
5940         if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
5941         {
5942                 write_msg(NULL, "query to get data of sequence \"%s\" failed: %s", tbinfo->relname, PQerrorMessage(g_conn));
5943                 exit_nicely();
5944         }
5945
5946         if (PQntuples(res) != 1)
5947         {
5948                 write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
5949                                   tbinfo->relname, PQntuples(res));
5950                 exit_nicely();
5951         }
5952
5953         /* Disable this check: it fails if sequence has been renamed */
5954 #ifdef NOT_USED
5955         if (strcmp(PQgetvalue(res, 0, 0), tbinfo->relname) != 0)
5956         {
5957                 write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
5958                                   tbinfo->relname, PQgetvalue(res, 0, 0));
5959                 exit_nicely();
5960         }
5961 #endif
5962
5963         last = PQgetvalue(res, 0, 1);
5964         incby = PQgetvalue(res, 0, 2);
5965         if (!PQgetisnull(res, 0, 3))
5966                 maxv = PQgetvalue(res, 0, 3);
5967         if (!PQgetisnull(res, 0, 4))
5968                 minv = PQgetvalue(res, 0, 4);
5969         cache = PQgetvalue(res, 0, 5);
5970         cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
5971         called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
5972
5973         /*
5974          * The logic we use for restoring sequences is as follows:
5975          *
5976          * Add a basic CREATE SEQUENCE statement (use last_val for start if
5977          * called is false, else use min_val for start_val).  Skip this if the
5978          * sequence came from a SERIAL column.
5979          *
5980          * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
5981          * data.  We do this for serial sequences too.
5982          */
5983
5984         if (!dataOnly && tbinfo->owning_tab == NULL)
5985         {
5986                 resetPQExpBuffer(delqry);
5987
5988                 /*
5989                  * DROP must be fully qualified in case same name appears in
5990                  * pg_catalog
5991                  */
5992                 appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
5993                                                   fmtId(tbinfo->relnamespace->nspname));
5994                 appendPQExpBuffer(delqry, "%s;\n",
5995                                                   fmtId(tbinfo->relname));
5996
5997                 resetPQExpBuffer(query);
5998                 appendPQExpBuffer(query,
5999                                                   "CREATE SEQUENCE %s\n",
6000                                                   fmtId(tbinfo->relname));
6001
6002                 if (!called)
6003                         appendPQExpBuffer(query, "    START WITH %s\n", last);
6004
6005                 appendPQExpBuffer(query, "    INCREMENT BY %s\n", incby);
6006
6007                 if (maxv)
6008                         appendPQExpBuffer(query, "    MAXVALUE %s\n", maxv);
6009                 else
6010                         appendPQExpBuffer(query, "    NO MAXVALUE\n");
6011
6012                 if (minv)
6013                         appendPQExpBuffer(query, "    MINVALUE %s\n", minv);
6014                 else
6015                         appendPQExpBuffer(query, "    NO MINVALUE\n");
6016
6017                 appendPQExpBuffer(query,
6018                                                   "    CACHE %s%s;\n",
6019                                                   cache, (cycled ? "\n    CYCLE" : ""));
6020
6021                 ArchiveEntry(fout, tbinfo->oid, tbinfo->relname,
6022                                          tbinfo->relnamespace->nspname, tbinfo->usename,
6023                                          "SEQUENCE", NULL,
6024                                          query->data, delqry->data,
6025                                          NULL, NULL, NULL);
6026         }
6027
6028         if (!schemaOnly)
6029         {
6030                 resetPQExpBuffer(query);
6031                 appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
6032                 appendStringLiteral(query, fmtId(tbinfo->relname), true);
6033                 appendPQExpBuffer(query, ", %s, %s);\n",
6034                                                   last, (called ? "true" : "false"));
6035
6036                 ArchiveEntry(fout, tbinfo->oid, tbinfo->relname,
6037                                          tbinfo->relnamespace->nspname, tbinfo->usename,
6038                                          "SEQUENCE SET", NULL,
6039                                          query->data, "" /* Del */ ,
6040                                          NULL, NULL, NULL);
6041         }
6042
6043         if (!dataOnly)
6044         {
6045                 /* Dump Sequence Comments */
6046
6047                 resetPQExpBuffer(query);
6048                 appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->relname));
6049                 dumpComment(fout, query->data,
6050                                         tbinfo->relnamespace->nspname, tbinfo->usename,
6051                                         tbinfo->oid, "pg_class", 0, NULL);
6052         }
6053
6054         PQclear(res);
6055
6056         destroyPQExpBuffer(query);
6057         destroyPQExpBuffer(delqry);
6058 }
6059
6060 /*
6061  * dumpConstraints
6062  *
6063  * Dump out constraints after all table creation statements in
6064  * an alter table format.  Currently handles foreign keys only.
6065  * (Unique and primary key constraints are handled with indexes,
6066  * while check constraints are merged into the table definition.)
6067  *
6068  * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that
6069  * the current table data is not processed
6070  */
6071 static void
6072 dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables)
6073 {
6074         int                     i,
6075                                 j;
6076         PQExpBuffer query;
6077         PQExpBuffer delqry;
6078         PGresult   *res;
6079         int                     i_condef,
6080                                 i_conoid,
6081                                 i_conname;
6082         int                     ntups;
6083
6084         /* pg_constraint was created in 7.3, so nothing to do if older */
6085         if (g_fout->remoteVersion < 70300)
6086                 return;
6087
6088         query = createPQExpBuffer();
6089         delqry = createPQExpBuffer();
6090
6091         for (i = 0; i < numTables; i++)
6092         {
6093                 TableInfo  *tbinfo = &tblinfo[i];
6094
6095                 if (tbinfo->ntrig == 0 || !tbinfo->dump)
6096                         continue;
6097
6098                 if (g_verbose)
6099                         write_msg(NULL, "dumping foreign key constraints for table \"%s\"\n",
6100                                           tbinfo->relname);
6101
6102                 /*
6103                  * select table schema to ensure regproc name is qualified if
6104                  * needed
6105                  */
6106                 selectSourceSchema(tbinfo->relnamespace->nspname);
6107
6108                 resetPQExpBuffer(query);
6109                 appendPQExpBuffer(query,
6110                                                   "SELECT oid, conname, "
6111                                                 "pg_catalog.pg_get_constraintdef(oid) as condef "
6112                                                   "FROM pg_catalog.pg_constraint "
6113                                                   "WHERE conrelid = '%s'::pg_catalog.oid "
6114                                                   "AND contype = 'f'",
6115                                                   tbinfo->oid);
6116                 res = PQexec(g_conn, query->data);
6117                 if (!res ||
6118                         PQresultStatus(res) != PGRES_TUPLES_OK)
6119                 {
6120                         write_msg(NULL, "query to obtain list of foreign key definitions failed: %s", PQerrorMessage(g_conn));
6121                         exit_nicely();
6122                 }
6123                 ntups = PQntuples(res);
6124
6125                 i_conoid = PQfnumber(res, "oid");
6126                 i_conname = PQfnumber(res, "conname");
6127                 i_condef = PQfnumber(res, "condef");
6128
6129                 for (j = 0; j < ntups; j++)
6130                 {
6131                         const char *conOid = PQgetvalue(res, j, i_conoid);
6132                         const char *conName = PQgetvalue(res, j, i_conname);
6133                         const char *conDef = PQgetvalue(res, j, i_condef);
6134
6135                         resetPQExpBuffer(query);
6136                         appendPQExpBuffer(query, "ALTER TABLE ONLY %s\n",
6137                                                           fmtId(tbinfo->relname));
6138                         appendPQExpBuffer(query, "    ADD CONSTRAINT %s %s;\n",
6139                                                           fmtId(conName),
6140                                                           conDef);
6141
6142                         /*
6143                          * DROP must be fully qualified in case same name appears in
6144                          * pg_catalog
6145                          */
6146                         resetPQExpBuffer(delqry);
6147                         appendPQExpBuffer(delqry, "ALTER TABLE ONLY %s.",
6148                                                           fmtId(tbinfo->relnamespace->nspname));
6149                         appendPQExpBuffer(delqry, "%s ",
6150                                                           fmtId(tbinfo->relname));
6151                         appendPQExpBuffer(delqry, "DROP CONSTRAINT %s;\n",
6152                                                           fmtId(conName));
6153
6154                         ArchiveEntry(fout, conOid,
6155                                                  conName,
6156                                                  tbinfo->relnamespace->nspname,
6157                                                  tbinfo->usename,
6158                                                  "FK CONSTRAINT", NULL,
6159                                                  query->data, delqry->data,
6160                                                  NULL, NULL, NULL);
6161
6162                         resetPQExpBuffer(query);
6163                         appendPQExpBuffer(query, "CONSTRAINT %s ",
6164                                                           fmtId(conName));
6165                         appendPQExpBuffer(query, "ON %s",
6166                                                           fmtId(tbinfo->relname));
6167
6168                         dumpComment(fout, query->data,
6169                                                 tbinfo->relnamespace->nspname, tbinfo->usename,
6170                                                 conOid, "pg_constraint", 0, NULL);
6171                 }
6172
6173                 PQclear(res);
6174         }
6175
6176         destroyPQExpBuffer(query);
6177         destroyPQExpBuffer(delqry);
6178 }
6179
6180 static void
6181 dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables)
6182 {
6183         int                     i,
6184                                 j;
6185         PQExpBuffer query = createPQExpBuffer();
6186         PQExpBuffer delqry = createPQExpBuffer();
6187         PGresult   *res;
6188         int                     i_tgoid,
6189                                 i_tgname,
6190                                 i_tgfname,
6191                                 i_tgtype,
6192                                 i_tgnargs,
6193                                 i_tgargs,
6194                                 i_tgisconstraint,
6195                                 i_tgconstrname,
6196                                 i_tgdeferrable,
6197                                 i_tgconstrrelid,
6198                                 i_tgconstrrelname,
6199                                 i_tginitdeferred;
6200         int                     ntups;
6201
6202         for (i = 0; i < numTables; i++)
6203         {
6204                 TableInfo  *tbinfo = &tblinfo[i];
6205
6206                 if (tbinfo->ntrig == 0 || !tbinfo->dump)
6207                         continue;
6208
6209                 if (g_verbose)
6210                         write_msg(NULL, "dumping triggers for table \"%s\"\n",
6211                                           tbinfo->relname);
6212
6213                 /*
6214                  * select table schema to ensure regproc name is qualified if
6215                  * needed
6216                  */
6217                 selectSourceSchema(tbinfo->relnamespace->nspname);
6218
6219                 resetPQExpBuffer(query);
6220                 if (g_fout->remoteVersion >= 70300)
6221                 {
6222                         /*
6223                          * We ignore triggers that are tied to a foreign-key
6224                          * constraint
6225                          */
6226                         appendPQExpBuffer(query,
6227                                                           "SELECT tgname, "
6228                                                           "tgfoid::pg_catalog.regproc as tgfname, "
6229                                                           "tgtype, tgnargs, tgargs, "
6230                                                    "tgisconstraint, tgconstrname, tgdeferrable, "
6231                                                           "tgconstrrelid, tginitdeferred, oid, "
6232                                  "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
6233                                                           "from pg_catalog.pg_trigger t "
6234                                                           "where tgrelid = '%s'::pg_catalog.oid "
6235                                                           "and (not tgisconstraint "
6236                                                           " OR NOT EXISTS"
6237                                                           "  (SELECT 1 FROM pg_catalog.pg_depend d "
6238                                                           "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
6239                                                           "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
6240                                                           tbinfo->oid);
6241                 }
6242                 else
6243                 {
6244                         appendPQExpBuffer(query,
6245                                                         "SELECT tgname, tgfoid::regproc as tgfname, "
6246                                                           "tgtype, tgnargs, tgargs, "
6247                                                    "tgisconstraint, tgconstrname, tgdeferrable, "
6248                                                           "tgconstrrelid, tginitdeferred, oid, "
6249                           "(select relname from pg_class where oid = tgconstrrelid) "
6250                                                           "             as tgconstrrelname "
6251                                                           "from pg_trigger "
6252                                                           "where tgrelid = '%s'::oid",
6253                                                           tbinfo->oid);
6254                 }
6255                 res = PQexec(g_conn, query->data);
6256                 if (!res ||
6257                         PQresultStatus(res) != PGRES_TUPLES_OK)
6258                 {
6259                         write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn));
6260                         exit_nicely();
6261                 }
6262                 ntups = PQntuples(res);
6263
6264                 /*
6265                  * We may have less triggers than recorded due to constraint
6266                  * triggers which are dumped by dumpConstraints
6267                  */
6268                 if (ntups > tbinfo->ntrig)
6269                 {
6270                         write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
6271                                           tbinfo->ntrig, tbinfo->relname, ntups);
6272                         exit_nicely();
6273                 }
6274                 i_tgname = PQfnumber(res, "tgname");
6275                 i_tgfname = PQfnumber(res, "tgfname");
6276                 i_tgtype = PQfnumber(res, "tgtype");
6277                 i_tgnargs = PQfnumber(res, "tgnargs");
6278                 i_tgargs = PQfnumber(res, "tgargs");
6279                 i_tgoid = PQfnumber(res, "oid");
6280                 i_tgisconstraint = PQfnumber(res, "tgisconstraint");
6281                 i_tgconstrname = PQfnumber(res, "tgconstrname");
6282                 i_tgdeferrable = PQfnumber(res, "tgdeferrable");
6283                 i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
6284                 i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
6285                 i_tginitdeferred = PQfnumber(res, "tginitdeferred");
6286
6287                 for (j = 0; j < ntups; j++)
6288                 {
6289                         const char *tgoid = PQgetvalue(res, j, i_tgoid);
6290                         char       *tgname = PQgetvalue(res, j, i_tgname);
6291                         const char *tgfname = PQgetvalue(res, j, i_tgfname);
6292                         int2            tgtype = atoi(PQgetvalue(res, j, i_tgtype));
6293                         int                     tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
6294                         const char *tgargs = PQgetvalue(res, j, i_tgargs);
6295                         int                     tgisconstraint;
6296                         int                     tgdeferrable;
6297                         int                     tginitdeferred;
6298                         char       *tgconstrrelid;
6299                         const char *p;
6300                         int                     findx;
6301
6302                         if (strcmp(PQgetvalue(res, j, i_tgisconstraint), "f") == 0)
6303                                 tgisconstraint = 0;
6304                         else
6305                                 tgisconstraint = 1;
6306
6307                         if (strcmp(PQgetvalue(res, j, i_tgdeferrable), "f") == 0)
6308                                 tgdeferrable = 0;
6309                         else
6310                                 tgdeferrable = 1;
6311
6312                         if (strcmp(PQgetvalue(res, j, i_tginitdeferred), "f") == 0)
6313                                 tginitdeferred = 0;
6314                         else
6315                                 tginitdeferred = 1;
6316
6317                         resetPQExpBuffer(delqry);
6318
6319                         /*
6320                          * DROP must be fully qualified in case same name appears in
6321                          * pg_catalog
6322                          */
6323                         appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
6324                                                           fmtId(tgname));
6325                         appendPQExpBuffer(delqry, "ON %s.",
6326                                                           fmtId(tbinfo->relnamespace->nspname));
6327                         appendPQExpBuffer(delqry, "%s;\n",
6328                                                           fmtId(tbinfo->relname));
6329
6330                         resetPQExpBuffer(query);
6331                         if (tgisconstraint)
6332                         {
6333                                 appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
6334                                 appendPQExpBuffer(query, fmtId(PQgetvalue(res, j, i_tgconstrname)));
6335                         }
6336                         else
6337                         {
6338                                 appendPQExpBuffer(query, "CREATE TRIGGER ");
6339                                 appendPQExpBuffer(query, fmtId(tgname));
6340                         }
6341                         appendPQExpBuffer(query, "\n    ");
6342                         /* Trigger type */
6343                         findx = 0;
6344                         if (TRIGGER_FOR_BEFORE(tgtype))
6345                                 appendPQExpBuffer(query, "BEFORE");
6346                         else
6347                                 appendPQExpBuffer(query, "AFTER");
6348                         if (TRIGGER_FOR_INSERT(tgtype))
6349                         {
6350                                 appendPQExpBuffer(query, " INSERT");
6351                                 findx++;
6352                         }
6353                         if (TRIGGER_FOR_DELETE(tgtype))
6354                         {
6355                                 if (findx > 0)
6356                                         appendPQExpBuffer(query, " OR DELETE");
6357                                 else
6358                                         appendPQExpBuffer(query, " DELETE");
6359                                 findx++;
6360                         }
6361                         if (TRIGGER_FOR_UPDATE(tgtype))
6362                         {
6363                                 if (findx > 0)
6364                                         appendPQExpBuffer(query, " OR UPDATE");
6365                                 else
6366                                         appendPQExpBuffer(query, " UPDATE");
6367                         }
6368                         appendPQExpBuffer(query, " ON %s\n",
6369                                                           fmtId(tbinfo->relname));
6370
6371                         if (tgisconstraint)
6372                         {
6373                                 tgconstrrelid = PQgetvalue(res, j, i_tgconstrrelid);
6374
6375                                 if (strcmp(tgconstrrelid, "0") != 0)
6376                                 {
6377
6378                                         if (PQgetisnull(res, j, i_tgconstrrelname))
6379                                         {
6380                                                 write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %s)\n",
6381                                                                   tgname, tbinfo->relname, tgconstrrelid);
6382                                                 exit_nicely();
6383                                         }
6384
6385                                         /* If we are using regclass, name is already quoted */
6386                                         if (g_fout->remoteVersion >= 70300)
6387                                                 appendPQExpBuffer(query, "    FROM %s\n    ",
6388                                                                   PQgetvalue(res, j, i_tgconstrrelname));
6389                                         else
6390                                                 appendPQExpBuffer(query, "    FROM %s\n    ",
6391                                                    fmtId(PQgetvalue(res, j, i_tgconstrrelname)));
6392                                 }
6393                                 if (!tgdeferrable)
6394                                         appendPQExpBuffer(query, "NOT ");
6395                                 appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
6396                                 if (tginitdeferred)
6397                                         appendPQExpBuffer(query, "DEFERRED\n");
6398                                 else
6399                                         appendPQExpBuffer(query, "IMMEDIATE\n");
6400
6401                         }
6402
6403                         if (TRIGGER_FOR_ROW(tgtype))
6404                                 appendPQExpBuffer(query, "    FOR EACH ROW\n    ");
6405                         else
6406                                 appendPQExpBuffer(query, "    FOR EACH STATEMENT\n    ");
6407
6408                         /* In 7.3, result of regproc is already quoted */
6409                         if (g_fout->remoteVersion >= 70300)
6410                                 appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
6411                                                                   tgfname);
6412                         else
6413                                 appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
6414                                                                   fmtId(tgfname));
6415                         for (findx = 0; findx < tgnargs; findx++)
6416                         {
6417                                 const char *s;
6418
6419                                 for (p = tgargs;;)
6420                                 {
6421                                         p = strchr(p, '\\');
6422                                         if (p == NULL)
6423                                         {
6424                                                 write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
6425                                                                   PQgetvalue(res, j, i_tgargs),
6426                                                                   tgname,
6427                                                                   tbinfo->relname);
6428                                                 exit_nicely();
6429                                         }
6430                                         p++;
6431                                         if (*p == '\\')
6432                                         {
6433                                                 p++;
6434                                                 continue;
6435                                         }
6436                                         if (p[0] == '0' && p[1] == '0' && p[2] == '0')
6437                                                 break;
6438                                 }
6439                                 p--;
6440                                 appendPQExpBufferChar(query, '\'');
6441                                 for (s = tgargs; s < p;)
6442                                 {
6443                                         if (*s == '\'')
6444                                                 appendPQExpBufferChar(query, '\\');
6445                                         appendPQExpBufferChar(query, *s++);
6446                                 }
6447                                 appendPQExpBufferChar(query, '\'');
6448                                 appendPQExpBuffer(query, (findx < tgnargs - 1) ? ", " : "");
6449                                 tgargs = p + 4;
6450                         }
6451                         appendPQExpBuffer(query, ");\n");
6452
6453                         ArchiveEntry(fout, tgoid,
6454                                                  tgname,
6455                                                  tbinfo->relnamespace->nspname,
6456                                                  tbinfo->usename,
6457                                                  "TRIGGER", NULL,
6458                                                  query->data, delqry->data,
6459                                                  NULL, NULL, NULL);
6460
6461                         resetPQExpBuffer(query);
6462                         appendPQExpBuffer(query, "TRIGGER %s ",
6463                                                           fmtId(tgname));
6464                         appendPQExpBuffer(query, "ON %s",
6465                                                           fmtId(tbinfo->relname));
6466
6467                         dumpComment(fout, query->data,
6468                                                 tbinfo->relnamespace->nspname, tbinfo->usename,
6469                                                 tgoid, "pg_trigger", 0, NULL);
6470                 }
6471
6472                 PQclear(res);
6473         }
6474
6475         destroyPQExpBuffer(query);
6476         destroyPQExpBuffer(delqry);
6477 }
6478
6479
6480 static void
6481 dumpRules(Archive *fout, TableInfo *tblinfo, int numTables)
6482 {
6483         PGresult   *res;
6484         int                     nrules;
6485         int                     i,
6486                                 t;
6487         PQExpBuffer query = createPQExpBuffer();
6488         PQExpBuffer cmd = createPQExpBuffer();
6489         int                     i_definition;
6490         int                     i_oid;
6491         int                     i_rulename;
6492
6493         if (g_verbose)
6494                 write_msg(NULL, "dumping out rules\n");
6495
6496         /*
6497          * For each table we dump
6498          */
6499         for (t = 0; t < numTables; t++)
6500         {
6501                 TableInfo  *tbinfo = &tblinfo[t];
6502
6503                 if (!tbinfo->hasrules || !tbinfo->dump)
6504                         continue;
6505
6506                 /* Make sure we are in proper schema */
6507                 selectSourceSchema(tbinfo->relnamespace->nspname);
6508
6509                 /*
6510                  * Get all rules defined for this table, except view select rules
6511                  */
6512                 resetPQExpBuffer(query);
6513
6514                 if (g_fout->remoteVersion >= 70300)
6515                 {
6516                         appendPQExpBuffer(query,
6517                                    "SELECT pg_catalog.pg_get_ruledef(oid) AS definition,"
6518                                                           " oid, rulename "
6519                                                           "FROM pg_catalog.pg_rewrite "
6520                                                           "WHERE ev_class = '%s'::pg_catalog.oid "
6521                                                           "AND rulename != '_RETURN' "
6522                                                           "ORDER BY oid",
6523                                                           tbinfo->oid);
6524                 }
6525                 else
6526                 {
6527                         /*
6528                          * We include pg_rules in the cross since it filters out all
6529                          * view rules (pjw 15-Sep-2000).
6530                          */
6531                         appendPQExpBuffer(query, "SELECT definition,"
6532                                                           "   pg_rewrite.oid, pg_rewrite.rulename "
6533                                                           "FROM pg_rewrite, pg_class, pg_rules "
6534                                                           "WHERE pg_class.relname = ");
6535                         appendStringLiteral(query, tbinfo->relname, true);
6536                         appendPQExpBuffer(query,
6537                                                         "    AND pg_rewrite.ev_class = pg_class.oid "
6538                                                  "    AND pg_rules.tablename = pg_class.relname "
6539                                            "    AND pg_rules.rulename = pg_rewrite.rulename "
6540                                                           "ORDER BY pg_rewrite.oid");
6541                 }
6542
6543                 res = PQexec(g_conn, query->data);
6544                 if (!res ||
6545                         PQresultStatus(res) != PGRES_TUPLES_OK)
6546                 {
6547                         write_msg(NULL, "query to get rules associated with table \"%s\" failed: %s",
6548                                           tbinfo->relname, PQerrorMessage(g_conn));
6549                         exit_nicely();
6550                 }
6551
6552                 nrules = PQntuples(res);
6553                 i_definition = PQfnumber(res, "definition");
6554                 i_oid = PQfnumber(res, "oid");
6555                 i_rulename = PQfnumber(res, "rulename");
6556
6557                 /*
6558                  * Dump them out
6559                  */
6560
6561                 for (i = 0; i < nrules; i++)
6562                 {
6563                         printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, i, i_definition));
6564                         ArchiveEntry(fout, PQgetvalue(res, i, i_oid),
6565                                                  PQgetvalue(res, i, i_rulename),
6566                                                  tbinfo->relnamespace->nspname,
6567                                                  tbinfo->usename,
6568                                                  "RULE", NULL,
6569                                                  cmd->data,
6570                                                  "",    /* Del */
6571                                                  NULL, NULL, NULL);
6572
6573                         /* Dump rule comments */
6574
6575                         resetPQExpBuffer(query);
6576                         appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename)));
6577                         appendPQExpBuffer(query, " ON %s", fmtId(tbinfo->relname));
6578                         dumpComment(fout, query->data,
6579                                                 tbinfo->relnamespace->nspname,
6580                                                 tbinfo->usename,
6581                                                 PQgetvalue(res, i, i_oid), "pg_rewrite", 0, NULL);
6582
6583                 }
6584
6585                 PQclear(res);
6586         }
6587
6588         destroyPQExpBuffer(query);
6589         destroyPQExpBuffer(cmd);
6590 }
6591
6592 /*
6593  * selectSourceSchema - make the specified schema the active search path
6594  * in the source database.
6595  *
6596  * NB: pg_catalog is explicitly searched after the specified schema;
6597  * so user names are only qualified if they are cross-schema references,
6598  * and system names are only qualified if they conflict with a user name
6599  * in the current schema.
6600  *
6601  * Whenever the selected schema is not pg_catalog, be careful to qualify
6602  * references to system catalogs and types in our emitted commands!
6603  */
6604 static void
6605 selectSourceSchema(const char *schemaName)
6606 {
6607         static char *curSchemaName = NULL;
6608         PQExpBuffer query;
6609         PGresult   *res;
6610
6611         /* Not relevant if fetching from pre-7.3 DB */
6612         if (g_fout->remoteVersion < 70300)
6613                 return;
6614         /* Ignore null schema names */
6615         if (schemaName == NULL || *schemaName == '\0')
6616                 return;
6617         /* Optimize away repeated selection of same schema */
6618         if (curSchemaName && strcmp(curSchemaName, schemaName) == 0)
6619                 return;
6620
6621         query = createPQExpBuffer();
6622         appendPQExpBuffer(query, "SET search_path = %s",
6623                                           fmtId(schemaName));
6624         if (strcmp(schemaName, "pg_catalog") != 0)
6625                 appendPQExpBuffer(query, ", pg_catalog");
6626         res = PQexec(g_conn, query->data);
6627         if (!res ||
6628                 PQresultStatus(res) != PGRES_COMMAND_OK)
6629         {
6630                 write_msg(NULL, "command to set search_path failed: %s",
6631                                   PQerrorMessage(g_conn));
6632                 exit_nicely();
6633         }
6634         PQclear(res);
6635         destroyPQExpBuffer(query);
6636
6637         if (curSchemaName)
6638                 free(curSchemaName);
6639         curSchemaName = strdup(schemaName);
6640 }
6641
6642 /*
6643  * getFormattedTypeName - retrieve a nicely-formatted type name for the
6644  * given type name.
6645  *
6646  * NB: in 7.3 and up the result may depend on the currently-selected
6647  * schema; this is why we don't try to cache the names.
6648  */
6649 static char *
6650 getFormattedTypeName(const char *oid, OidOptions opts)
6651 {
6652         char       *result;
6653         PQExpBuffer query;
6654         PGresult   *res;
6655         int                     ntups;
6656
6657         if (atooid(oid) == 0)
6658         {
6659                 if ((opts & zeroAsOpaque) != 0)
6660                         return strdup(g_opaque_type);
6661                 else if ((opts & zeroAsAny) != 0)
6662                         return strdup("'any'");
6663                 else if ((opts & zeroAsStar) != 0)
6664                         return strdup("*");
6665                 else if ((opts & zeroAsNone) != 0)
6666                         return strdup("NONE");
6667         }
6668
6669         query = createPQExpBuffer();
6670         if (g_fout->remoteVersion >= 70300)
6671         {
6672                 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%s'::pg_catalog.oid, NULL)",
6673                                                   oid);
6674         }
6675         else if (g_fout->remoteVersion >= 70100)
6676         {
6677                 appendPQExpBuffer(query, "SELECT format_type('%s'::oid, NULL)",
6678                                                   oid);
6679         }
6680         else
6681         {
6682                 appendPQExpBuffer(query, "SELECT typname "
6683                                                   "FROM pg_type "
6684                                                   "WHERE oid = '%s'::oid",
6685                                                   oid);
6686         }
6687
6688         res = PQexec(g_conn, query->data);
6689         if (!res ||
6690                 PQresultStatus(res) != PGRES_TUPLES_OK)
6691         {
6692                 write_msg(NULL, "query to obtain name of data type %s failed: %s",
6693                                   oid, PQerrorMessage(g_conn));
6694                 exit_nicely();
6695         }
6696
6697         /* Expecting a single result only */
6698         ntups = PQntuples(res);
6699         if (ntups != 1)
6700         {
6701                 write_msg(NULL, "query yielded %d rows instead of one: %s\n",
6702                                   ntups, query->data);
6703                 exit_nicely();
6704         }
6705
6706         if (g_fout->remoteVersion >= 70100)
6707         {
6708                 /* already quoted */
6709                 result = strdup(PQgetvalue(res, 0, 0));
6710         }
6711         else
6712         {
6713                 /* may need to quote it */
6714                 result = strdup(fmtId(PQgetvalue(res, 0, 0)));
6715         }
6716
6717         PQclear(res);
6718         destroyPQExpBuffer(query);
6719
6720         return result;
6721 }
6722
6723 /*
6724  * myFormatType --- local implementation of format_type for use with 7.0.
6725  */
6726 static char *
6727 myFormatType(const char *typname, int32 typmod)
6728 {
6729         char       *result;
6730         PQExpBuffer buf = createPQExpBuffer();
6731
6732         /* Show lengths on bpchar and varchar */
6733         if (!strcmp(typname, "bpchar"))
6734         {
6735                 int                     len = (typmod - VARHDRSZ);
6736
6737                 appendPQExpBuffer(buf, "character");
6738                 if (len > 1)
6739                         appendPQExpBuffer(buf, "(%d)",
6740                                                           typmod - VARHDRSZ);
6741         }
6742         else if (!strcmp(typname, "varchar"))
6743         {
6744                 appendPQExpBuffer(buf, "character varying");
6745                 if (typmod != -1)
6746                         appendPQExpBuffer(buf, "(%d)",
6747                                                           typmod - VARHDRSZ);
6748         }
6749         else if (!strcmp(typname, "numeric"))
6750         {
6751                 appendPQExpBuffer(buf, "numeric");
6752                 if (typmod != -1)
6753                 {
6754                         int32           tmp_typmod;
6755                         int                     precision;
6756                         int                     scale;
6757
6758                         tmp_typmod = typmod - VARHDRSZ;
6759                         precision = (tmp_typmod >> 16) & 0xffff;
6760                         scale = tmp_typmod & 0xffff;
6761                         appendPQExpBuffer(buf, "(%d,%d)",
6762                                                           precision, scale);
6763                 }
6764         }
6765
6766         /*
6767          * char is an internal single-byte data type; Let's make sure we force
6768          * it through with quotes. - thomas 1998-12-13
6769          */
6770         else if (strcmp(typname, "char") == 0)
6771                 appendPQExpBuffer(buf, "\"char\"");
6772         else
6773                 appendPQExpBuffer(buf, "%s", fmtId(typname));
6774
6775         result = strdup(buf->data);
6776         destroyPQExpBuffer(buf);
6777
6778         return result;
6779 }
6780
6781 /*
6782  * fmtQualifiedId - convert a qualified name to the proper format for
6783  * the source database.
6784  *
6785  * Like fmtId, use the result before calling again.
6786  */
6787 static const char *
6788 fmtQualifiedId(const char *schema, const char *id)
6789 {
6790         static PQExpBuffer id_return = NULL;
6791
6792         if (id_return)                          /* first time through? */
6793                 resetPQExpBuffer(id_return);
6794         else
6795                 id_return = createPQExpBuffer();
6796
6797         /* Suppress schema name if fetching from pre-7.3 DB */
6798         if (g_fout->remoteVersion >= 70300 && schema && *schema)
6799         {
6800                 appendPQExpBuffer(id_return, "%s.",
6801                                                   fmtId(schema));
6802         }
6803         appendPQExpBuffer(id_return, "%s",
6804                                           fmtId(id));
6805
6806         return id_return->data;
6807 }
6808
6809 /*
6810  * Return a column list clause for the given relation.
6811  *
6812  * Special case: if there are no undropped columns in the relation, return
6813  * "", not an invalid "()" column list.
6814  */
6815 static const char *
6816 fmtCopyColumnList(const TableInfo *ti)
6817 {
6818         static PQExpBuffer q = NULL;
6819         int                     numatts = ti->numatts;
6820         char      **attnames = ti->attnames;
6821         bool       *attisdropped = ti->attisdropped;
6822         bool            needComma;
6823         int                     i;
6824
6825         if (q)                                          /* first time through? */
6826                 resetPQExpBuffer(q);
6827         else
6828                 q = createPQExpBuffer();
6829
6830         appendPQExpBuffer(q, "(");
6831         needComma = false;
6832         for (i = 0; i < numatts; i++)
6833         {
6834                 if (attisdropped[i])
6835                         continue;
6836                 if (needComma)
6837                         appendPQExpBuffer(q, ", ");
6838                 appendPQExpBuffer(q, "%s", fmtId(attnames[i]));
6839                 needComma = true;
6840         }
6841
6842         if (!needComma)
6843                 return "";                              /* no undropped columns */
6844
6845         appendPQExpBuffer(q, ")");
6846         return q->data;
6847 }