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-2019, 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 * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 * so it sees a consistent snapshot of the database including system
16 * catalogs. However, it relies in part on various specialized backend
17 * functions like pg_get_indexdef(), and those things tend to look at
18 * the currently committed state. So it is possible to get 'cache
19 * lookup failed' error if someone performs DDL changes while a dump is
20 * happening. The window for this sort of thing is from the acquisition
21 * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 * AccessShareLock on every table it intends to dump). It isn't very large,
23 * but it can happen.
24 *
25 * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 *
27 * IDENTIFICATION
28 * src/bin/pg_dump/pg_dump.c
29 *
30 *-------------------------------------------------------------------------
31 */
32#include "postgres_fe.h"
33
34#include <unistd.h>
35#include <ctype.h>
36#include <limits.h>
37#ifdef HAVE_TERMIOS_H
38#include <termios.h>
39#endif
40
41#include "getopt_long.h"
42
43#include "access/attnum.h"
44#include "access/sysattr.h"
45#include "access/transam.h"
46#include "catalog/pg_aggregate_d.h"
47#include "catalog/pg_am_d.h"
48#include "catalog/pg_attribute_d.h"
49#include "catalog/pg_cast_d.h"
50#include "catalog/pg_class_d.h"
51#include "catalog/pg_default_acl_d.h"
52#include "catalog/pg_largeobject_d.h"
53#include "catalog/pg_largeobject_metadata_d.h"
54#include "catalog/pg_proc_d.h"
55#include "catalog/pg_trigger_d.h"
56#include "catalog/pg_type_d.h"
57#include "libpq/libpq-fs.h"
58#include "storage/block.h"
59
60#include "dumputils.h"
61#include "parallel.h"
62#include "pg_backup_db.h"
63#include "pg_backup_utils.h"
64#include "pg_dump.h"
65#include "fe_utils/connect.h"
66#include "fe_utils/string_utils.h"
67
68
69typedef struct
70{
71 const char *descr; /* comment for an object */
72 Oid classoid; /* object class (catalog OID) */
73 Oid objoid; /* object OID */
74 int objsubid; /* subobject (table column #) */
75} CommentItem;
76
77typedef struct
78{
79 const char *provider; /* label provider of this security label */
80 const char *label; /* security label for an object */
81 Oid classoid; /* object class (catalog OID) */
82 Oid objoid; /* object OID */
83 int objsubid; /* subobject (table column #) */
84} SecLabelItem;
85
86typedef enum OidOptions
87{
88 zeroAsOpaque = 1,
89 zeroAsAny = 2,
90 zeroAsStar = 4,
91 zeroAsNone = 8
92} OidOptions;
93
94/* global decls */
95static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
96
97/* subquery used to convert user ID (eg, datdba) to user name */
98static const char *username_subquery;
99
100/*
101 * For 8.0 and earlier servers, pulled from pg_database, for 8.1+ we use
102 * FirstNormalObjectId - 1.
103 */
104static Oid g_last_builtin_oid; /* value of the last builtin oid */
105
106/* The specified names/patterns should to match at least one entity */
107static int strict_names = 0;
108
109/*
110 * Object inclusion/exclusion lists
111 *
112 * The string lists record the patterns given by command-line switches,
113 * which we then convert to lists of OIDs of matching objects.
114 */
115static SimpleStringList schema_include_patterns = {NULL, NULL};
116static SimpleOidList schema_include_oids = {NULL, NULL};
117static SimpleStringList schema_exclude_patterns = {NULL, NULL};
118static SimpleOidList schema_exclude_oids = {NULL, NULL};
119
120static SimpleStringList table_include_patterns = {NULL, NULL};
121static SimpleOidList table_include_oids = {NULL, NULL};
122static SimpleStringList table_exclude_patterns = {NULL, NULL};
123static SimpleOidList table_exclude_oids = {NULL, NULL};
124static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
125static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
126
127
128char g_opaque_type[10]; /* name for the opaque type */
129
130/* placeholders for the delimiters for comments */
131char g_comment_start[10];
132char g_comment_end[10];
133
134static const CatalogId nilCatalogId = {0, 0};
135
136/* override for standard extra_float_digits setting */
137static bool have_extra_float_digits = false;
138static int extra_float_digits;
139
140/*
141 * The default number of rows per INSERT when
142 * --inserts is specified without --rows-per-insert
143 */
144#define DUMP_DEFAULT_ROWS_PER_INSERT 1
145
146/*
147 * Macro for producing quoted, schema-qualified name of a dumpable object.
148 */
149#define fmtQualifiedDumpable(obj) \
150 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
151 (obj)->dobj.name)
152
153static void help(const char *progname);
154static void setup_connection(Archive *AH,
155 const char *dumpencoding, const char *dumpsnapshot,
156 char *use_role);
157static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
158static void expand_schema_name_patterns(Archive *fout,
159 SimpleStringList *patterns,
160 SimpleOidList *oids,
161 bool strict_names);
162static void expand_table_name_patterns(Archive *fout,
163 SimpleStringList *patterns,
164 SimpleOidList *oids,
165 bool strict_names);
166static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid);
167static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
168static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo);
169static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
170static void dumpComment(Archive *fout, const char *type, const char *name,
171 const char *namespace, const char *owner,
172 CatalogId catalogId, int subid, DumpId dumpId);
173static int findComments(Archive *fout, Oid classoid, Oid objoid,
174 CommentItem **items);
175static int collectComments(Archive *fout, CommentItem **items);
176static void dumpSecLabel(Archive *fout, const char *type, const char *name,
177 const char *namespace, const char *owner,
178 CatalogId catalogId, int subid, DumpId dumpId);
179static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
180 SecLabelItem **items);
181static int collectSecLabels(Archive *fout, SecLabelItem **items);
182static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
183static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
184static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
185static void dumpType(Archive *fout, TypeInfo *tyinfo);
186static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
187static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
188static void dumpRangeType(Archive *fout, TypeInfo *tyinfo);
189static void dumpUndefinedType(Archive *fout, TypeInfo *tyinfo);
190static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
191static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
192static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
193static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
194static void dumpProcLang(Archive *fout, ProcLangInfo *plang);
195static void dumpFunc(Archive *fout, FuncInfo *finfo);
196static void dumpCast(Archive *fout, CastInfo *cast);
197static void dumpTransform(Archive *fout, TransformInfo *transform);
198static void dumpOpr(Archive *fout, OprInfo *oprinfo);
199static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
200static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
201static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
202static void dumpCollation(Archive *fout, CollInfo *collinfo);
203static void dumpConversion(Archive *fout, ConvInfo *convinfo);
204static void dumpRule(Archive *fout, RuleInfo *rinfo);
205static void dumpAgg(Archive *fout, AggInfo *agginfo);
206static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
207static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo);
208static void dumpTable(Archive *fout, TableInfo *tbinfo);
209static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
210static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
211static void dumpSequence(Archive *fout, TableInfo *tbinfo);
212static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo);
213static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
214static void dumpIndexAttach(Archive *fout, IndexAttachInfo *attachinfo);
215static void dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo);
216static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
217static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
218static void dumpTSParser(Archive *fout, TSParserInfo *prsinfo);
219static void dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo);
220static void dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo);
221static void dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo);
222static void dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo);
223static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo);
224static void dumpUserMappings(Archive *fout,
225 const char *servername, const char *namespace,
226 const char *owner, CatalogId catalogId, DumpId dumpId);
227static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
228
229static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
230 const char *type, const char *name, const char *subname,
231 const char *nspname, const char *owner,
232 const char *acls, const char *racls,
233 const char *initacls, const char *initracls);
234
235static void getDependencies(Archive *fout);
236static void BuildArchiveDependencies(Archive *fout);
237static void findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj,
238 DumpId **dependencies, int *nDeps, int *allocDeps);
239
240static DumpableObject *createBoundaryObjects(void);
241static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
242 DumpableObject *boundaryObjs);
243
244static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
245static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
246static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
247static void buildMatViewRefreshDependencies(Archive *fout);
248static void getTableDataFKConstraints(void);
249static char *format_function_arguments(FuncInfo *finfo, char *funcargs,
250 bool is_agg);
251static char *format_function_arguments_old(Archive *fout,
252 FuncInfo *finfo, int nallargs,
253 char **allargtypes,
254 char **argmodes,
255 char **argnames);
256static char *format_function_signature(Archive *fout,
257 FuncInfo *finfo, bool honor_quotes);
258static char *convertRegProcReference(Archive *fout,
259 const char *proc);
260static char *getFormattedOperatorName(Archive *fout, const char *oproid);
261static char *convertTSFunction(Archive *fout, Oid funcOid);
262static Oid findLastBuiltinOid_V71(Archive *fout);
263static char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
264static void getBlobs(Archive *fout);
265static void dumpBlob(Archive *fout, BlobInfo *binfo);
266static int dumpBlobs(Archive *fout, void *arg);
267static void dumpPolicy(Archive *fout, PolicyInfo *polinfo);
268static void dumpPublication(Archive *fout, PublicationInfo *pubinfo);
269static void dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo);
270static void dumpSubscription(Archive *fout, SubscriptionInfo *subinfo);
271static void dumpDatabase(Archive *AH);
272static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
273 const char *dbname, Oid dboid);
274static void dumpEncoding(Archive *AH);
275static void dumpStdStrings(Archive *AH);
276static void dumpSearchPath(Archive *AH);
277static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
278 PQExpBuffer upgrade_buffer,
279 Oid pg_type_oid,
280 bool force_array_type);
281static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
282 PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
283static void binary_upgrade_set_pg_class_oids(Archive *fout,
284 PQExpBuffer upgrade_buffer,
285 Oid pg_class_oid, bool is_index);
286static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
287 DumpableObject *dobj,
288 const char *objtype,
289 const char *objname,
290 const char *objnamespace);
291static const char *getAttrName(int attrnum, TableInfo *tblInfo);
292static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
293static bool nonemptyReloptions(const char *reloptions);
294static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
295 const char *prefix, Archive *fout);
296static char *get_synchronized_snapshot(Archive *fout);
297static void setupDumpWorker(Archive *AHX);
298static TableInfo *getRootTableInfo(TableInfo *tbinfo);
299
300
301int
302main(int argc, char **argv)
303{
304 int c;
305 const char *filename = NULL;
306 const char *format = "p";
307 TableInfo *tblinfo;
308 int numTables;
309 DumpableObject **dobjs;
310 int numObjs;
311 DumpableObject *boundaryObjs;
312 int i;
313 int optindex;
314 char *endptr;
315 RestoreOptions *ropt;
316 Archive *fout; /* the script file */
317 bool g_verbose = false;
318 const char *dumpencoding = NULL;
319 const char *dumpsnapshot = NULL;
320 char *use_role = NULL;
321 long rowsPerInsert;
322 int numWorkers = 1;
323 trivalue prompt_password = TRI_DEFAULT;
324 int compressLevel = -1;
325 int plainText = 0;
326 ArchiveFormat archiveFormat = archUnknown;
327 ArchiveMode archiveMode;
328
329 static DumpOptions dopt;
330
331 static struct option long_options[] = {
332 {"data-only", no_argument, NULL, 'a'},
333 {"blobs", no_argument, NULL, 'b'},
334 {"no-blobs", no_argument, NULL, 'B'},
335 {"clean", no_argument, NULL, 'c'},
336 {"create", no_argument, NULL, 'C'},
337 {"dbname", required_argument, NULL, 'd'},
338 {"file", required_argument, NULL, 'f'},
339 {"format", required_argument, NULL, 'F'},
340 {"host", required_argument, NULL, 'h'},
341 {"jobs", 1, NULL, 'j'},
342 {"no-reconnect", no_argument, NULL, 'R'},
343 {"no-owner", no_argument, NULL, 'O'},
344 {"port", required_argument, NULL, 'p'},
345 {"schema", required_argument, NULL, 'n'},
346 {"exclude-schema", required_argument, NULL, 'N'},
347 {"schema-only", no_argument, NULL, 's'},
348 {"superuser", required_argument, NULL, 'S'},
349 {"table", required_argument, NULL, 't'},
350 {"exclude-table", required_argument, NULL, 'T'},
351 {"no-password", no_argument, NULL, 'w'},
352 {"password", no_argument, NULL, 'W'},
353 {"username", required_argument, NULL, 'U'},
354 {"verbose", no_argument, NULL, 'v'},
355 {"no-privileges", no_argument, NULL, 'x'},
356 {"no-acl", no_argument, NULL, 'x'},
357 {"compress", required_argument, NULL, 'Z'},
358 {"encoding", required_argument, NULL, 'E'},
359 {"help", no_argument, NULL, '?'},
360 {"version", no_argument, NULL, 'V'},
361
362 /*
363 * the following options don't have an equivalent short option letter
364 */
365 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
366 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
367 {"column-inserts", no_argument, &dopt.column_inserts, 1},
368 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
369 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
370 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
371 {"exclude-table-data", required_argument, NULL, 4},
372 {"extra-float-digits", required_argument, NULL, 8},
373 {"if-exists", no_argument, &dopt.if_exists, 1},
374 {"inserts", no_argument, NULL, 9},
375 {"lock-wait-timeout", required_argument, NULL, 2},
376 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
377 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
378 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
379 {"role", required_argument, NULL, 3},
380 {"section", required_argument, NULL, 5},
381 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
382 {"snapshot", required_argument, NULL, 6},
383 {"strict-names", no_argument, &strict_names, 1},
384 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
385 {"no-comments", no_argument, &dopt.no_comments, 1},
386 {"no-publications", no_argument, &dopt.no_publications, 1},
387 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
388 {"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
389 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
390 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
391 {"no-sync", no_argument, NULL, 7},
392 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
393 {"rows-per-insert", required_argument, NULL, 10},
394
395 {NULL, 0, NULL, 0}
396 };
397
398 pg_logging_init(argv[0]);
399 pg_logging_set_level(PG_LOG_WARNING);
400 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
401
402 /*
403 * Initialize what we need for parallel execution, especially for thread
404 * support on Windows.
405 */
406 init_parallel_dump_utils();
407
408 strcpy(g_comment_start, "-- ");
409 g_comment_end[0] = '\0';
410 strcpy(g_opaque_type, "opaque");
411
412 progname = get_progname(argv[0]);
413
414 if (argc > 1)
415 {
416 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
417 {
418 help(progname);
419 exit_nicely(0);
420 }
421 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
422 {
423 puts("pg_dump (PostgreSQL) " PG_VERSION);
424 exit_nicely(0);
425 }
426 }
427
428 InitDumpOptions(&dopt);
429
430 while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
431 long_options, &optindex)) != -1)
432 {
433 switch (c)
434 {
435 case 'a': /* Dump data only */
436 dopt.dataOnly = true;
437 break;
438
439 case 'b': /* Dump blobs */
440 dopt.outputBlobs = true;
441 break;
442
443 case 'B': /* Don't dump blobs */
444 dopt.dontOutputBlobs = true;
445 break;
446
447 case 'c': /* clean (i.e., drop) schema prior to create */
448 dopt.outputClean = 1;
449 break;
450
451 case 'C': /* Create DB */
452 dopt.outputCreateDB = 1;
453 break;
454
455 case 'd': /* database name */
456 dopt.dbname = pg_strdup(optarg);
457 break;
458
459 case 'E': /* Dump encoding */
460 dumpencoding = pg_strdup(optarg);
461 break;
462
463 case 'f':
464 filename = pg_strdup(optarg);
465 break;
466
467 case 'F':
468 format = pg_strdup(optarg);
469 break;
470
471 case 'h': /* server host */
472 dopt.pghost = pg_strdup(optarg);
473 break;
474
475 case 'j': /* number of dump jobs */
476 numWorkers = atoi(optarg);
477 break;
478
479 case 'n': /* include schema(s) */
480 simple_string_list_append(&schema_include_patterns, optarg);
481 dopt.include_everything = false;
482 break;
483
484 case 'N': /* exclude schema(s) */
485 simple_string_list_append(&schema_exclude_patterns, optarg);
486 break;
487
488 case 'O': /* Don't reconnect to match owner */
489 dopt.outputNoOwner = 1;
490 break;
491
492 case 'p': /* server port */
493 dopt.pgport = pg_strdup(optarg);
494 break;
495
496 case 'R':
497 /* no-op, still accepted for backwards compatibility */
498 break;
499
500 case 's': /* dump schema only */
501 dopt.schemaOnly = true;
502 break;
503
504 case 'S': /* Username for superuser in plain text output */
505 dopt.outputSuperuser = pg_strdup(optarg);
506 break;
507
508 case 't': /* include table(s) */
509 simple_string_list_append(&table_include_patterns, optarg);
510 dopt.include_everything = false;
511 break;
512
513 case 'T': /* exclude table(s) */
514 simple_string_list_append(&table_exclude_patterns, optarg);
515 break;
516
517 case 'U':
518 dopt.username = pg_strdup(optarg);
519 break;
520
521 case 'v': /* verbose */
522 g_verbose = true;
523 pg_logging_set_level(PG_LOG_INFO);
524 break;
525
526 case 'w':
527 prompt_password = TRI_NO;
528 break;
529
530 case 'W':
531 prompt_password = TRI_YES;
532 break;
533
534 case 'x': /* skip ACL dump */
535 dopt.aclsSkip = true;
536 break;
537
538 case 'Z': /* Compression Level */
539 compressLevel = atoi(optarg);
540 if (compressLevel < 0 || compressLevel > 9)
541 {
542 pg_log_error("compression level must be in range 0..9");
543 exit_nicely(1);
544 }
545 break;
546
547 case 0:
548 /* This covers the long options. */
549 break;
550
551 case 2: /* lock-wait-timeout */
552 dopt.lockWaitTimeout = pg_strdup(optarg);
553 break;
554
555 case 3: /* SET ROLE */
556 use_role = pg_strdup(optarg);
557 break;
558
559 case 4: /* exclude table(s) data */
560 simple_string_list_append(&tabledata_exclude_patterns, optarg);
561 break;
562
563 case 5: /* section */
564 set_dump_section(optarg, &dopt.dumpSections);
565 break;
566
567 case 6: /* snapshot */
568 dumpsnapshot = pg_strdup(optarg);
569 break;
570
571 case 7: /* no-sync */
572 dosync = false;
573 break;
574
575 case 8:
576 have_extra_float_digits = true;
577 extra_float_digits = atoi(optarg);
578 if (extra_float_digits < -15 || extra_float_digits > 3)
579 {
580 pg_log_error("extra_float_digits must be in range -15..3");
581 exit_nicely(1);
582 }
583 break;
584
585 case 9: /* inserts */
586
587 /*
588 * dump_inserts also stores --rows-per-insert, careful not to
589 * overwrite that.
590 */
591 if (dopt.dump_inserts == 0)
592 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
593 break;
594
595 case 10: /* rows per insert */
596 errno = 0;
597 rowsPerInsert = strtol(optarg, &endptr, 10);
598
599 if (endptr == optarg || *endptr != '\0' ||
600 rowsPerInsert <= 0 || rowsPerInsert > INT_MAX ||
601 errno == ERANGE)
602 {
603 pg_log_error("rows-per-insert must be in range %d..%d",
604 1, INT_MAX);
605 exit_nicely(1);
606 }
607 dopt.dump_inserts = (int) rowsPerInsert;
608 break;
609
610 default:
611 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
612 exit_nicely(1);
613 }
614 }
615
616 /*
617 * Non-option argument specifies database name as long as it wasn't
618 * already specified with -d / --dbname
619 */
620 if (optind < argc && dopt.dbname == NULL)
621 dopt.dbname = argv[optind++];
622
623 /* Complain if any arguments remain */
624 if (optind < argc)
625 {
626 pg_log_error("too many command-line arguments (first is \"%s\")",
627 argv[optind]);
628 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
629 progname);
630 exit_nicely(1);
631 }
632
633 /* --column-inserts implies --inserts */
634 if (dopt.column_inserts && dopt.dump_inserts == 0)
635 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
636
637 /*
638 * Binary upgrade mode implies dumping sequence data even in schema-only
639 * mode. This is not exposed as a separate option, but kept separate
640 * internally for clarity.
641 */
642 if (dopt.binary_upgrade)
643 dopt.sequence_data = 1;
644
645 if (dopt.dataOnly && dopt.schemaOnly)
646 {
647 pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
648 exit_nicely(1);
649 }
650
651 if (dopt.dataOnly && dopt.outputClean)
652 {
653 pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
654 exit_nicely(1);
655 }
656
657 if (dopt.if_exists && !dopt.outputClean)
658 fatal("option --if-exists requires option -c/--clean");
659
660 /*
661 * --inserts are already implied above if --column-inserts or
662 * --rows-per-insert were specified.
663 */
664 if (dopt.do_nothing && dopt.dump_inserts == 0)
665 fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
666
667 /* Identify archive format to emit */
668 archiveFormat = parseArchiveFormat(format, &archiveMode);
669
670 /* archiveFormat specific setup */
671 if (archiveFormat == archNull)
672 plainText = 1;
673
674 /* Custom and directory formats are compressed by default, others not */
675 if (compressLevel == -1)
676 {
677#ifdef HAVE_LIBZ
678 if (archiveFormat == archCustom || archiveFormat == archDirectory)
679 compressLevel = Z_DEFAULT_COMPRESSION;
680 else
681#endif
682 compressLevel = 0;
683 }
684
685#ifndef HAVE_LIBZ
686 if (compressLevel != 0)
687 pg_log_warning("requested compression not available in this installation -- archive will be uncompressed");
688 compressLevel = 0;
689#endif
690
691 /*
692 * If emitting an archive format, we always want to emit a DATABASE item,
693 * in case --create is specified at pg_restore time.
694 */
695 if (!plainText)
696 dopt.outputCreateDB = 1;
697
698 /*
699 * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually)
700 * parallel jobs because that's the maximum limit for the
701 * WaitForMultipleObjects() call.
702 */
703 if (numWorkers <= 0
704#ifdef WIN32
705 || numWorkers > MAXIMUM_WAIT_OBJECTS
706#endif
707 )
708 fatal("invalid number of parallel jobs");
709
710 /* Parallel backup only in the directory archive format so far */
711 if (archiveFormat != archDirectory && numWorkers > 1)
712 fatal("parallel backup only supported by the directory format");
713
714 /* Open the output file */
715 fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
716 archiveMode, setupDumpWorker);
717
718 /* Make dump options accessible right away */
719 SetArchiveOptions(fout, &dopt, NULL);
720
721 /* Register the cleanup hook */
722 on_exit_close_archive(fout);
723
724 /* Let the archiver know how noisy to be */
725 fout->verbose = g_verbose;
726
727
728 /*
729 * We allow the server to be back to 8.0, and up to any minor release of
730 * our own major version. (See also version check in pg_dumpall.c.)
731 */
732 fout->minRemoteVersion = 80000;
733 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
734
735 fout->numWorkers = numWorkers;
736
737 /*
738 * Open the database using the Archiver, so it knows about it. Errors mean
739 * death.
740 */
741 ConnectDatabase(fout, dopt.dbname, dopt.pghost, dopt.pgport, dopt.username, prompt_password);
742 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
743
744 /*
745 * Disable security label support if server version < v9.1.x (prevents
746 * access to nonexistent pg_seclabel catalog)
747 */
748 if (fout->remoteVersion < 90100)
749 dopt.no_security_labels = 1;
750
751 /*
752 * On hot standbys, never try to dump unlogged table data, since it will
753 * just throw an error.
754 */
755 if (fout->isStandby)
756 dopt.no_unlogged_table_data = true;
757
758 /* Select the appropriate subquery to convert user IDs to names */
759 if (fout->remoteVersion >= 80100)
760 username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =";
761 else
762 username_subquery = "SELECT usename FROM pg_catalog.pg_user WHERE usesysid =";
763
764 /* check the version for the synchronized snapshots feature */
765 if (numWorkers > 1 && fout->remoteVersion < 90200
766 && !dopt.no_synchronized_snapshots)
767 fatal("Synchronized snapshots are not supported by this server version.\n"
768 "Run with --no-synchronized-snapshots instead if you do not need\n"
769 "synchronized snapshots.");
770
771 /* check the version when a snapshot is explicitly specified by user */
772 if (dumpsnapshot && fout->remoteVersion < 90200)
773 fatal("Exported snapshots are not supported by this server version.");
774
775 /*
776 * Find the last built-in OID, if needed (prior to 8.1)
777 *
778 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
779 */
780 if (fout->remoteVersion < 80100)
781 g_last_builtin_oid = findLastBuiltinOid_V71(fout);
782 else
783 g_last_builtin_oid = FirstNormalObjectId - 1;
784
785 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
786
787 /* Expand schema selection patterns into OID lists */
788 if (schema_include_patterns.head != NULL)
789 {
790 expand_schema_name_patterns(fout, &schema_include_patterns,
791 &schema_include_oids,
792 strict_names);
793 if (schema_include_oids.head == NULL)
794 fatal("no matching schemas were found");
795 }
796 expand_schema_name_patterns(fout, &schema_exclude_patterns,
797 &schema_exclude_oids,
798 false);
799 /* non-matching exclusion patterns aren't an error */
800
801 /* Expand table selection patterns into OID lists */
802 if (table_include_patterns.head != NULL)
803 {
804 expand_table_name_patterns(fout, &table_include_patterns,
805 &table_include_oids,
806 strict_names);
807 if (table_include_oids.head == NULL)
808 fatal("no matching tables were found");
809 }
810 expand_table_name_patterns(fout, &table_exclude_patterns,
811 &table_exclude_oids,
812 false);
813
814 expand_table_name_patterns(fout, &tabledata_exclude_patterns,
815 &tabledata_exclude_oids,
816 false);
817
818 /* non-matching exclusion patterns aren't an error */
819
820 /*
821 * Dumping blobs is the default for dumps where an inclusion switch is not
822 * used (an "include everything" dump). -B can be used to exclude blobs
823 * from those dumps. -b can be used to include blobs even when an
824 * inclusion switch is used.
825 *
826 * -s means "schema only" and blobs are data, not schema, so we never
827 * include blobs when -s is used.
828 */
829 if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputBlobs)
830 dopt.outputBlobs = true;
831
832 /*
833 * Now scan the database and create DumpableObject structs for all the
834 * objects we intend to dump.
835 */
836 tblinfo = getSchemaData(fout, &numTables);
837
838 if (fout->remoteVersion < 80400)
839 guessConstraintInheritance(tblinfo, numTables);
840
841 if (!dopt.schemaOnly)
842 {
843 getTableData(&dopt, tblinfo, numTables, 0);
844 buildMatViewRefreshDependencies(fout);
845 if (dopt.dataOnly)
846 getTableDataFKConstraints();
847 }
848
849 if (dopt.schemaOnly && dopt.sequence_data)
850 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
851
852 /*
853 * In binary-upgrade mode, we do not have to worry about the actual blob
854 * data or the associated metadata that resides in the pg_largeobject and
855 * pg_largeobject_metadata tables, respectively.
856 *
857 * However, we do need to collect blob information as there may be
858 * comments or other information on blobs that we do need to dump out.
859 */
860 if (dopt.outputBlobs || dopt.binary_upgrade)
861 getBlobs(fout);
862
863 /*
864 * Collect dependency data to assist in ordering the objects.
865 */
866 getDependencies(fout);
867
868 /* Lastly, create dummy objects to represent the section boundaries */
869 boundaryObjs = createBoundaryObjects();
870
871 /* Get pointers to all the known DumpableObjects */
872 getDumpableObjects(&dobjs, &numObjs);
873
874 /*
875 * Add dummy dependencies to enforce the dump section ordering.
876 */
877 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
878
879 /*
880 * Sort the objects into a safe dump order (no forward references).
881 *
882 * We rely on dependency information to help us determine a safe order, so
883 * the initial sort is mostly for cosmetic purposes: we sort by name to
884 * ensure that logically identical schemas will dump identically.
885 */
886 sortDumpableObjectsByTypeName(dobjs, numObjs);
887
888 sortDumpableObjects(dobjs, numObjs,
889 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
890
891 /*
892 * Create archive TOC entries for all the objects to be dumped, in a safe
893 * order.
894 */
895
896 /* First the special ENCODING, STDSTRINGS, and SEARCHPATH entries. */
897 dumpEncoding(fout);
898 dumpStdStrings(fout);
899 dumpSearchPath(fout);
900
901 /* The database items are always next, unless we don't want them at all */
902 if (dopt.outputCreateDB)
903 dumpDatabase(fout);
904
905 /* Now the rearrangeable objects. */
906 for (i = 0; i < numObjs; i++)
907 dumpDumpableObject(fout, dobjs[i]);
908
909 /*
910 * Set up options info to ensure we dump what we want.
911 */
912 ropt = NewRestoreOptions();
913 ropt->filename = filename;
914
915 /* if you change this list, see dumpOptionsFromRestoreOptions */
916 ropt->dropSchema = dopt.outputClean;
917 ropt->dataOnly = dopt.dataOnly;
918 ropt->schemaOnly = dopt.schemaOnly;
919 ropt->if_exists = dopt.if_exists;
920 ropt->column_inserts = dopt.column_inserts;
921 ropt->dumpSections = dopt.dumpSections;
922 ropt->aclsSkip = dopt.aclsSkip;
923 ropt->superuser = dopt.outputSuperuser;
924 ropt->createDB = dopt.outputCreateDB;
925 ropt->noOwner = dopt.outputNoOwner;
926 ropt->noTablespace = dopt.outputNoTablespaces;
927 ropt->disable_triggers = dopt.disable_triggers;
928 ropt->use_setsessauth = dopt.use_setsessauth;
929 ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
930 ropt->dump_inserts = dopt.dump_inserts;
931 ropt->no_comments = dopt.no_comments;
932 ropt->no_publications = dopt.no_publications;
933 ropt->no_security_labels = dopt.no_security_labels;
934 ropt->no_subscriptions = dopt.no_subscriptions;
935 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
936 ropt->include_everything = dopt.include_everything;
937 ropt->enable_row_security = dopt.enable_row_security;
938 ropt->sequence_data = dopt.sequence_data;
939 ropt->binary_upgrade = dopt.binary_upgrade;
940
941 if (compressLevel == -1)
942 ropt->compression = 0;
943 else
944 ropt->compression = compressLevel;
945
946 ropt->suppressDumpWarnings = true; /* We've already shown them */
947
948 SetArchiveOptions(fout, &dopt, ropt);
949
950 /* Mark which entries should be output */
951 ProcessArchiveRestoreOptions(fout);
952
953 /*
954 * The archive's TOC entries are now marked as to which ones will actually
955 * be output, so we can set up their dependency lists properly. This isn't
956 * necessary for plain-text output, though.
957 */
958 if (!plainText)
959 BuildArchiveDependencies(fout);
960
961 /*
962 * And finally we can do the actual output.
963 *
964 * Note: for non-plain-text output formats, the output file is written
965 * inside CloseArchive(). This is, um, bizarre; but not worth changing
966 * right now.
967 */
968 if (plainText)
969 RestoreArchive(fout);
970
971 CloseArchive(fout);
972
973 exit_nicely(0);
974}
975
976
977static void
978help(const char *progname)
979{
980 printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
981 printf(_("Usage:\n"));
982 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
983
984 printf(_("\nGeneral options:\n"));
985 printf(_(" -f, --file=FILENAME output file or directory name\n"));
986 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
987 " plain text (default))\n"));
988 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
989 printf(_(" -v, --verbose verbose mode\n"));
990 printf(_(" -V, --version output version information, then exit\n"));
991 printf(_(" -Z, --compress=0-9 compression level for compressed formats\n"));
992 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
993 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
994 printf(_(" -?, --help show this help, then exit\n"));
995
996 printf(_("\nOptions controlling the output content:\n"));
997 printf(_(" -a, --data-only dump only the data, not the schema\n"));
998 printf(_(" -b, --blobs include large objects in dump\n"));
999 printf(_(" -B, --no-blobs exclude large objects in dump\n"));
1000 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1001 printf(_(" -C, --create include commands to create database in dump\n"));
1002 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1003 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1004 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1005 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1006 " plain-text format\n"));
1007 printf(_(" -s, --schema-only dump only the schema, no data\n"));
1008 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1009 printf(_(" -t, --table=PATTERN dump the specified table(s) only\n"));
1010 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1011 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1012 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1013 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1014 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1015 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1016 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1017 " access to)\n"));
1018 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1019 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1020 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1021 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1022 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1023 printf(_(" --no-comments do not dump comments\n"));
1024 printf(_(" --no-publications do not dump publications\n"));
1025 printf(_(" --no-security-labels do not dump security label assignments\n"));
1026 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1027 printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n"));
1028 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1029 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1030 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1031 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1032 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1033 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1034 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1035 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1036 printf(_(" --strict-names require table and/or schema include patterns to\n"
1037 " match at least one entity each\n"));
1038 printf(_(" --use-set-session-authorization\n"
1039 " use SET SESSION AUTHORIZATION commands instead of\n"
1040 " ALTER OWNER commands to set ownership\n"));
1041
1042 printf(_("\nConnection options:\n"));
1043 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1044 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1045 printf(_(" -p, --port=PORT database server port number\n"));
1046 printf(_(" -U, --username=NAME connect as specified database user\n"));
1047 printf(_(" -w, --no-password never prompt for password\n"));
1048 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1049 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1050
1051 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1052 "variable value is used.\n\n"));
1053 printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
1054}
1055
1056static void
1057setup_connection(Archive *AH, const char *dumpencoding,
1058 const char *dumpsnapshot, char *use_role)
1059{
1060 DumpOptions *dopt = AH->dopt;
1061 PGconn *conn = GetConnection(AH);
1062 const char *std_strings;
1063
1064 PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1065
1066 /*
1067 * Set the client encoding if requested.
1068 */
1069 if (dumpencoding)
1070 {
1071 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1072 fatal("invalid client encoding \"%s\" specified",
1073 dumpencoding);
1074 }
1075
1076 /*
1077 * Get the active encoding and the standard_conforming_strings setting, so
1078 * we know how to escape strings.
1079 */
1080 AH->encoding = PQclientEncoding(conn);
1081
1082 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1083 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1084
1085 /*
1086 * Set the role if requested. In a parallel dump worker, we'll be passed
1087 * use_role == NULL, but AH->use_role is already set (if user specified it
1088 * originally) and we should use that.
1089 */
1090 if (!use_role && AH->use_role)
1091 use_role = AH->use_role;
1092
1093 /* Set the role if requested */
1094 if (use_role && AH->remoteVersion >= 80100)
1095 {
1096 PQExpBuffer query = createPQExpBuffer();
1097
1098 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1099 ExecuteSqlStatement(AH, query->data);
1100 destroyPQExpBuffer(query);
1101
1102 /* save it for possible later use by parallel workers */
1103 if (!AH->use_role)
1104 AH->use_role = pg_strdup(use_role);
1105 }
1106
1107 /* Set the datestyle to ISO to ensure the dump's portability */
1108 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1109
1110 /* Likewise, avoid using sql_standard intervalstyle */
1111 if (AH->remoteVersion >= 80400)
1112 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1113
1114 /*
1115 * Use an explicitly specified extra_float_digits if it has been provided.
1116 * Otherwise, set extra_float_digits so that we can dump float data
1117 * exactly (given correctly implemented float I/O code, anyway).
1118 */
1119 if (have_extra_float_digits)
1120 {
1121 PQExpBuffer q = createPQExpBuffer();
1122
1123 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1124 extra_float_digits);
1125 ExecuteSqlStatement(AH, q->data);
1126 destroyPQExpBuffer(q);
1127 }
1128 else if (AH->remoteVersion >= 90000)
1129 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1130 else
1131 ExecuteSqlStatement(AH, "SET extra_float_digits TO 2");
1132
1133 /*
1134 * If synchronized scanning is supported, disable it, to prevent
1135 * unpredictable changes in row ordering across a dump and reload.
1136 */
1137 if (AH->remoteVersion >= 80300)
1138 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1139
1140 /*
1141 * Disable timeouts if supported.
1142 */
1143 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1144 if (AH->remoteVersion >= 90300)
1145 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1146 if (AH->remoteVersion >= 90600)
1147 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1148
1149 /*
1150 * Quote all identifiers, if requested.
1151 */
1152 if (quote_all_identifiers && AH->remoteVersion >= 90100)
1153 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1154
1155 /*
1156 * Adjust row-security mode, if supported.
1157 */
1158 if (AH->remoteVersion >= 90500)
1159 {
1160 if (dopt->enable_row_security)
1161 ExecuteSqlStatement(AH, "SET row_security = on");
1162 else
1163 ExecuteSqlStatement(AH, "SET row_security = off");
1164 }
1165
1166 /*
1167 * Start transaction-snapshot mode transaction to dump consistent data.
1168 */
1169 ExecuteSqlStatement(AH, "BEGIN");
1170 if (AH->remoteVersion >= 90100)
1171 {
1172 /*
1173 * To support the combination of serializable_deferrable with the jobs
1174 * option we use REPEATABLE READ for the worker connections that are
1175 * passed a snapshot. As long as the snapshot is acquired in a
1176 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1177 * REPEATABLE READ transaction provides the appropriate integrity
1178 * guarantees. This is a kluge, but safe for back-patching.
1179 */
1180 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1181 ExecuteSqlStatement(AH,
1182 "SET TRANSACTION ISOLATION LEVEL "
1183 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1184 else
1185 ExecuteSqlStatement(AH,
1186 "SET TRANSACTION ISOLATION LEVEL "
1187 "REPEATABLE READ, READ ONLY");
1188 }
1189 else
1190 {
1191 ExecuteSqlStatement(AH,
1192 "SET TRANSACTION ISOLATION LEVEL "
1193 "SERIALIZABLE, READ ONLY");
1194 }
1195
1196 /*
1197 * If user specified a snapshot to use, select that. In a parallel dump
1198 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1199 * is already set (if the server can handle it) and we should use that.
1200 */
1201 if (dumpsnapshot)
1202 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1203
1204 if (AH->sync_snapshot_id)
1205 {
1206 PQExpBuffer query = createPQExpBuffer();
1207
1208 appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT ");
1209 appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1210 ExecuteSqlStatement(AH, query->data);
1211 destroyPQExpBuffer(query);
1212 }
1213 else if (AH->numWorkers > 1 &&
1214 AH->remoteVersion >= 90200 &&
1215 !dopt->no_synchronized_snapshots)
1216 {
1217 if (AH->isStandby && AH->remoteVersion < 100000)
1218 fatal("Synchronized snapshots on standby servers are not supported by this server version.\n"
1219 "Run with --no-synchronized-snapshots instead if you do not need\n"
1220 "synchronized snapshots.");
1221
1222
1223 AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1224 }
1225}
1226
1227/* Set up connection for a parallel worker process */
1228static void
1229setupDumpWorker(Archive *AH)
1230{
1231 /*
1232 * We want to re-select all the same values the master connection is
1233 * using. We'll have inherited directly-usable values in
1234 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1235 * inherited encoding value back to a string to pass to setup_connection.
1236 */
1237 setup_connection(AH,
1238 pg_encoding_to_char(AH->encoding),
1239 NULL,
1240 NULL);
1241}
1242
1243static char *
1244get_synchronized_snapshot(Archive *fout)
1245{
1246 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1247 char *result;
1248 PGresult *res;
1249
1250 res = ExecuteSqlQueryForSingleRow(fout, query);
1251 result = pg_strdup(PQgetvalue(res, 0, 0));
1252 PQclear(res);
1253
1254 return result;
1255}
1256
1257static ArchiveFormat
1258parseArchiveFormat(const char *format, ArchiveMode *mode)
1259{
1260 ArchiveFormat archiveFormat;
1261
1262 *mode = archModeWrite;
1263
1264 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1265 {
1266 /* This is used by pg_dumpall, and is not documented */
1267 archiveFormat = archNull;
1268 *mode = archModeAppend;
1269 }
1270 else if (pg_strcasecmp(format, "c") == 0)
1271 archiveFormat = archCustom;
1272 else if (pg_strcasecmp(format, "custom") == 0)
1273 archiveFormat = archCustom;
1274 else if (pg_strcasecmp(format, "d") == 0)
1275 archiveFormat = archDirectory;
1276 else if (pg_strcasecmp(format, "directory") == 0)
1277 archiveFormat = archDirectory;
1278 else if (pg_strcasecmp(format, "p") == 0)
1279 archiveFormat = archNull;
1280 else if (pg_strcasecmp(format, "plain") == 0)
1281 archiveFormat = archNull;
1282 else if (pg_strcasecmp(format, "t") == 0)
1283 archiveFormat = archTar;
1284 else if (pg_strcasecmp(format, "tar") == 0)
1285 archiveFormat = archTar;
1286 else
1287 fatal("invalid output format \"%s\" specified", format);
1288 return archiveFormat;
1289}
1290
1291/*
1292 * Find the OIDs of all schemas matching the given list of patterns,
1293 * and append them to the given OID list.
1294 */
1295static void
1296expand_schema_name_patterns(Archive *fout,
1297 SimpleStringList *patterns,
1298 SimpleOidList *oids,
1299 bool strict_names)
1300{
1301 PQExpBuffer query;
1302 PGresult *res;
1303 SimpleStringListCell *cell;
1304 int i;
1305
1306 if (patterns->head == NULL)
1307 return; /* nothing to do */
1308
1309 query = createPQExpBuffer();
1310
1311 /*
1312 * The loop below runs multiple SELECTs might sometimes result in
1313 * duplicate entries in the OID list, but we don't care.
1314 */
1315
1316 for (cell = patterns->head; cell; cell = cell->next)
1317 {
1318 appendPQExpBuffer(query,
1319 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1320 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1321 false, NULL, "n.nspname", NULL, NULL);
1322
1323 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1324 if (strict_names && PQntuples(res) == 0)
1325 fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1326
1327 for (i = 0; i < PQntuples(res); i++)
1328 {
1329 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1330 }
1331
1332 PQclear(res);
1333 resetPQExpBuffer(query);
1334 }
1335
1336 destroyPQExpBuffer(query);
1337}
1338
1339/*
1340 * Find the OIDs of all tables matching the given list of patterns,
1341 * and append them to the given OID list. See also expand_dbname_patterns()
1342 * in pg_dumpall.c
1343 */
1344static void
1345expand_table_name_patterns(Archive *fout,
1346 SimpleStringList *patterns, SimpleOidList *oids,
1347 bool strict_names)
1348{
1349 PQExpBuffer query;
1350 PGresult *res;
1351 SimpleStringListCell *cell;
1352 int i;
1353
1354 if (patterns->head == NULL)
1355 return; /* nothing to do */
1356
1357 query = createPQExpBuffer();
1358
1359 /*
1360 * this might sometimes result in duplicate entries in the OID list, but
1361 * we don't care.
1362 */
1363
1364 for (cell = patterns->head; cell; cell = cell->next)
1365 {
1366 /*
1367 * Query must remain ABSOLUTELY devoid of unqualified names. This
1368 * would be unnecessary given a pg_table_is_visible() variant taking a
1369 * search_path argument.
1370 */
1371 appendPQExpBuffer(query,
1372 "SELECT c.oid"
1373 "\nFROM pg_catalog.pg_class c"
1374 "\n LEFT JOIN pg_catalog.pg_namespace n"
1375 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1376 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1377 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1378 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1379 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1380 RELKIND_PARTITIONED_TABLE);
1381 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1382 false, "n.nspname", "c.relname", NULL,
1383 "pg_catalog.pg_table_is_visible(c.oid)");
1384
1385 ExecuteSqlStatement(fout, "RESET search_path");
1386 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1387 PQclear(ExecuteSqlQueryForSingleRow(fout,
1388 ALWAYS_SECURE_SEARCH_PATH_SQL));
1389 if (strict_names && PQntuples(res) == 0)
1390 fatal("no matching tables were found for pattern \"%s\"", cell->val);
1391
1392 for (i = 0; i < PQntuples(res); i++)
1393 {
1394 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1395 }
1396
1397 PQclear(res);
1398 resetPQExpBuffer(query);
1399 }
1400
1401 destroyPQExpBuffer(query);
1402}
1403
1404/*
1405 * checkExtensionMembership
1406 * Determine whether object is an extension member, and if so,
1407 * record an appropriate dependency and set the object's dump flag.
1408 *
1409 * It's important to call this for each object that could be an extension
1410 * member. Generally, we integrate this with determining the object's
1411 * to-be-dumped-ness, since extension membership overrides other rules for that.
1412 *
1413 * Returns true if object is an extension member, else false.
1414 */
1415static bool
1416checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1417{
1418 ExtensionInfo *ext = findOwningExtension(dobj->catId);
1419
1420 if (ext == NULL)
1421 return false;
1422
1423 dobj->ext_member = true;
1424
1425 /* Record dependency so that getDependencies needn't deal with that */
1426 addObjectDependency(dobj, ext->dobj.dumpId);
1427
1428 /*
1429 * In 9.6 and above, mark the member object to have any non-initial ACL,
1430 * policies, and security labels dumped.
1431 *
1432 * Note that any initial ACLs (see pg_init_privs) will be removed when we
1433 * extract the information about the object. We don't provide support for
1434 * initial policies and security labels and it seems unlikely for those to
1435 * ever exist, but we may have to revisit this later.
1436 *
1437 * Prior to 9.6, we do not include any extension member components.
1438 *
1439 * In binary upgrades, we still dump all components of the members
1440 * individually, since the idea is to exactly reproduce the database
1441 * contents rather than replace the extension contents with something
1442 * different.
1443 */
1444 if (fout->dopt->binary_upgrade)
1445 dobj->dump = ext->dobj.dump;
1446 else
1447 {
1448 if (fout->remoteVersion < 90600)
1449 dobj->dump = DUMP_COMPONENT_NONE;
1450 else
1451 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
1452 DUMP_COMPONENT_SECLABEL |
1453 DUMP_COMPONENT_POLICY);
1454 }
1455
1456 return true;
1457}
1458
1459/*
1460 * selectDumpableNamespace: policy-setting subroutine
1461 * Mark a namespace as to be dumped or not
1462 */
1463static void
1464selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1465{
1466 /*
1467 * If specific tables are being dumped, do not dump any complete
1468 * namespaces. If specific namespaces are being dumped, dump just those
1469 * namespaces. Otherwise, dump all non-system namespaces.
1470 */
1471 if (table_include_oids.head != NULL)
1472 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1473 else if (schema_include_oids.head != NULL)
1474 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1475 simple_oid_list_member(&schema_include_oids,
1476 nsinfo->dobj.catId.oid) ?
1477 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1478 else if (fout->remoteVersion >= 90600 &&
1479 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1480 {
1481 /*
1482 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1483 * they are interesting (and not the original ACLs which were set at
1484 * initdb time, see pg_init_privs).
1485 */
1486 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1487 }
1488 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1489 strcmp(nsinfo->dobj.name, "information_schema") == 0)
1490 {
1491 /* Other system schemas don't get dumped */
1492 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1493 }
1494 else if (strcmp(nsinfo->dobj.name, "public") == 0)
1495 {
1496 /*
1497 * The public schema is a strange beast that sits in a sort of
1498 * no-mans-land between being a system object and a user object. We
1499 * don't want to dump creation or comment commands for it, because
1500 * that complicates matters for non-superuser use of pg_dump. But we
1501 * should dump any ACL changes that have occurred for it, and of
1502 * course we should dump contained objects.
1503 */
1504 nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1505 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1506 }
1507 else
1508 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1509
1510 /*
1511 * In any case, a namespace can be excluded by an exclusion switch
1512 */
1513 if (nsinfo->dobj.dump_contains &&
1514 simple_oid_list_member(&schema_exclude_oids,
1515 nsinfo->dobj.catId.oid))
1516 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1517
1518 /*
1519 * If the schema belongs to an extension, allow extension membership to
1520 * override the dump decision for the schema itself. However, this does
1521 * not change dump_contains, so this won't change what we do with objects
1522 * within the schema. (If they belong to the extension, they'll get
1523 * suppressed by it, otherwise not.)
1524 */
1525 (void) checkExtensionMembership(&nsinfo->dobj, fout);
1526}
1527
1528/*
1529 * selectDumpableTable: policy-setting subroutine
1530 * Mark a table as to be dumped or not
1531 */
1532static void
1533selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1534{
1535 if (checkExtensionMembership(&tbinfo->dobj, fout))
1536 return; /* extension membership overrides all else */
1537
1538 /*
1539 * If specific tables are being dumped, dump just those tables; else, dump
1540 * according to the parent namespace's dump flag.
1541 */
1542 if (table_include_oids.head != NULL)
1543 tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1544 tbinfo->dobj.catId.oid) ?
1545 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1546 else
1547 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1548
1549 /*
1550 * In any case, a table can be excluded by an exclusion switch
1551 */
1552 if (tbinfo->dobj.dump &&
1553 simple_oid_list_member(&table_exclude_oids,
1554 tbinfo->dobj.catId.oid))
1555 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1556}
1557
1558/*
1559 * selectDumpableType: policy-setting subroutine
1560 * Mark a type as to be dumped or not
1561 *
1562 * If it's a table's rowtype or an autogenerated array type, we also apply a
1563 * special type code to facilitate sorting into the desired order. (We don't
1564 * want to consider those to be ordinary types because that would bring tables
1565 * up into the datatype part of the dump order.) We still set the object's
1566 * dump flag; that's not going to cause the dummy type to be dumped, but we
1567 * need it so that casts involving such types will be dumped correctly -- see
1568 * dumpCast. This means the flag should be set the same as for the underlying
1569 * object (the table or base type).
1570 */
1571static void
1572selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1573{
1574 /* skip complex types, except for standalone composite types */
1575 if (OidIsValid(tyinfo->typrelid) &&
1576 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1577 {
1578 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1579
1580 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1581 if (tytable != NULL)
1582 tyinfo->dobj.dump = tytable->dobj.dump;
1583 else
1584 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1585 return;
1586 }
1587
1588 /* skip auto-generated array types */
1589 if (tyinfo->isArray)
1590 {
1591 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1592
1593 /*
1594 * Fall through to set the dump flag; we assume that the subsequent
1595 * rules will do the same thing as they would for the array's base
1596 * type. (We cannot reliably look up the base type here, since
1597 * getTypes may not have processed it yet.)
1598 */
1599 }
1600
1601 if (checkExtensionMembership(&tyinfo->dobj, fout))
1602 return; /* extension membership overrides all else */
1603
1604 /* Dump based on if the contents of the namespace are being dumped */
1605 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1606}
1607
1608/*
1609 * selectDumpableDefaultACL: policy-setting subroutine
1610 * Mark a default ACL as to be dumped or not
1611 *
1612 * For per-schema default ACLs, dump if the schema is to be dumped.
1613 * Otherwise dump if we are dumping "everything". Note that dataOnly
1614 * and aclsSkip are checked separately.
1615 */
1616static void
1617selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
1618{
1619 /* Default ACLs can't be extension members */
1620
1621 if (dinfo->dobj.namespace)
1622 /* default ACLs are considered part of the namespace */
1623 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
1624 else
1625 dinfo->dobj.dump = dopt->include_everything ?
1626 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1627}
1628
1629/*
1630 * selectDumpableCast: policy-setting subroutine
1631 * Mark a cast as to be dumped or not
1632 *
1633 * Casts do not belong to any particular namespace (since they haven't got
1634 * names), nor do they have identifiable owners. To distinguish user-defined
1635 * casts from built-in ones, we must resort to checking whether the cast's
1636 * OID is in the range reserved for initdb.
1637 */
1638static void
1639selectDumpableCast(CastInfo *cast, Archive *fout)
1640{
1641 if (checkExtensionMembership(&cast->dobj, fout))
1642 return; /* extension membership overrides all else */
1643
1644 /*
1645 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
1646 * support ACLs currently.
1647 */
1648 if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1649 cast->dobj.dump = DUMP_COMPONENT_NONE;
1650 else
1651 cast->dobj.dump = fout->dopt->include_everything ?
1652 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1653}
1654
1655/*
1656 * selectDumpableProcLang: policy-setting subroutine
1657 * Mark a procedural language as to be dumped or not
1658 *
1659 * Procedural languages do not belong to any particular namespace. To
1660 * identify built-in languages, we must resort to checking whether the
1661 * language's OID is in the range reserved for initdb.
1662 */
1663static void
1664selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
1665{
1666 if (checkExtensionMembership(&plang->dobj, fout))
1667 return; /* extension membership overrides all else */
1668
1669 /*
1670 * Only include procedural languages when we are dumping everything.
1671 *
1672 * For from-initdb procedural languages, only include ACLs, as we do for
1673 * the pg_catalog namespace. We need this because procedural languages do
1674 * not live in any namespace.
1675 */
1676 if (!fout->dopt->include_everything)
1677 plang->dobj.dump = DUMP_COMPONENT_NONE;
1678 else
1679 {
1680 if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1681 plang->dobj.dump = fout->remoteVersion < 90600 ?
1682 DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
1683 else
1684 plang->dobj.dump = DUMP_COMPONENT_ALL;
1685 }
1686}
1687
1688/*
1689 * selectDumpableAccessMethod: policy-setting subroutine
1690 * Mark an access method as to be dumped or not
1691 *
1692 * Access methods do not belong to any particular namespace. To identify
1693 * built-in access methods, we must resort to checking whether the
1694 * method's OID is in the range reserved for initdb.
1695 */
1696static void
1697selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
1698{
1699 if (checkExtensionMembership(&method->dobj, fout))
1700 return; /* extension membership overrides all else */
1701
1702 /*
1703 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
1704 * they do not support ACLs currently.
1705 */
1706 if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1707 method->dobj.dump = DUMP_COMPONENT_NONE;
1708 else
1709 method->dobj.dump = fout->dopt->include_everything ?
1710 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1711}
1712
1713/*
1714 * selectDumpableExtension: policy-setting subroutine
1715 * Mark an extension as to be dumped or not
1716 *
1717 * Built-in extensions should be skipped except for checking ACLs, since we
1718 * assume those will already be installed in the target database. We identify
1719 * such extensions by their having OIDs in the range reserved for initdb.
1720 * We dump all user-added extensions by default, or none of them if
1721 * include_everything is false (i.e., a --schema or --table switch was given).
1722 */
1723static void
1724selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
1725{
1726 /*
1727 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
1728 * change permissions on their member objects, if they wish to, and have
1729 * those changes preserved.
1730 */
1731 if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1732 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
1733 else
1734 extinfo->dobj.dump = extinfo->dobj.dump_contains =
1735 dopt->include_everything ? DUMP_COMPONENT_ALL :
1736 DUMP_COMPONENT_NONE;
1737}
1738
1739/*
1740 * selectDumpablePublicationTable: policy-setting subroutine
1741 * Mark a publication table as to be dumped or not
1742 *
1743 * Publication tables have schemas, but those are ignored in decision making,
1744 * because publications are only dumped when we are dumping everything.
1745 */
1746static void
1747selectDumpablePublicationTable(DumpableObject *dobj, Archive *fout)
1748{
1749 if (checkExtensionMembership(dobj, fout))
1750 return; /* extension membership overrides all else */
1751
1752 dobj->dump = fout->dopt->include_everything ?
1753 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1754}
1755
1756/*
1757 * selectDumpableObject: policy-setting subroutine
1758 * Mark a generic dumpable object as to be dumped or not
1759 *
1760 * Use this only for object types without a special-case routine above.
1761 */
1762static void
1763selectDumpableObject(DumpableObject *dobj, Archive *fout)
1764{
1765 if (checkExtensionMembership(dobj, fout))
1766 return; /* extension membership overrides all else */
1767
1768 /*
1769 * Default policy is to dump if parent namespace is dumpable, or for
1770 * non-namespace-associated items, dump if we're dumping "everything".
1771 */
1772 if (dobj->namespace)
1773 dobj->dump = dobj->namespace->dobj.dump_contains;
1774 else
1775 dobj->dump = fout->dopt->include_everything ?
1776 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1777}
1778
1779/*
1780 * Dump a table's contents for loading using the COPY command
1781 * - this routine is called by the Archiver when it wants the table
1782 * to be dumped.
1783 */
1784
1785static int
1786dumpTableData_copy(Archive *fout, void *dcontext)
1787{
1788 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
1789 TableInfo *tbinfo = tdinfo->tdtable;
1790 const char *classname = tbinfo->dobj.name;
1791 PQExpBuffer q = createPQExpBuffer();
1792
1793 /*
1794 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
1795 * which uses it already.
1796 */
1797 PQExpBuffer clistBuf = createPQExpBuffer();
1798 PGconn *conn = GetConnection(fout);
1799 PGresult *res;
1800 int ret;
1801 char *copybuf;
1802 const char *column_list;
1803
1804 pg_log_info("dumping contents of table \"%s.%s\"",
1805 tbinfo->dobj.namespace->dobj.name, classname);
1806
1807 /*
1808 * Specify the column list explicitly so that we have no possibility of
1809 * retrieving data in the wrong column order. (The default column
1810 * ordering of COPY will not be what we want in certain corner cases
1811 * involving ADD COLUMN and inheritance.)
1812 */
1813 column_list = fmtCopyColumnList(tbinfo, clistBuf);
1814
1815 if (tdinfo->filtercond)
1816 {
1817 /* Note: this syntax is only supported in 8.2 and up */
1818 appendPQExpBufferStr(q, "COPY (SELECT ");
1819 /* klugery to get rid of parens in column list */
1820 if (strlen(column_list) > 2)
1821 {
1822 appendPQExpBufferStr(q, column_list + 1);
1823 q->data[q->len - 1] = ' ';
1824 }
1825 else
1826 appendPQExpBufferStr(q, "* ");
1827 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
1828 fmtQualifiedDumpable(tbinfo),
1829 tdinfo->filtercond);
1830 }
1831 else
1832 {
1833 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
1834 fmtQualifiedDumpable(tbinfo),
1835 column_list);
1836 }
1837 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
1838 PQclear(res);
1839 destroyPQExpBuffer(clistBuf);
1840
1841 for (;;)
1842 {
1843 ret = PQgetCopyData(conn, &copybuf, 0);
1844
1845 if (ret < 0)
1846 break; /* done or error */
1847
1848 if (copybuf)
1849 {
1850 WriteData(fout, copybuf, ret);
1851 PQfreemem(copybuf);
1852 }
1853
1854 /* ----------
1855 * THROTTLE:
1856 *
1857 * There was considerable discussion in late July, 2000 regarding
1858 * slowing down pg_dump when backing up large tables. Users with both
1859 * slow & fast (multi-processor) machines experienced performance
1860 * degradation when doing a backup.
1861 *
1862 * Initial attempts based on sleeping for a number of ms for each ms
1863 * of work were deemed too complex, then a simple 'sleep in each loop'
1864 * implementation was suggested. The latter failed because the loop
1865 * was too tight. Finally, the following was implemented:
1866 *
1867 * If throttle is non-zero, then
1868 * See how long since the last sleep.
1869 * Work out how long to sleep (based on ratio).
1870 * If sleep is more than 100ms, then
1871 * sleep
1872 * reset timer
1873 * EndIf
1874 * EndIf
1875 *
1876 * where the throttle value was the number of ms to sleep per ms of
1877 * work. The calculation was done in each loop.
1878 *
1879 * Most of the hard work is done in the backend, and this solution
1880 * still did not work particularly well: on slow machines, the ratio
1881 * was 50:1, and on medium paced machines, 1:1, and on fast
1882 * multi-processor machines, it had little or no effect, for reasons
1883 * that were unclear.
1884 *
1885 * Further discussion ensued, and the proposal was dropped.
1886 *
1887 * For those people who want this feature, it can be implemented using
1888 * gettimeofday in each loop, calculating the time since last sleep,
1889 * multiplying that by the sleep ratio, then if the result is more
1890 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
1891 * function to sleep for a subsecond period ie.
1892 *
1893 * select(0, NULL, NULL, NULL, &tvi);
1894 *
1895 * This will return after the interval specified in the structure tvi.
1896 * Finally, call gettimeofday again to save the 'last sleep time'.
1897 * ----------
1898 */
1899 }
1900 archprintf(fout, "\\.\n\n\n");
1901
1902 if (ret == -2)
1903 {
1904 /* copy data transfer failed */
1905 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
1906 pg_log_error("Error message from server: %s", PQerrorMessage(conn));
1907 pg_log_error("The command was: %s", q->data);
1908 exit_nicely(1);
1909 }
1910
1911 /* Check command status and return to normal libpq state */
1912 res = PQgetResult(conn);
1913 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1914 {
1915 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
1916 pg_log_error("Error message from server: %s", PQerrorMessage(conn));
1917 pg_log_error("The command was: %s", q->data);
1918 exit_nicely(1);
1919 }
1920 PQclear(res);
1921
1922 /* Do this to ensure we've pumped libpq back to idle state */
1923 if (PQgetResult(conn) != NULL)
1924 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
1925 classname);
1926
1927 destroyPQExpBuffer(q);
1928 return 1;
1929}
1930
1931/*
1932 * Dump table data using INSERT commands.
1933 *
1934 * Caution: when we restore from an archive file direct to database, the
1935 * INSERT commands emitted by this function have to be parsed by
1936 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
1937 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
1938 */
1939static int
1940dumpTableData_insert(Archive *fout, void *dcontext)
1941{
1942 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
1943 TableInfo *tbinfo = tdinfo->tdtable;
1944 DumpOptions *dopt = fout->dopt;
1945 PQExpBuffer q = createPQExpBuffer();
1946 PQExpBuffer insertStmt = NULL;
1947 PGresult *res;
1948 int nfields;
1949 int rows_per_statement = dopt->dump_inserts;
1950 int rows_this_statement = 0;
1951
1952 appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
1953 "SELECT * FROM ONLY %s",
1954 fmtQualifiedDumpable(tbinfo));
1955 if (tdinfo->filtercond)
1956 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
1957
1958 ExecuteSqlStatement(fout, q->data);
1959
1960 while (1)
1961 {
1962 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
1963 PGRES_TUPLES_OK);
1964 nfields = PQnfields(res);
1965
1966 /*
1967 * First time through, we build as much of the INSERT statement as
1968 * possible in "insertStmt", which we can then just print for each
1969 * statement. If the table happens to have zero columns then this will
1970 * be a complete statement, otherwise it will end in "VALUES" and be
1971 * ready to have the row's column values printed.
1972 */
1973 if (insertStmt == NULL)
1974 {
1975 TableInfo *targettab;
1976
1977 insertStmt = createPQExpBuffer();
1978
1979 /*
1980 * When load-via-partition-root is set, get the root table name
1981 * for the partition table, so that we can reload data through the
1982 * root table.
1983 */
1984 if (dopt->load_via_partition_root && tbinfo->ispartition)
1985 targettab = getRootTableInfo(tbinfo);
1986 else
1987 targettab = tbinfo;
1988
1989 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1990 fmtQualifiedDumpable(targettab));
1991
1992 /* corner case for zero-column table */
1993 if (nfields == 0)
1994 {
1995 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
1996 }
1997 else
1998 {
1999 /* append the list of column names if required */
2000 if (dopt->column_inserts)
2001 {
2002 appendPQExpBufferChar(insertStmt, '(');
2003 for (int field = 0; field < nfields; field++)
2004 {
2005 if (field > 0)
2006 appendPQExpBufferStr(insertStmt, ", ");
2007 appendPQExpBufferStr(insertStmt,
2008 fmtId(PQfname(res, field)));
2009 }
2010 appendPQExpBufferStr(insertStmt, ") ");
2011 }
2012
2013 if (tbinfo->needs_override)
2014 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2015
2016 appendPQExpBufferStr(insertStmt, "VALUES");
2017 }
2018 }
2019
2020 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2021 {
2022 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2023 if (rows_this_statement == 0)
2024 archputs(insertStmt->data, fout);
2025
2026 /*
2027 * If it is zero-column table then we've already written the
2028 * complete statement, which will mean we've disobeyed
2029 * --rows-per-insert when it's set greater than 1. We do support
2030 * a way to make this multi-row with: SELECT UNION ALL SELECT
2031 * UNION ALL ... but that's non-standard so we should avoid it
2032 * given that using INSERTs is mostly only ever needed for
2033 * cross-database exports.
2034 */
2035 if (nfields == 0)
2036 continue;
2037
2038 /* Emit a row heading */
2039 if (rows_per_statement == 1)
2040 archputs(" (", fout);
2041 else if (rows_this_statement > 0)
2042 archputs(",\n\t(", fout);
2043 else
2044 archputs("\n\t(", fout);
2045
2046 for (int field = 0; field < nfields; field++)
2047 {
2048 if (field > 0)
2049 archputs(", ", fout);
2050 if (tbinfo->attgenerated[field])
2051 {
2052 archputs("DEFAULT", fout);
2053 continue;
2054 }
2055 if (PQgetisnull(res, tuple, field))
2056 {
2057 archputs("NULL", fout);
2058 continue;
2059 }
2060
2061 /* XXX This code is partially duplicated in ruleutils.c */
2062 switch (PQftype(res, field))
2063 {
2064 case INT2OID:
2065 case INT4OID:
2066 case INT8OID:
2067 case OIDOID:
2068 case FLOAT4OID:
2069 case FLOAT8OID:
2070 case NUMERICOID:
2071 {
2072 /*
2073 * These types are printed without quotes unless
2074 * they contain values that aren't accepted by the
2075 * scanner unquoted (e.g., 'NaN'). Note that
2076 * strtod() and friends might accept NaN, so we
2077 * can't use that to test.
2078 *
2079 * In reality we only need to defend against
2080 * infinity and NaN, so we need not get too crazy
2081 * about pattern matching here.
2082 */
2083 const char *s = PQgetvalue(res, tuple, field);
2084
2085 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2086 archputs(s, fout);
2087 else
2088 archprintf(fout, "'%s'", s);
2089 }
2090 break;
2091
2092 case BITOID:
2093 case VARBITOID:
2094 archprintf(fout, "B'%s'",
2095 PQgetvalue(res, tuple, field));
2096 break;
2097
2098 case BOOLOID:
2099 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2100 archputs("true", fout);
2101 else
2102 archputs("false", fout);
2103 break;
2104
2105 default:
2106 /* All other types are printed as string literals. */
2107 resetPQExpBuffer(q);
2108 appendStringLiteralAH(q,
2109 PQgetvalue(res, tuple, field),
2110 fout);
2111 archputs(q->data, fout);
2112 break;
2113 }
2114 }
2115
2116 /* Terminate the row ... */
2117 archputs(")", fout);
2118
2119 /* ... and the statement, if the target no. of rows is reached */
2120 if (++rows_this_statement >= rows_per_statement)
2121 {
2122 if (dopt->do_nothing)
2123 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2124 else
2125 archputs(";\n", fout);
2126 /* Reset the row counter */
2127 rows_this_statement = 0;
2128 }
2129 }
2130
2131 if (PQntuples(res) <= 0)
2132 {
2133 PQclear(res);
2134 break;
2135 }
2136 PQclear(res);
2137 }
2138
2139 /* Terminate any statements that didn't make the row count. */
2140 if (rows_this_statement > 0)
2141 {
2142 if (dopt->do_nothing)
2143 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2144 else
2145 archputs(";\n", fout);
2146 }
2147
2148 archputs("\n\n", fout);
2149
2150 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2151
2152 destroyPQExpBuffer(q);
2153 if (insertStmt != NULL)
2154 destroyPQExpBuffer(insertStmt);
2155
2156 return 1;
2157}
2158
2159/*
2160 * getRootTableInfo:
2161 * get the root TableInfo for the given partition table.
2162 */
2163static TableInfo *
2164getRootTableInfo(TableInfo *tbinfo)
2165{
2166 TableInfo *parentTbinfo;
2167
2168 Assert(tbinfo->ispartition);
2169 Assert(tbinfo->numParents == 1);
2170
2171 parentTbinfo = tbinfo->parents[0];
2172 while (parentTbinfo->ispartition)
2173 {
2174 Assert(parentTbinfo->numParents == 1);
2175 parentTbinfo = parentTbinfo->parents[0];
2176 }
2177
2178 return parentTbinfo;
2179}
2180
2181/*
2182 * dumpTableData -
2183 * dump the contents of a single table
2184 *
2185 * Actually, this just makes an ArchiveEntry for the table contents.
2186 */
2187static void
2188dumpTableData(Archive *fout, TableDataInfo *tdinfo)
2189{
2190 DumpOptions *dopt = fout->dopt;
2191 TableInfo *tbinfo = tdinfo->tdtable;
2192 PQExpBuffer copyBuf = createPQExpBuffer();
2193 PQExpBuffer clistBuf = createPQExpBuffer();
2194 DataDumperPtr dumpFn;
2195 char *copyStmt;
2196 const char *copyFrom;
2197
2198 if (!dopt->dump_inserts)
2199 {
2200 /* Dump/restore using COPY */
2201 dumpFn = dumpTableData_copy;
2202
2203 /*
2204 * When load-via-partition-root is set, get the root table name for
2205 * the partition table, so that we can reload data through the root
2206 * table.
2207 */
2208 if (dopt->load_via_partition_root && tbinfo->ispartition)
2209 {
2210 TableInfo *parentTbinfo;
2211
2212 parentTbinfo = getRootTableInfo(tbinfo);
2213 copyFrom = fmtQualifiedDumpable(parentTbinfo);
2214 }
2215 else
2216 copyFrom = fmtQualifiedDumpable(tbinfo);
2217
2218 /* must use 2 steps here 'cause fmtId is nonreentrant */
2219 appendPQExpBuffer(copyBuf, "COPY %s ",
2220 copyFrom);
2221 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2222 fmtCopyColumnList(tbinfo, clistBuf));
2223 copyStmt = copyBuf->data;
2224 }
2225 else
2226 {
2227 /* Restore using INSERT */
2228 dumpFn = dumpTableData_insert;
2229 copyStmt = NULL;
2230 }
2231
2232 /*
2233 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2234 * dependency on its table as "special" and pass it to ArchiveEntry now.
2235 * See comments for BuildArchiveDependencies.
2236 */
2237 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2238 {
2239 TocEntry *te;
2240
2241 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2242 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2243 .namespace = tbinfo->dobj.namespace->dobj.name,
2244 .owner = tbinfo->rolname,
2245 .description = "TABLE DATA",
2246 .section = SECTION_DATA,
2247 .copyStmt = copyStmt,
2248 .deps = &(tbinfo->dobj.dumpId),
2249 .nDeps = 1,
2250 .dumpFn = dumpFn,
2251 .dumpArg = tdinfo));
2252
2253 /*
2254 * Set the TocEntry's dataLength in case we are doing a parallel dump
2255 * and want to order dump jobs by table size. We choose to measure
2256 * dataLength in table pages during dump, so no scaling is needed.
2257 * However, relpages is declared as "integer" in pg_class, and hence
2258 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2259 * Cast so that we get the right interpretation of table sizes
2260 * exceeding INT_MAX pages.
2261 */
2262 te->dataLength = (BlockNumber) tbinfo->relpages;
2263 }
2264
2265 destroyPQExpBuffer(copyBuf);
2266 destroyPQExpBuffer(clistBuf);
2267}
2268
2269/*
2270 * refreshMatViewData -
2271 * load or refresh the contents of a single materialized view
2272 *
2273 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2274 * statement.
2275 */
2276static void
2277refreshMatViewData(Archive *fout, TableDataInfo *tdinfo)
2278{
2279 TableInfo *tbinfo = tdinfo->tdtable;
2280 PQExpBuffer q;
2281
2282 /* If the materialized view is not flagged as populated, skip this. */
2283 if (!tbinfo->relispopulated)
2284 return;
2285
2286 q = createPQExpBuffer();
2287
2288 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2289 fmtQualifiedDumpable(tbinfo));
2290
2291 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2292 ArchiveEntry(fout,
2293 tdinfo->dobj.catId, /* catalog ID */
2294 tdinfo->dobj.dumpId, /* dump ID */
2295 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2296 .namespace = tbinfo->dobj.namespace->dobj.name,
2297 .owner = tbinfo->rolname,
2298 .description = "MATERIALIZED VIEW DATA",
2299 .section = SECTION_POST_DATA,
2300 .createStmt = q->data,
2301 .deps = tdinfo->dobj.dependencies,
2302 .nDeps = tdinfo->dobj.nDeps));
2303
2304 destroyPQExpBuffer(q);
2305}
2306
2307/*
2308 * getTableData -
2309 * set up dumpable objects representing the contents of tables
2310 */
2311static void
2312getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2313{
2314 int i;
2315
2316 for (i = 0; i < numTables; i++)
2317 {
2318 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2319 (!relkind || tblinfo[i].relkind == relkind))
2320 makeTableDataInfo(dopt, &(tblinfo[i]));
2321 }
2322}
2323
2324/*
2325 * Make a dumpable object for the data of this specific table
2326 *
2327 * Note: we make a TableDataInfo if and only if we are going to dump the
2328 * table data; the "dump" flag in such objects isn't used.
2329 */
2330static void
2331makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2332{
2333 TableDataInfo *tdinfo;
2334
2335 /*
2336 * Nothing to do if we already decided to dump the table. This will
2337 * happen for "config" tables.
2338 */
2339 if (tbinfo->dataObj != NULL)
2340 return;
2341
2342 /* Skip VIEWs (no data to dump) */
2343 if (tbinfo->relkind == RELKIND_VIEW)
2344 return;
2345 /* Skip FOREIGN TABLEs (no data to dump) */
2346 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2347 return;
2348 /* Skip partitioned tables (data in partitions) */
2349 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2350 return;
2351
2352 /* Don't dump data in unlogged tables, if so requested */
2353 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2354 dopt->no_unlogged_table_data)
2355 return;
2356
2357 /* Check that the data is not explicitly excluded */
2358 if (simple_oid_list_member(&tabledata_exclude_oids,
2359 tbinfo->dobj.catId.oid))
2360 return;
2361
2362 /* OK, let's dump it */
2363 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2364
2365 if (tbinfo->relkind == RELKIND_MATVIEW)
2366 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2367 else if (tbinfo->relkind == RELKIND_SEQUENCE)
2368 tdinfo->dobj.objType = DO_SEQUENCE_SET;
2369 else
2370 tdinfo->dobj.objType = DO_TABLE_DATA;
2371
2372 /*
2373 * Note: use tableoid 0 so that this object won't be mistaken for
2374 * something that pg_depend entries apply to.
2375 */
2376 tdinfo->dobj.catId.tableoid = 0;
2377 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2378 AssignDumpId(&tdinfo->dobj);
2379 tdinfo->dobj.name = tbinfo->dobj.name;
2380 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2381 tdinfo->tdtable = tbinfo;
2382 tdinfo->filtercond = NULL; /* might get set later */
2383 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2384
2385 tbinfo->dataObj = tdinfo;
2386}
2387
2388/*
2389 * The refresh for a materialized view must be dependent on the refresh for
2390 * any materialized view that this one is dependent on.
2391 *
2392 * This must be called after all the objects are created, but before they are
2393 * sorted.
2394 */
2395static void
2396buildMatViewRefreshDependencies(Archive *fout)
2397{
2398 PQExpBuffer query;
2399 PGresult *res;
2400 int ntups,
2401 i;
2402 int i_classid,
2403 i_objid,
2404 i_refobjid;
2405
2406 /* No Mat Views before 9.3. */
2407 if (fout->remoteVersion < 90300)
2408 return;
2409
2410 query = createPQExpBuffer();
2411
2412 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2413 "( "
2414 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2415 "FROM pg_depend d1 "
2416 "JOIN pg_class c1 ON c1.oid = d1.objid "
2417 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2418 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2419 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2420 "AND d2.objid = r1.oid "
2421 "AND d2.refobjid <> d1.objid "
2422 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2423 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2424 CppAsString2(RELKIND_VIEW) ") "
2425 "WHERE d1.classid = 'pg_class'::regclass "
2426 "UNION "
2427 "SELECT w.objid, d3.refobjid, c3.relkind "
2428 "FROM w "
2429 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2430 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2431 "AND d3.objid = r3.oid "
2432 "AND d3.refobjid <> w.refobjid "
2433 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2434 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2435 CppAsString2(RELKIND_VIEW) ") "
2436 ") "
2437 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2438 "FROM w "
2439 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2440
2441 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2442
2443 ntups = PQntuples(res);
2444
2445 i_classid = PQfnumber(res, "classid");
2446 i_objid = PQfnumber(res, "objid");
2447 i_refobjid = PQfnumber(res, "refobjid");
2448
2449 for (i = 0; i < ntups; i++)
2450 {
2451 CatalogId objId;
2452 CatalogId refobjId;
2453 DumpableObject *dobj;
2454 DumpableObject *refdobj;
2455 TableInfo *tbinfo;
2456 TableInfo *reftbinfo;
2457
2458 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2459 objId.oid = atooid(PQgetvalue(res, i, i_objid));
2460 refobjId.tableoid = objId.tableoid;
2461 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
2462
2463 dobj = findObjectByCatalogId(objId);
2464 if (dobj == NULL)
2465 continue;
2466
2467 Assert(dobj->objType == DO_TABLE);
2468 tbinfo = (TableInfo *) dobj;
2469 Assert(tbinfo->relkind == RELKIND_MATVIEW);
2470 dobj = (DumpableObject *) tbinfo->dataObj;
2471 if (dobj == NULL)
2472 continue;
2473 Assert(dobj->objType == DO_REFRESH_MATVIEW);
2474
2475 refdobj = findObjectByCatalogId(refobjId);
2476 if (refdobj == NULL)
2477 continue;
2478
2479 Assert(refdobj->objType == DO_TABLE);
2480 reftbinfo = (TableInfo *) refdobj;
2481 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
2482 refdobj = (DumpableObject *) reftbinfo->dataObj;
2483 if (refdobj == NULL)
2484 continue;
2485 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
2486
2487 addObjectDependency(dobj, refdobj->dumpId);
2488
2489 if (!reftbinfo->relispopulated)
2490 tbinfo->relispopulated = false;
2491 }
2492
2493 PQclear(res);
2494
2495 destroyPQExpBuffer(query);
2496}
2497
2498/*
2499 * getTableDataFKConstraints -
2500 * add dump-order dependencies reflecting foreign key constraints
2501 *
2502 * This code is executed only in a data-only dump --- in schema+data dumps
2503 * we handle foreign key issues by not creating the FK constraints until
2504 * after the data is loaded. In a data-only dump, however, we want to
2505 * order the table data objects in such a way that a table's referenced
2506 * tables are restored first. (In the presence of circular references or
2507 * self-references this may be impossible; we'll detect and complain about
2508 * that during the dependency sorting step.)
2509 */
2510static void
2511getTableDataFKConstraints(void)
2512{
2513 DumpableObject **dobjs;
2514 int numObjs;
2515 int i;
2516
2517 /* Search through all the dumpable objects for FK constraints */
2518 getDumpableObjects(&dobjs, &numObjs);
2519 for (i = 0; i < numObjs; i++)
2520 {
2521 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
2522 {
2523 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
2524 TableInfo *ftable;
2525
2526 /* Not interesting unless both tables are to be dumped */
2527 if (cinfo->contable == NULL ||
2528 cinfo->contable->dataObj == NULL)
2529 continue;
2530 ftable = findTableByOid(cinfo->confrelid);
2531 if (ftable == NULL ||
2532 ftable->dataObj == NULL)
2533 continue;
2534
2535 /*
2536 * Okay, make referencing table's TABLE_DATA object depend on the
2537 * referenced table's TABLE_DATA object.
2538 */
2539 addObjectDependency(&cinfo->contable->dataObj->dobj,
2540 ftable->dataObj->dobj.dumpId);
2541 }
2542 }
2543 free(dobjs);
2544}
2545
2546
2547/*
2548 * guessConstraintInheritance:
2549 * In pre-8.4 databases, we can't tell for certain which constraints
2550 * are inherited. We assume a CHECK constraint is inherited if its name
2551 * matches the name of any constraint in the parent. Originally this code
2552 * tried to compare the expression texts, but that can fail for various
2553 * reasons --- for example, if the parent and child tables are in different
2554 * schemas, reverse-listing of function calls may produce different text
2555 * (schema-qualified or not) depending on search path.
2556 *
2557 * In 8.4 and up we can rely on the conislocal field to decide which
2558 * constraints must be dumped; much safer.
2559 *
2560 * This function assumes all conislocal flags were initialized to true.
2561 * It clears the flag on anything that seems to be inherited.
2562 */
2563static void
2564guessConstraintInheritance(TableInfo *tblinfo, int numTables)
2565{
2566 int i,
2567 j,
2568 k;
2569
2570 for (i = 0; i < numTables; i++)
2571 {
2572 TableInfo *tbinfo = &(tblinfo[i]);
2573 int numParents;
2574 TableInfo **parents;
2575 TableInfo *parent;
2576
2577 /* Sequences and views never have parents */
2578 if (tbinfo->relkind == RELKIND_SEQUENCE ||
2579 tbinfo->relkind == RELKIND_VIEW)
2580 continue;
2581
2582 /* Don't bother computing anything for non-target tables, either */
2583 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
2584 continue;
2585
2586 numParents = tbinfo->numParents;
2587 parents = tbinfo->parents;
2588
2589 if (numParents == 0)
2590 continue; /* nothing to see here, move along */
2591
2592 /* scan for inherited CHECK constraints */
2593 for (j = 0; j < tbinfo->ncheck; j++)
2594 {
2595 ConstraintInfo *constr;
2596
2597 constr = &(tbinfo->checkexprs[j]);
2598
2599 for (k = 0; k < numParents; k++)
2600 {
2601 int l;
2602
2603 parent = parents[k];
2604 for (l = 0; l < parent->ncheck; l++)
2605 {
2606 ConstraintInfo *pconstr = &(parent->checkexprs[l]);
2607
2608 if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
2609 {
2610 constr->conislocal = false;
2611 break;
2612 }
2613 }
2614 if (!constr->conislocal)
2615 break;
2616 }
2617 }
2618 }
2619}
2620
2621
2622/*
2623 * dumpDatabase:
2624 * dump the database definition
2625 */
2626static void
2627dumpDatabase(Archive *fout)
2628{
2629 DumpOptions *dopt = fout->dopt;
2630 PQExpBuffer dbQry = createPQExpBuffer();
2631 PQExpBuffer delQry = createPQExpBuffer();
2632 PQExpBuffer creaQry = createPQExpBuffer();
2633 PQExpBuffer labelq = createPQExpBuffer();
2634 PGconn *conn = GetConnection(fout);
2635 PGresult *res;
2636 int i_tableoid,
2637 i_oid,
2638 i_datname,
2639 i_dba,
2640 i_encoding,
2641 i_collate,
2642 i_ctype,
2643 i_frozenxid,
2644 i_minmxid,
2645 i_datacl,
2646 i_rdatacl,
2647 i_datistemplate,
2648 i_datconnlimit,
2649 i_tablespace;
2650 CatalogId dbCatId;
2651 DumpId dbDumpId;
2652 const char *datname,
2653 *dba,
2654 *encoding,
2655 *collate,
2656 *ctype,
2657 *datacl,
2658 *rdatacl,
2659 *datistemplate,
2660 *datconnlimit,
2661 *tablespace;
2662 uint32 frozenxid,
2663 minmxid;
2664 char *qdatname;
2665
2666 pg_log_info("saving database definition");
2667
2668 /*
2669 * Fetch the database-level properties for this database.
2670 *
2671 * The order in which privileges are in the ACL string (the order they
2672 * have been GRANT'd in, which the backend maintains) must be preserved to
2673 * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
2674 * those are dumped in the correct order. Note that initial privileges
2675 * (pg_init_privs) are not supported on databases, so this logic cannot
2676 * make use of buildACLQueries().
2677 */
2678 if (fout->remoteVersion >= 90600)
2679 {
2680 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
2681 "(%s datdba) AS dba, "
2682 "pg_encoding_to_char(encoding) AS encoding, "
2683 "datcollate, datctype, datfrozenxid, datminmxid, "
2684 "(SELECT array_agg(acl ORDER BY row_n) FROM "
2685 " (SELECT acl, row_n FROM "
2686 " unnest(coalesce(datacl,acldefault('d',datdba))) "
2687 " WITH ORDINALITY AS perm(acl,row_n) "
2688 " WHERE NOT EXISTS ( "
2689 " SELECT 1 "
2690 " FROM unnest(acldefault('d',datdba)) "
2691 " AS init(init_acl) "
2692 " WHERE acl = init_acl)) AS datacls) "
2693 " AS datacl, "
2694 "(SELECT array_agg(acl ORDER BY row_n) FROM "
2695 " (SELECT acl, row_n FROM "
2696 " unnest(acldefault('d',datdba)) "
2697 " WITH ORDINALITY AS initp(acl,row_n) "
2698 " WHERE NOT EXISTS ( "
2699 " SELECT 1 "
2700 " FROM unnest(coalesce(datacl,acldefault('d',datdba))) "
2701 " AS permp(orig_acl) "
2702 " WHERE acl = orig_acl)) AS rdatacls) "
2703 " AS rdatacl, "
2704 "datistemplate, datconnlimit, "
2705 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2706 "shobj_description(oid, 'pg_database') AS description "
2707
2708 "FROM pg_database "
2709 "WHERE datname = current_database()",
2710 username_subquery);
2711 }
2712 else if (fout->remoteVersion >= 90300)
2713 {
2714 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
2715 "(%s datdba) AS dba, "
2716 "pg_encoding_to_char(encoding) AS encoding, "
2717 "datcollate, datctype, datfrozenxid, datminmxid, "
2718 "datacl, '' as rdatacl, datistemplate, datconnlimit, "
2719 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2720 "shobj_description(oid, 'pg_database') AS description "
2721
2722 "FROM pg_database "
2723 "WHERE datname = current_database()",
2724 username_subquery);
2725 }
2726 else if (fout->remoteVersion >= 80400)
2727 {
2728 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
2729 "(%s datdba) AS dba, "
2730 "pg_encoding_to_char(encoding) AS encoding, "
2731 "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
2732 "datacl, '' as rdatacl, datistemplate, datconnlimit, "
2733 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2734 "shobj_description(oid, 'pg_database') AS description "
2735
2736 "FROM pg_database "
2737 "WHERE datname = current_database()",
2738 username_subquery);
2739 }
2740 else if (fout->remoteVersion >= 80200)
2741 {
2742 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
2743 "(%s datdba) AS dba, "
2744 "pg_encoding_to_char(encoding) AS encoding, "
2745 "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
2746 "datacl, '' as rdatacl, datistemplate, datconnlimit, "
2747 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
2748 "shobj_description(oid, 'pg_database') AS description "
2749
2750 "FROM pg_database "
2751 "WHERE datname = current_database()",
2752 username_subquery);
2753 }
2754 else
2755 {
2756 appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
2757 "(%s datdba) AS dba, "
2758 "pg_encoding_to_char(encoding) AS encoding, "
2759 "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
2760 "datacl, '' as rdatacl, datistemplate, "
2761 "-1 as datconnlimit, "
2762 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
2763 "FROM pg_database "
2764 "WHERE datname = current_database()",
2765 username_subquery);
2766 }
2767
2768 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
2769
2770 i_tableoid = PQfnumber(res, "tableoid");
2771 i_oid = PQfnumber(res, "oid");
2772 i_datname = PQfnumber(res, "datname");
2773 i_dba = PQfnumber(res, "dba");
2774 i_encoding = PQfnumber(res, "encoding");
2775 i_collate = PQfnumber(res, "datcollate");
2776 i_ctype = PQfnumber(res, "datctype");
2777 i_frozenxid = PQfnumber(res, "datfrozenxid");
2778 i_minmxid = PQfnumber(res, "datminmxid");
2779 i_datacl = PQfnumber(res, "datacl");
2780 i_rdatacl = PQfnumber(res, "rdatacl");
2781 i_datistemplate = PQfnumber(res, "datistemplate");
2782 i_datconnlimit = PQfnumber(res, "datconnlimit");
2783 i_tablespace = PQfnumber(res, "tablespace");
2784
2785 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
2786 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
2787 datname = PQgetvalue(res, 0, i_datname);
2788 dba = PQgetvalue(res, 0, i_dba);
2789 encoding = PQgetvalue(res, 0, i_encoding);
2790 collate = PQgetvalue(res, 0, i_collate);
2791 ctype = PQgetvalue(res, 0, i_ctype);
2792 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
2793 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
2794 datacl = PQgetvalue(res, 0, i_datacl);
2795 rdatacl = PQgetvalue(res, 0, i_rdatacl);
2796 datistemplate = PQgetvalue(res, 0, i_datistemplate);
2797 datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
2798 tablespace = PQgetvalue(res, 0, i_tablespace);
2799
2800 qdatname = pg_strdup(fmtId(datname));
2801
2802 /*
2803 * Prepare the CREATE DATABASE command. We must specify encoding, locale,
2804 * and tablespace since those can't be altered later. Other DB properties
2805 * are left to the DATABASE PROPERTIES entry, so that they can be applied
2806 * after reconnecting to the target DB.
2807 */
2808 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
2809 qdatname);
2810 if (strlen(encoding) > 0)
2811 {
2812 appendPQExpBufferStr(creaQry, " ENCODING = ");
2813 appendStringLiteralAH(creaQry, encoding, fout);
2814 }
2815 if (strlen(collate) > 0)
2816 {
2817 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
2818 appendStringLiteralAH(creaQry, collate, fout);
2819 }
2820 if (strlen(ctype) > 0)
2821 {
2822 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
2823 appendStringLiteralAH(creaQry, ctype, fout);
2824 }
2825
2826 /*
2827 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
2828 * thing; the decision whether to specify a tablespace should be left till
2829 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
2830 * label the DATABASE entry with the tablespace and let the normal
2831 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
2832 * attention to default_tablespace, so that won't work.
2833 */
2834 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
2835 !dopt->outputNoTablespaces)
2836 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
2837 fmtId(tablespace));
2838 appendPQExpBufferStr(creaQry, ";\n");
2839
2840 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
2841 qdatname);
2842
2843 dbDumpId = createDumpId();
2844
2845 ArchiveEntry(fout,
2846 dbCatId, /* catalog ID */
2847 dbDumpId, /* dump ID */
2848 ARCHIVE_OPTS(.tag = datname,
2849 .owner = dba,
2850 .description = "DATABASE",
2851 .section = SECTION_PRE_DATA,
2852 .createStmt = creaQry->data,
2853 .dropStmt = delQry->data));
2854
2855 /* Compute correct tag for archive entry */
2856 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
2857
2858 /* Dump DB comment if any */
2859 if (fout->remoteVersion >= 80200)
2860 {
2861 /*
2862 * 8.2 and up keep comments on shared objects in a shared table, so we
2863 * cannot use the dumpComment() code used for other database objects.
2864 * Be careful that the ArchiveEntry parameters match that function.
2865 */
2866 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
2867
2868 if (comment && *comment && !dopt->no_comments)
2869 {
2870 resetPQExpBuffer(dbQry);
2871
2872 /*
2873 * Generates warning when loaded into a differently-named
2874 * database.
2875 */
2876 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
2877 appendStringLiteralAH(dbQry, comment, fout);
2878 appendPQExpBufferStr(dbQry, ";\n");
2879
2880 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2881 ARCHIVE_OPTS(.tag = labelq->data,
2882 .owner = dba,
2883 .description = "COMMENT",
2884 .section = SECTION_NONE,
2885 .createStmt = dbQry->data,
2886 .deps = &dbDumpId,
2887 .nDeps = 1));
2888 }
2889 }
2890 else
2891 {
2892 dumpComment(fout, "DATABASE", qdatname, NULL, dba,
2893 dbCatId, 0, dbDumpId);
2894 }
2895
2896 /* Dump DB security label, if enabled */
2897 if (!dopt->no_security_labels && fout->remoteVersion >= 90200)
2898 {
2899 PGresult *shres;
2900 PQExpBuffer seclabelQry;
2901
2902 seclabelQry = createPQExpBuffer();
2903
2904 buildShSecLabelQuery(conn, "pg_database", dbCatId.oid, seclabelQry);
2905 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
2906 resetPQExpBuffer(seclabelQry);
2907 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
2908 if (seclabelQry->len > 0)
2909 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2910 ARCHIVE_OPTS(.tag = labelq->data,
2911 .owner = dba,
2912 .description = "SECURITY LABEL",
2913 .section = SECTION_NONE,
2914 .createStmt = seclabelQry->data,
2915 .deps = &dbDumpId,
2916 .nDeps = 1));
2917 destroyPQExpBuffer(seclabelQry);
2918 PQclear(shres);
2919 }
2920
2921 /*
2922 * Dump ACL if any. Note that we do not support initial privileges
2923 * (pg_init_privs) on databases.
2924 */
2925 dumpACL(fout, dbCatId, dbDumpId, "DATABASE",
2926 qdatname, NULL, NULL,
2927 dba, datacl, rdatacl, "", "");
2928
2929 /*
2930 * Now construct a DATABASE PROPERTIES archive entry to restore any
2931 * non-default database-level properties. (The reason this must be
2932 * separate is that we cannot put any additional commands into the TOC
2933 * entry that has CREATE DATABASE. pg_restore would execute such a group
2934 * in an implicit transaction block, and the backend won't allow CREATE
2935 * DATABASE in that context.)
2936 */
2937 resetPQExpBuffer(creaQry);
2938 resetPQExpBuffer(delQry);
2939
2940 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
2941 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
2942 qdatname, datconnlimit);
2943
2944 if (strcmp(datistemplate, "t") == 0)
2945 {
2946 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
2947 qdatname);
2948
2949 /*
2950 * The backend won't accept DROP DATABASE on a template database. We
2951 * can deal with that by removing the template marking before the DROP
2952 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
2953 * since no such command is currently supported, fake it with a direct
2954 * UPDATE on pg_database.
2955 */
2956 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
2957 "SET datistemplate = false WHERE datname = ");
2958 appendStringLiteralAH(delQry, datname, fout);
2959 appendPQExpBufferStr(delQry, ";\n");
2960 }
2961
2962 /* Add database-specific SET options */
2963 dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
2964
2965 /*
2966 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
2967 * entry, too, for lack of a better place.
2968 */
2969 if (dopt->binary_upgrade)
2970 {
2971 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
2972 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
2973 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
2974 "WHERE datname = ",
2975 frozenxid, minmxid);
2976 appendStringLiteralAH(creaQry, datname, fout);
2977 appendPQExpBufferStr(creaQry, ";\n");
2978 }
2979
2980 if (creaQry->len > 0)
2981 ArchiveEntry(fout, nilCatalogId, createDumpId(),
2982 ARCHIVE_OPTS(.tag = datname,
2983 .owner = dba,
2984 .description = "DATABASE PROPERTIES",
2985 .section = SECTION_PRE_DATA,
2986 .createStmt = creaQry->data,
2987 .dropStmt = delQry->data,
2988 .deps = &dbDumpId));
2989
2990 /*
2991 * pg_largeobject comes from the old system intact, so set its
2992 * relfrozenxids and relminmxids.
2993 */
2994 if (dopt->binary_upgrade)
2995 {
2996 PGresult *lo_res;
2997 PQExpBuffer loFrozenQry = createPQExpBuffer();
2998 PQExpBuffer loOutQry = createPQExpBuffer();
2999 int i_relfrozenxid,
3000 i_relminmxid;
3001
3002 /*
3003 * pg_largeobject
3004 */
3005 if (fout->remoteVersion >= 90300)
3006 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid\n"
3007 "FROM pg_catalog.pg_class\n"
3008 "WHERE oid = %u;\n",
3009 LargeObjectRelationId);
3010 else
3011 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid\n"
3012 "FROM pg_catalog.pg_class\n"
3013 "WHERE oid = %u;\n",
3014 LargeObjectRelationId);
3015
3016 lo_res = ExecuteSqlQueryForSingleRow(fout, loFrozenQry->data);
3017
3018 i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3019 i_relminmxid = PQfnumber(lo_res, "relminmxid");
3020
3021 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3022 appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
3023 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3024 "WHERE oid = %u;\n",
3025 atooid(PQgetvalue(lo_res, 0, i_relfrozenxid)),
3026 atooid(PQgetvalue(lo_res, 0, i_relminmxid)),
3027 LargeObjectRelationId);
3028 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3029 ARCHIVE_OPTS(.tag = "pg_largeobject",
3030 .description = "pg_largeobject",
3031 .section = SECTION_PRE_DATA,
3032 .createStmt = loOutQry->data));
3033
3034 PQclear(lo_res);
3035
3036 destroyPQExpBuffer(loFrozenQry);
3037 destroyPQExpBuffer(loOutQry);
3038 }
3039
3040 PQclear(res);
3041
3042 free(qdatname);
3043 destroyPQExpBuffer(dbQry);
3044 destroyPQExpBuffer(delQry);
3045 destroyPQExpBuffer(creaQry);
3046 destroyPQExpBuffer(labelq);
3047}
3048
3049/*
3050 * Collect any database-specific or role-and-database-specific SET options
3051 * for this database, and append them to outbuf.
3052 */
3053static void
3054dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3055 const char *dbname, Oid dboid)
3056{
3057 PGconn *conn = GetConnection(AH);
3058 PQExpBuffer buf = createPQExpBuffer();
3059 PGresult *res;
3060 int count = 1;
3061
3062 /*
3063 * First collect database-specific options. Pre-8.4 server versions lack
3064 * unnest(), so we do this the hard way by querying once per subscript.
3065 */
3066 for (;;)
3067 {
3068 if (AH->remoteVersion >= 90000)
3069 printfPQExpBuffer(buf, "SELECT setconfig[%d] FROM pg_db_role_setting "
3070 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3071 count, dboid);
3072 else
3073 printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE oid = '%u'::oid", count, dboid);
3074
3075 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3076
3077 if (PQntuples(res) == 1 &&
3078 !PQgetisnull(res, 0, 0))
3079 {
3080 makeAlterConfigCommand(conn, PQgetvalue(res, 0, 0),
3081 "DATABASE", dbname, NULL, NULL,
3082 outbuf);
3083 PQclear(res);
3084 count++;
3085 }
3086 else
3087 {
3088 PQclear(res);
3089 break;
3090 }
3091 }
3092
3093 /* Now look for role-and-database-specific options */
3094 if (AH->remoteVersion >= 90000)
3095 {
3096 /* Here we can assume we have unnest() */
3097 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3098 "FROM pg_db_role_setting s, pg_roles r "
3099 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3100 dboid);
3101
3102 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3103
3104 if (PQntuples(res) > 0)
3105 {
3106 int i;
3107
3108 for (i = 0; i < PQntuples(res); i++)
3109 makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3110 "ROLE", PQgetvalue(res, i, 0),
3111 "DATABASE", dbname,
3112 outbuf);
3113 }
3114
3115 PQclear(res);
3116 }
3117
3118 destroyPQExpBuffer(buf);
3119}
3120
3121/*
3122 * dumpEncoding: put the correct encoding into the archive
3123 */
3124static void
3125dumpEncoding(Archive *AH)
3126{
3127 const char *encname = pg_encoding_to_char(AH->encoding);
3128 PQExpBuffer qry = createPQExpBuffer();
3129
3130 pg_log_info("saving encoding = %s", encname);
3131
3132 appendPQExpBufferStr(qry, "SET client_encoding = ");
3133 appendStringLiteralAH(qry, encname, AH);
3134 appendPQExpBufferStr(qry, ";\n");
3135
3136 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3137 ARCHIVE_OPTS(.tag = "ENCODING",
3138 .description = "ENCODING",
3139 .section = SECTION_PRE_DATA,
3140 .createStmt = qry->data));
3141
3142 destroyPQExpBuffer(qry);
3143}
3144
3145
3146/*
3147 * dumpStdStrings: put the correct escape string behavior into the archive
3148 */
3149static void
3150dumpStdStrings(Archive *AH)
3151{
3152 const char *stdstrings = AH->std_strings ? "on" : "off";
3153 PQExpBuffer qry = createPQExpBuffer();
3154
3155 pg_log_info("saving standard_conforming_strings = %s",
3156 stdstrings);
3157
3158 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3159 stdstrings);
3160
3161 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3162 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3163 .description = "STDSTRINGS",
3164 .section = SECTION_PRE_DATA,
3165 .createStmt = qry->data));
3166
3167 destroyPQExpBuffer(qry);
3168}
3169
3170/*
3171 * dumpSearchPath: record the active search_path in the archive
3172 */
3173static void
3174dumpSearchPath(Archive *AH)
3175{
3176 PQExpBuffer qry = createPQExpBuffer();
3177 PQExpBuffer path = createPQExpBuffer();
3178 PGresult *res;
3179 char **schemanames = NULL;
3180 int nschemanames = 0;
3181 int i;
3182
3183 /*
3184 * We use the result of current_schemas(), not the search_path GUC,
3185 * because that might contain wildcards such as "$user", which won't
3186 * necessarily have the same value during restore. Also, this way avoids
3187 * listing schemas that may appear in search_path but not actually exist,
3188 * which seems like a prudent exclusion.
3189 */
3190 res = ExecuteSqlQueryForSingleRow(AH,
3191 "SELECT pg_catalog.current_schemas(false)");
3192
3193 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3194 fatal("could not parse result of current_schemas()");
3195
3196 /*
3197 * We use set_config(), not a simple "SET search_path" command, because
3198 * the latter has less-clean behavior if the search path is empty. While
3199 * that's likely to get fixed at some point, it seems like a good idea to
3200 * be as backwards-compatible as possible in what we put into archives.
3201 */
3202 for (i = 0; i < nschemanames; i++)
3203 {
3204 if (i > 0)
3205 appendPQExpBufferStr(path, ", ");
3206 appendPQExpBufferStr(path, fmtId(schemanames[i]));
3207 }
3208
3209 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3210 appendStringLiteralAH(qry, path->data, AH);
3211 appendPQExpBufferStr(qry, ", false);\n");
3212
3213 pg_log_info("saving search_path = %s", path->data);
3214
3215 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3216 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3217 .description = "SEARCHPATH",
3218 .section = SECTION_PRE_DATA,
3219 .createStmt = qry->data));
3220
3221 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3222 AH->searchpath = pg_strdup(qry->data);
3223
3224 if (schemanames)
3225 free(schemanames);
3226 PQclear(res);
3227 destroyPQExpBuffer(qry);
3228 destroyPQExpBuffer(path);
3229}
3230
3231
3232/*
3233 * getBlobs:
3234 * Collect schema-level data about large objects
3235 */
3236static void
3237getBlobs(Archive *fout)
3238{
3239 DumpOptions *dopt = fout->dopt;
3240 PQExpBuffer blobQry = createPQExpBuffer();
3241 BlobInfo *binfo;
3242 DumpableObject *bdata;
3243 PGresult *res;
3244 int ntups;
3245 int i;
3246 int i_oid;
3247 int i_lomowner;
3248 int i_lomacl;
3249 int i_rlomacl;
3250 int i_initlomacl;
3251 int i_initrlomacl;
3252
3253 pg_log_info("reading large objects");
3254
3255 /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */
3256 if (fout->remoteVersion >= 90600)
3257 {
3258 PQExpBuffer acl_subquery = createPQExpBuffer();
3259 PQExpBuffer racl_subquery = createPQExpBuffer();
3260 PQExpBuffer init_acl_subquery = createPQExpBuffer();
3261 PQExpBuffer init_racl_subquery = createPQExpBuffer();
3262
3263 buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
3264 init_racl_subquery, "l.lomacl", "l.lomowner", "'L'",
3265 dopt->binary_upgrade);
3266
3267 appendPQExpBuffer(blobQry,
3268 "SELECT l.oid, (%s l.lomowner) AS rolname, "
3269 "%s AS lomacl, "
3270 "%s AS rlomacl, "
3271 "%s AS initlomacl, "
3272 "%s AS initrlomacl "
3273 "FROM pg_largeobject_metadata l "
3274 "LEFT JOIN pg_init_privs pip ON "
3275 "(l.oid = pip.objoid "
3276 "AND pip.classoid = 'pg_largeobject'::regclass "
3277 "AND pip.objsubid = 0) ",
3278 username_subquery,
3279 acl_subquery->data,
3280 racl_subquery->data,
3281 init_acl_subquery->data,
3282 init_racl_subquery->data);
3283
3284 destroyPQExpBuffer(acl_subquery);
3285 destroyPQExpBuffer(racl_subquery);
3286 destroyPQExpBuffer(init_acl_subquery);
3287 destroyPQExpBuffer(init_racl_subquery);
3288 }
3289 else if (fout->remoteVersion >= 90000)
3290 appendPQExpBuffer(blobQry,
3291 "SELECT oid, (%s lomowner) AS rolname, lomacl, "
3292 "NULL AS rlomacl, NULL AS initlomacl, "
3293 "NULL AS initrlomacl "
3294 " FROM pg_largeobject_metadata",
3295 username_subquery);
3296 else
3297 appendPQExpBufferStr(blobQry,
3298 "SELECT DISTINCT loid AS oid, "
3299 "NULL::name AS rolname, NULL::oid AS lomacl, "
3300 "NULL::oid AS rlomacl, NULL::oid AS initlomacl, "
3301 "NULL::oid AS initrlomacl "
3302 " FROM pg_largeobject");
3303
3304 res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK);
3305
3306 i_oid = PQfnumber(res, "oid");
3307 i_lomowner = PQfnumber(res, "rolname");
3308 i_lomacl = PQfnumber(res, "lomacl");
3309 i_rlomacl = PQfnumber(res, "rlomacl");
3310 i_initlomacl = PQfnumber(res, "initlomacl");
3311 i_initrlomacl = PQfnumber(res, "initrlomacl");
3312
3313 ntups = PQntuples(res);
3314
3315 /*
3316 * Each large object has its own BLOB archive entry.
3317 */
3318 binfo = (BlobInfo *) pg_malloc(ntups * sizeof(BlobInfo));
3319
3320 for (i = 0; i < ntups; i++)
3321 {
3322 binfo[i].dobj.objType = DO_BLOB;
3323 binfo[i].dobj.catId.tableoid = LargeObjectRelationId;
3324 binfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3325 AssignDumpId(&binfo[i].dobj);
3326
3327 binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
3328 binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner));
3329 binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl));
3330 binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl));
3331 binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl));
3332 binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl));
3333
3334 if (PQgetisnull(res, i, i_lomacl) &&
3335 PQgetisnull(res, i, i_rlomacl) &&
3336 PQgetisnull(res, i, i_initlomacl) &&
3337 PQgetisnull(res, i, i_initrlomacl))
3338 binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
3339
3340 /*
3341 * In binary-upgrade mode for blobs, we do *not* dump out the blob
3342 * data, as it will be copied by pg_upgrade, which simply copies the
3343 * pg_largeobject table. We *do* however dump out anything but the
3344 * data, as pg_upgrade copies just pg_largeobject, but not
3345 * pg_largeobject_metadata, after the dump is restored.
3346 */
3347 if (dopt->binary_upgrade)
3348 binfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA;
3349 }
3350
3351 /*
3352 * If we have any large objects, a "BLOBS" archive entry is needed. This
3353 * is just a placeholder for sorting; it carries no data now.
3354 */
3355 if (ntups > 0)
3356 {
3357 bdata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3358 bdata->objType = DO_BLOB_DATA;
3359 bdata->catId = nilCatalogId;
3360 AssignDumpId(bdata);
3361 bdata->name = pg_strdup("BLOBS");
3362 }
3363
3364 PQclear(res);
3365 destroyPQExpBuffer(blobQry);
3366}
3367
3368/*
3369 * dumpBlob
3370 *
3371 * dump the definition (metadata) of the given large object
3372 */
3373static void
3374dumpBlob(Archive *fout, BlobInfo *binfo)
3375{
3376 PQExpBuffer cquery = createPQExpBuffer();
3377 PQExpBuffer dquery = createPQExpBuffer();
3378
3379 appendPQExpBuffer(cquery,
3380 "SELECT pg_catalog.lo_create('%s');\n",
3381 binfo->dobj.name);
3382
3383 appendPQExpBuffer(dquery,
3384 "SELECT pg_catalog.lo_unlink('%s');\n",
3385 binfo->dobj.name);
3386
3387 if (binfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3388 ArchiveEntry(fout, binfo->dobj.catId, binfo->dobj.dumpId,
3389 ARCHIVE_OPTS(.tag = binfo->dobj.name,
3390 .owner = binfo->rolname,
3391 .description = "BLOB",
3392 .section = SECTION_PRE_DATA,
3393 .createStmt = cquery->data,
3394 .dropStmt = dquery->data));
3395
3396 /* Dump comment if any */
3397 if (binfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3398 dumpComment(fout, "LARGE OBJECT", binfo->dobj.name,
3399 NULL, binfo->rolname,
3400 binfo->dobj.catId, 0, binfo->dobj.dumpId);
3401
3402 /* Dump security label if any */
3403 if (binfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3404 dumpSecLabel(fout, "LARGE OBJECT", binfo->dobj.name,
3405 NULL, binfo->rolname,
3406 binfo->dobj.catId, 0, binfo->dobj.dumpId);
3407
3408 /* Dump ACL if any */
3409 if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
3410 dumpACL(fout, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
3411 binfo->dobj.name, NULL,
3412 NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl,
3413 binfo->initblobacl, binfo->initrblobacl);
3414
3415 destroyPQExpBuffer(cquery);
3416 destroyPQExpBuffer(dquery);
3417}
3418
3419/*
3420 * dumpBlobs:
3421 * dump the data contents of all large objects
3422 */
3423static int
3424dumpBlobs(Archive *fout, void *arg)
3425{
3426 const char *blobQry;
3427 const char *blobFetchQry;
3428 PGconn *conn = GetConnection(fout);
3429 PGresult *res;
3430 char buf[LOBBUFSIZE];
3431 int ntups;
3432 int i;
3433 int cnt;
3434
3435 pg_log_info("saving large objects");
3436
3437 /*
3438 * Currently, we re-fetch all BLOB OIDs using a cursor. Consider scanning
3439 * the already-in-memory dumpable objects instead...
3440 */
3441 if (fout->remoteVersion >= 90000)
3442 blobQry =
3443 "DECLARE bloboid CURSOR FOR "
3444 "SELECT oid FROM pg_largeobject_metadata ORDER BY 1";
3445 else
3446 blobQry =
3447 "DECLARE bloboid CURSOR FOR "
3448 "SELECT DISTINCT loid FROM pg_largeobject ORDER BY 1";
3449
3450 ExecuteSqlStatement(fout, blobQry);
3451
3452 /* Command to fetch from cursor */
3453 blobFetchQry = "FETCH 1000 IN bloboid";
3454
3455 do
3456 {
3457 /* Do a fetch */
3458 res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK);
3459
3460 /* Process the tuples, if any */
3461 ntups = PQntuples(res);
3462 for (i = 0; i < ntups; i++)
3463 {
3464 Oid blobOid;
3465 int loFd;
3466
3467 blobOid = atooid(PQgetvalue(res, i, 0));
3468 /* Open the BLOB */
3469 loFd = lo_open(conn, blobOid, INV_READ);
3470 if (loFd == -1)
3471 fatal("could not open large object %u: %s",
3472 blobOid, PQerrorMessage(conn));
3473
3474 StartBlob(fout, blobOid);
3475
3476 /* Now read it in chunks, sending data to archive */
3477 do
3478 {
3479 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3480 if (cnt < 0)
3481 fatal("error reading large object %u: %s",
3482 blobOid, PQerrorMessage(conn));
3483
3484 WriteData(fout, buf, cnt);
3485 } while (cnt > 0);
3486
3487 lo_close(conn, loFd);
3488
3489 EndBlob(fout, blobOid);
3490 }
3491
3492 PQclear(res);
3493 } while (ntups > 0);
3494
3495 return 1;
3496}
3497
3498/*
3499 * getPolicies
3500 * get information about policies on a dumpable table.
3501 */
3502void
3503getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3504{
3505 PQExpBuffer query;
3506 PGresult *res;
3507 PolicyInfo *polinfo;
3508 int i_oid;
3509 int i_tableoid;
3510 int i_polname;
3511 int i_polcmd;
3512 int i_polpermissive;
3513 int i_polroles;
3514 int i_polqual;
3515 int i_polwithcheck;
3516 int i,
3517 j,
3518 ntups;
3519
3520 if (fout->remoteVersion < 90500)
3521 return;
3522
3523 query = createPQExpBuffer();
3524
3525 for (i = 0; i < numTables; i++)
3526 {
3527 TableInfo *tbinfo = &tblinfo[i];
3528
3529 /* Ignore row security on tables not to be dumped */
3530 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3531 continue;
3532
3533 pg_log_info("reading row security enabled for table \"%s.%s\"",
3534 tbinfo->dobj.namespace->dobj.name,
3535 tbinfo->dobj.name);
3536
3537 /*
3538 * Get row security enabled information for the table. We represent
3539 * RLS being enabled on a table by creating a PolicyInfo object with
3540 * null polname.
3541 */
3542 if (tbinfo->rowsec)
3543 {
3544 /*
3545 * Note: use tableoid 0 so that this object won't be mistaken for
3546 * something that pg_depend entries apply to.
3547 */
3548 polinfo = pg_malloc(sizeof(PolicyInfo));
3549 polinfo->dobj.objType = DO_POLICY;
3550 polinfo->dobj.catId.tableoid = 0;
3551 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3552 AssignDumpId(&polinfo->dobj);
3553 polinfo->dobj.namespace = tbinfo->dobj.namespace;
3554 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
3555 polinfo->poltable = tbinfo;
3556 polinfo->polname = NULL;
3557 polinfo->polcmd = '\0';
3558 polinfo->polpermissive = 0;
3559 polinfo->polroles = NULL;
3560 polinfo->polqual = NULL;
3561 polinfo->polwithcheck = NULL;
3562 }
3563
3564 pg_log_info("reading policies for table \"%s.%s\"",
3565 tbinfo->dobj.namespace->dobj.name,
3566 tbinfo->dobj.name);
3567
3568 resetPQExpBuffer(query);
3569
3570 /* Get the policies for the table. */
3571 if (fout->remoteVersion >= 100000)
3572 appendPQExpBuffer(query,
3573 "SELECT oid, tableoid, pol.polname, pol.polcmd, pol.polpermissive, "
3574 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3575 " pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
3576 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3577 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3578 "FROM pg_catalog.pg_policy pol "
3579 "WHERE polrelid = '%u'",
3580 tbinfo->dobj.catId.oid);
3581 else
3582 appendPQExpBuffer(query,
3583 "SELECT oid, tableoid, pol.polname, pol.polcmd, 't' as polpermissive, "
3584 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3585 " pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
3586 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3587 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3588 "FROM pg_catalog.pg_policy pol "
3589 "WHERE polrelid = '%u'",
3590 tbinfo->dobj.catId.oid);
3591 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3592
3593 ntups = PQntuples(res);
3594
3595 if (ntups == 0)
3596 {
3597 /*
3598 * No explicit policies to handle (only the default-deny policy,
3599 * which is handled as part of the table definition). Clean up
3600 * and return.
3601 */
3602 PQclear(res);
3603 continue;
3604 }
3605
3606 i_oid = PQfnumber(res, "oid");
3607 i_tableoid = PQfnumber(res, "tableoid");
3608 i_polname = PQfnumber(res, "polname");
3609 i_polcmd = PQfnumber(res, "polcmd");
3610 i_polpermissive = PQfnumber(res, "polpermissive");
3611 i_polroles = PQfnumber(res, "polroles");
3612 i_polqual = PQfnumber(res, "polqual");
3613 i_polwithcheck = PQfnumber(res, "polwithcheck");
3614
3615 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
3616
3617 for (j = 0; j < ntups; j++)
3618 {
3619 polinfo[j].dobj.objType = DO_POLICY;
3620 polinfo[j].dobj.catId.tableoid =
3621 atooid(PQgetvalue(res, j, i_tableoid));
3622 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
3623 AssignDumpId(&polinfo[j].dobj);
3624 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3625 polinfo[j].poltable = tbinfo;
3626 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
3627 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
3628
3629 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
3630 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
3631
3632 if (PQgetisnull(res, j, i_polroles))
3633 polinfo[j].polroles = NULL;
3634 else
3635 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
3636
3637 if (PQgetisnull(res, j, i_polqual))
3638 polinfo[j].polqual = NULL;
3639 else
3640 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
3641
3642 if (PQgetisnull(res, j, i_polwithcheck))
3643 polinfo[j].polwithcheck = NULL;
3644 else
3645 polinfo[j].polwithcheck
3646 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
3647 }
3648 PQclear(res);
3649 }
3650 destroyPQExpBuffer(query);
3651}
3652
3653/*
3654 * dumpPolicy
3655 * dump the definition of the given policy
3656 */
3657static void
3658dumpPolicy(Archive *fout, PolicyInfo *polinfo)
3659{
3660 DumpOptions *dopt = fout->dopt;
3661 TableInfo *tbinfo = polinfo->poltable;
3662 PQExpBuffer query;
3663 PQExpBuffer delqry;
3664 const char *cmd;
3665 char *tag;
3666
3667 if (dopt->dataOnly)
3668 return;
3669
3670 /*
3671 * If polname is NULL, then this record is just indicating that ROW LEVEL
3672 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
3673 * ROW LEVEL SECURITY.
3674 */
3675 if (polinfo->polname == NULL)
3676 {
3677 query = createPQExpBuffer();
3678
3679 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
3680 fmtQualifiedDumpable(tbinfo));
3681
3682 /*
3683 * We must emit the ROW SECURITY object's dependency on its table
3684 * explicitly, because it will not match anything in pg_depend (unlike
3685 * the case for other PolicyInfo objects).
3686 */
3687 if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
3688 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
3689 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
3690 .namespace = polinfo->dobj.namespace->dobj.name,
3691 .owner = tbinfo->rolname,
3692 .description = "ROW SECURITY",
3693 .section = SECTION_POST_DATA,
3694 .createStmt = query->data,
3695 .deps = &(tbinfo->dobj.dumpId),
3696 .nDeps = 1));
3697
3698 destroyPQExpBuffer(query);
3699 return;
3700 }
3701
3702 if (polinfo->polcmd == '*')
3703 cmd = "";
3704 else if (polinfo->polcmd == 'r')
3705 cmd = " FOR SELECT";
3706 else if (polinfo->polcmd == 'a')
3707 cmd = " FOR INSERT";
3708 else if (polinfo->polcmd == 'w')
3709 cmd = " FOR UPDATE";
3710 else if (polinfo->polcmd == 'd')
3711 cmd = " FOR DELETE";
3712 else
3713 {
3714 pg_log_error("unexpected policy command type: %c",
3715 polinfo->polcmd);
3716 exit_nicely(1);
3717 }
3718
3719 query = createPQExpBuffer();
3720 delqry = createPQExpBuffer();
3721
3722 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
3723
3724 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
3725 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
3726
3727 if (polinfo->polroles != NULL)
3728 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
3729
3730 if (polinfo->polqual != NULL)
3731 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
3732
3733 if (polinfo->polwithcheck != NULL)
3734 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
3735
3736 appendPQExpBuffer(query, ";\n");
3737
3738 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
3739 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
3740
3741 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
3742
3743 if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
3744 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
3745 ARCHIVE_OPTS(.tag = tag,
3746 .namespace = polinfo->dobj.namespace->dobj.name,
3747 .owner = tbinfo->rolname,
3748 .description = "POLICY",
3749 .section = SECTION_POST_DATA,
3750 .createStmt = query->data,
3751 .dropStmt = delqry->data));
3752
3753 free(tag);
3754 destroyPQExpBuffer(query);
3755 destroyPQExpBuffer(delqry);
3756}
3757
3758/*
3759 * getPublications
3760 * get information about publications
3761 */
3762void
3763getPublications(Archive *fout)
3764{
3765 DumpOptions *dopt = fout->dopt;
3766 PQExpBuffer query;
3767 PGresult *res;
3768 PublicationInfo *pubinfo;
3769 int i_tableoid;
3770 int i_oid;
3771 int i_pubname;
3772 int i_rolname;
3773 int i_puballtables;
3774 int i_pubinsert;
3775 int i_pubupdate;
3776 int i_pubdelete;
3777 int i_pubtruncate;
3778 int i,
3779 ntups;
3780
3781 if (dopt->no_publications || fout->remoteVersion < 100000)
3782 return;
3783
3784 query = createPQExpBuffer();
3785
3786 resetPQExpBuffer(query);
3787
3788 /* Get the publications. */
3789 if (fout->remoteVersion >= 110000)
3790 appendPQExpBuffer(query,
3791 "SELECT p.tableoid, p.oid, p.pubname, "
3792 "(%s p.pubowner) AS rolname, "
3793 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate "
3794 "FROM pg_publication p",
3795 username_subquery);
3796 else
3797 appendPQExpBuffer(query,
3798 "SELECT p.tableoid, p.oid, p.pubname, "
3799 "(%s p.pubowner) AS rolname, "
3800 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate "
3801 "FROM pg_publication p",
3802 username_subquery);
3803
3804 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3805
3806 ntups = PQntuples(res);
3807
3808 i_tableoid = PQfnumber(res, "tableoid");
3809 i_oid = PQfnumber(res, "oid");
3810 i_pubname = PQfnumber(res, "pubname");
3811 i_rolname = PQfnumber(res, "rolname");
3812 i_puballtables = PQfnumber(res, "puballtables");
3813 i_pubinsert = PQfnumber(res, "pubinsert");
3814 i_pubupdate = PQfnumber(res, "pubupdate");
3815 i_pubdelete = PQfnumber(res, "pubdelete");
3816 i_pubtruncate = PQfnumber(res, "pubtruncate");
3817
3818 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
3819
3820 for (i = 0; i < ntups; i++)
3821 {
3822 pubinfo[i].dobj.objType = DO_PUBLICATION;
3823 pubinfo[i].dobj.catId.tableoid =
3824 atooid(PQgetvalue(res, i, i_tableoid));
3825 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3826 AssignDumpId(&pubinfo[i].dobj);
3827 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
3828 pubinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
3829 pubinfo[i].puballtables =
3830 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
3831 pubinfo[i].pubinsert =
3832 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
3833 pubinfo[i].pubupdate =
3834 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
3835 pubinfo[i].pubdelete =
3836 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
3837 pubinfo[i].pubtruncate =
3838 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
3839
3840 if (strlen(pubinfo[i].rolname) == 0)
3841 pg_log_warning("owner of publication \"%s\" appears to be invalid",
3842 pubinfo[i].dobj.name);
3843
3844 /* Decide whether we want to dump it */
3845 selectDumpableObject(&(pubinfo[i].dobj), fout);
3846 }
3847 PQclear(res);
3848
3849 destroyPQExpBuffer(query);
3850}
3851
3852/*
3853 * dumpPublication
3854 * dump the definition of the given publication
3855 */
3856static void
3857dumpPublication(Archive *fout, PublicationInfo *pubinfo)
3858{
3859 PQExpBuffer delq;
3860 PQExpBuffer query;
3861 char *qpubname;
3862 bool first = true;
3863
3864 if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3865 return;
3866
3867 delq = createPQExpBuffer();
3868 query = createPQExpBuffer();
3869
3870 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
3871
3872 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
3873 qpubname);
3874
3875 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
3876 qpubname);
3877
3878 if (pubinfo->puballtables)
3879 appendPQExpBufferStr(query, " FOR ALL TABLES");
3880
3881 appendPQExpBufferStr(query, " WITH (publish = '");
3882 if (pubinfo->pubinsert)
3883 {
3884 appendPQExpBufferStr(query, "insert");
3885 first = false;
3886 }
3887
3888 if (pubinfo->pubupdate)
3889 {
3890 if (!first)
3891 appendPQExpBufferStr(query, ", ");
3892
3893 appendPQExpBufferStr(query, "update");
3894 first = false;
3895 }
3896
3897 if (pubinfo->pubdelete)
3898 {
3899 if (!first)
3900 appendPQExpBufferStr(query, ", ");
3901
3902 appendPQExpBufferStr(query, "delete");
3903 first = false;
3904 }
3905
3906 if (pubinfo->pubtruncate)
3907 {
3908 if (!first)
3909 appendPQExpBufferStr(query, ", ");
3910
3911 appendPQExpBufferStr(query, "truncate");
3912 first = false;
3913 }
3914
3915 appendPQExpBufferStr(query, "');\n");
3916
3917 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
3918 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
3919 .owner = pubinfo->rolname,
3920 .description = "PUBLICATION",
3921 .section = SECTION_POST_DATA,
3922 .createStmt = query->data,
3923 .dropStmt = delq->data));
3924
3925 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3926 dumpComment(fout, "PUBLICATION", qpubname,
3927 NULL, pubinfo->rolname,
3928 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
3929
3930 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3931 dumpSecLabel(fout, "PUBLICATION", qpubname,
3932 NULL, pubinfo->rolname,
3933 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
3934
3935 destroyPQExpBuffer(delq);
3936 destroyPQExpBuffer(query);
3937 free(qpubname);
3938}
3939
3940/*
3941 * getPublicationTables
3942 * get information about publication membership for dumpable tables.
3943 */
3944void
3945getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
3946{
3947 PQExpBuffer query;
3948 PGresult *res;
3949 PublicationRelInfo *pubrinfo;
3950 DumpOptions *dopt = fout->dopt;
3951 int i_tableoid;
3952 int i_oid;
3953 int i_pubname;
3954 int i,
3955 j,
3956 ntups;
3957
3958 if (dopt->no_publications || fout->remoteVersion < 100000)
3959 return;
3960
3961 query = createPQExpBuffer();
3962
3963 for (i = 0; i < numTables; i++)
3964 {
3965 TableInfo *tbinfo = &tblinfo[i];
3966
3967 /* Only plain tables can be aded to publications. */
3968 if (tbinfo->relkind != RELKIND_RELATION)
3969 continue;
3970
3971 /*
3972 * Ignore publication membership of tables whose definitions are not
3973 * to be dumped.
3974 */
3975 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
3976 continue;
3977
3978 pg_log_info("reading publication membership for table \"%s.%s\"",
3979 tbinfo->dobj.namespace->dobj.name,
3980 tbinfo->dobj.name);
3981
3982 resetPQExpBuffer(query);
3983
3984 /* Get the publication membership for the table. */
3985 appendPQExpBuffer(query,
3986 "SELECT pr.tableoid, pr.oid, p.pubname "
3987 "FROM pg_publication_rel pr, pg_publication p "
3988 "WHERE pr.prrelid = '%u'"
3989 " AND p.oid = pr.prpubid",
3990 tbinfo->dobj.catId.oid);
3991 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3992
3993 ntups = PQntuples(res);
3994
3995 if (ntups == 0)
3996 {
3997 /*
3998 * Table is not member of any publications. Clean up and return.
3999 */
4000 PQclear(res);
4001 continue;
4002 }
4003
4004 i_tableoid = PQfnumber(res, "tableoid");
4005 i_oid = PQfnumber(res, "oid");
4006 i_pubname = PQfnumber(res, "pubname");
4007
4008 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4009
4010 for (j = 0; j < ntups; j++)
4011 {
4012 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4013 pubrinfo[j].dobj.catId.tableoid =
4014 atooid(PQgetvalue(res, j, i_tableoid));
4015 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4016 AssignDumpId(&pubrinfo[j].dobj);
4017 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4018 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4019 pubrinfo[j].pubname = pg_strdup(PQgetvalue(res, j, i_pubname));
4020 pubrinfo[j].pubtable = tbinfo;
4021
4022 /* Decide whether we want to dump it */
4023 selectDumpablePublicationTable(&(pubrinfo[j].dobj), fout);
4024 }
4025 PQclear(res);
4026 }
4027 destroyPQExpBuffer(query);
4028}
4029
4030/*
4031 * dumpPublicationTable
4032 * dump the definition of the given publication table mapping
4033 */
4034static void
4035dumpPublicationTable(Archive *fout, PublicationRelInfo *pubrinfo)
4036{
4037 TableInfo *tbinfo = pubrinfo->pubtable;
4038 PQExpBuffer query;
4039 char *tag;
4040
4041 if (!(pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4042 return;
4043
4044 tag = psprintf("%s %s", pubrinfo->pubname, tbinfo->dobj.name);
4045
4046 query = createPQExpBuffer();
4047
4048 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4049 fmtId(pubrinfo->pubname));
4050 appendPQExpBuffer(query, " %s;\n",
4051 fmtQualifiedDumpable(tbinfo));
4052
4053 /*
4054 * There is no point in creating drop query as the drop is done by table
4055 * drop.
4056 */
4057 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4058 ARCHIVE_OPTS(.tag = tag,
4059 .namespace = tbinfo->dobj.namespace->dobj.name,
4060 .description = "PUBLICATION TABLE",
4061 .section = SECTION_POST_DATA,
4062 .createStmt = query->data));
4063
4064 free(tag);
4065 destroyPQExpBuffer(query);
4066}
4067
4068/*
4069 * Is the currently connected user a superuser?
4070 */
4071static bool
4072is_superuser(Archive *fout)
4073{
4074 ArchiveHandle *AH = (ArchiveHandle *) fout;
4075 const char *val;
4076
4077 val = PQparameterStatus(AH->connection, "is_superuser");
4078
4079 if (val && strcmp(val, "on") == 0)
4080 return true;
4081
4082 return false;
4083}
4084
4085/*
4086 * getSubscriptions
4087 * get information about subscriptions
4088 */
4089void
4090getSubscriptions(Archive *fout)
4091{
4092 DumpOptions *dopt = fout->dopt;
4093 PQExpBuffer query;
4094 PGresult *res;
4095 SubscriptionInfo *subinfo;
4096 int i_tableoid;
4097 int i_oid;
4098 int i_subname;
4099 int i_rolname;
4100 int i_subconninfo;
4101 int i_subslotname;
4102 int i_subsynccommit;
4103 int i_subpublications;
4104 int i,
4105 ntups;
4106
4107 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4108 return;
4109
4110 if (!is_superuser(fout))
4111 {
4112 int n;
4113
4114 res = ExecuteSqlQuery(fout,
4115 "SELECT count(*) FROM pg_subscription "
4116 "WHERE subdbid = (SELECT oid FROM pg_database"
4117 " WHERE datname = current_database())",
4118 PGRES_TUPLES_OK);
4119 n = atoi(PQgetvalue(res, 0, 0));
4120 if (n > 0)
4121 pg_log_warning("subscriptions not dumped because current user is not a superuser");
4122 PQclear(res);
4123 return;
4124 }
4125
4126 query = createPQExpBuffer();
4127
4128 resetPQExpBuffer(query);
4129
4130 /* Get the subscriptions in current database. */
4131 appendPQExpBuffer(query,
4132 "SELECT s.tableoid, s.oid, s.subname,"
4133 "(%s s.subowner) AS rolname, "
4134 " s.subconninfo, s.subslotname, s.subsynccommit, "
4135 " s.subpublications "
4136 "FROM pg_subscription s "
4137 "WHERE s.subdbid = (SELECT oid FROM pg_database"
4138 " WHERE datname = current_database())",
4139 username_subquery);
4140 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4141
4142 ntups = PQntuples(res);
4143
4144 i_tableoid = PQfnumber(res, "tableoid");
4145 i_oid = PQfnumber(res, "oid");
4146 i_subname = PQfnumber(res, "subname");
4147 i_rolname = PQfnumber(res, "rolname");
4148 i_subconninfo = PQfnumber(res, "subconninfo");
4149 i_subslotname = PQfnumber(res, "subslotname");
4150 i_subsynccommit = PQfnumber(res, "subsynccommit");
4151 i_subpublications = PQfnumber(res, "subpublications");
4152
4153 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4154
4155 for (i = 0; i < ntups; i++)
4156 {
4157 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4158 subinfo[i].dobj.catId.tableoid =
4159 atooid(PQgetvalue(res, i, i_tableoid));
4160 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4161 AssignDumpId(&subinfo[i].dobj);
4162 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4163 subinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4164 subinfo[i].subconninfo = pg_strdup(PQgetvalue(res, i, i_subconninfo));
4165 if (PQgetisnull(res, i, i_subslotname))
4166 subinfo[i].subslotname = NULL;
4167 else
4168 subinfo[i].subslotname = pg_strdup(PQgetvalue(res, i, i_subslotname));
4169 subinfo[i].subsynccommit =
4170 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
4171 subinfo[i].subpublications =
4172 pg_strdup(PQgetvalue(res, i, i_subpublications));
4173
4174 if (strlen(subinfo[i].rolname) == 0)
4175 pg_log_warning("owner of subscription \"%s\" appears to be invalid",
4176 subinfo[i].dobj.name);
4177
4178 /* Decide whether we want to dump it */
4179 selectDumpableObject(&(subinfo[i].dobj), fout);
4180 }
4181 PQclear(res);
4182
4183 destroyPQExpBuffer(query);
4184}
4185
4186/*
4187 * dumpSubscription
4188 * dump the definition of the given subscription
4189 */
4190static void
4191dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
4192{
4193 PQExpBuffer delq;
4194 PQExpBuffer query;
4195 PQExpBuffer publications;
4196 char *qsubname;
4197 char **pubnames = NULL;
4198 int npubnames = 0;
4199 int i;
4200
4201 if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4202 return;
4203
4204 delq = createPQExpBuffer();
4205 query = createPQExpBuffer();
4206
4207 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
4208
4209 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
4210 qsubname);
4211
4212 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
4213 qsubname);
4214 appendStringLiteralAH(query, subinfo->subconninfo, fout);
4215
4216 /* Build list of quoted publications and append them to query. */
4217 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
4218 {
4219 pg_log_warning("could not parse subpublications array");
4220 if (pubnames)
4221 free(pubnames);
4222 pubnames = NULL;
4223 npubnames = 0;
4224 }
4225
4226 publications = createPQExpBuffer();
4227 for (i = 0; i < npubnames; i++)
4228 {
4229 if (i > 0)
4230 appendPQExpBufferStr(publications, ", ");
4231
4232 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
4233 }
4234
4235 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
4236 if (subinfo->subslotname)
4237 appendStringLiteralAH(query, subinfo->subslotname, fout);
4238 else
4239 appendPQExpBufferStr(query, "NONE");
4240
4241 if (strcmp(subinfo->subsynccommit, "off") != 0)
4242 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
4243
4244 appendPQExpBufferStr(query, ");\n");
4245
4246 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
4247 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
4248 .owner = subinfo->rolname,
4249 .description = "SUBSCRIPTION",
4250 .section = SECTION_POST_DATA,
4251 .createStmt = query->data,
4252 .dropStmt = delq->data));
4253
4254 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4255 dumpComment(fout, "SUBSCRIPTION", qsubname,
4256 NULL, subinfo->rolname,
4257 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
4258
4259 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4260 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
4261 NULL, subinfo->rolname,
4262 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
4263
4264 destroyPQExpBuffer(publications);
4265 if (pubnames)
4266 free(pubnames);
4267
4268 destroyPQExpBuffer(delq);
4269 destroyPQExpBuffer(query);
4270 free(qsubname);
4271}
4272
4273static void
4274binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
4275 PQExpBuffer upgrade_buffer,
4276 Oid pg_type_oid,
4277 bool force_array_type)
4278{
4279 PQExpBuffer upgrade_query = createPQExpBuffer();
4280 PGresult *res;
4281 Oid pg_type_array_oid;
4282
4283 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
4284 appendPQExpBuffer(upgrade_buffer,
4285 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4286 pg_type_oid);
4287
4288 /* we only support old >= 8.3 for binary upgrades */
4289 appendPQExpBuffer(upgrade_query,
4290 "SELECT typarray "
4291 "FROM pg_catalog.pg_type "
4292 "WHERE oid = '%u'::pg_catalog.oid;",
4293 pg_type_oid);
4294
4295 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4296
4297 pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
4298
4299 PQclear(res);
4300
4301 if (!OidIsValid(pg_type_array_oid) && force_array_type)
4302 {
4303 /*
4304 * If the old version didn't assign an array type, but the new version
4305 * does, we must select an unused type OID to assign. This currently
4306 * only happens for domains, when upgrading pre-v11 to v11 and up.
4307 *
4308 * Note: local state here is kind of ugly, but we must have some,
4309 * since we mustn't choose the same unused OID more than once.
4310 */
4311 static Oid next_possible_free_oid = FirstNormalObjectId;
4312 bool is_dup;
4313
4314 do
4315 {
4316 ++next_possible_free_oid;
4317 printfPQExpBuffer(upgrade_query,
4318 "SELECT EXISTS(SELECT 1 "
4319 "FROM pg_catalog.pg_type "
4320 "WHERE oid = '%u'::pg_catalog.oid);",
4321 next_possible_free_oid);
4322 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4323 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
4324 PQclear(res);
4325 } while (is_dup);
4326
4327 pg_type_array_oid = next_possible_free_oid;
4328 }
4329
4330 if (OidIsValid(pg_type_array_oid))
4331 {
4332 appendPQExpBufferStr(upgrade_buffer,
4333 "\n-- For binary upgrade, must preserve pg_type array oid\n");
4334 appendPQExpBuffer(upgrade_buffer,
4335 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4336 pg_type_array_oid);
4337 }
4338
4339 destroyPQExpBuffer(upgrade_query);
4340}
4341
4342static bool
4343binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
4344 PQExpBuffer upgrade_buffer,
4345 Oid pg_rel_oid)
4346{
4347 PQExpBuffer upgrade_query = createPQExpBuffer();
4348 PGresult *upgrade_res;
4349 Oid pg_type_oid;
4350 bool toast_set = false;
4351
4352 /*
4353 * We only support old >= 8.3 for binary upgrades.
4354 *
4355 * We purposefully ignore toast OIDs for partitioned tables; the reason is
4356 * that versions 10 and 11 have them, but 12 does not, so emitting them
4357 * causes the upgrade to fail.
4358 */
4359 appendPQExpBuffer(upgrade_query,
4360 "SELECT c.reltype AS crel, t.reltype AS trel "
4361 "FROM pg_catalog.pg_class c "
4362 "LEFT JOIN pg_catalog.pg_class t ON "
4363 " (c.reltoastrelid = t.oid AND c.relkind <> '%c') "
4364 "WHERE c.oid = '%u'::pg_catalog.oid;",
4365 RELKIND_PARTITIONED_TABLE, pg_rel_oid);
4366
4367 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4368
4369 pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
4370
4371 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
4372 pg_type_oid, false);
4373
4374 if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
4375 {
4376 /* Toast tables do not have pg_type array rows */
4377 Oid pg_type_toast_oid = atooid(PQgetvalue(upgrade_res, 0,
4378 PQfnumber(upgrade_res, "trel")));
4379
4380 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n");
4381 appendPQExpBuffer(upgrade_buffer,
4382 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4383 pg_type_toast_oid);
4384
4385 toast_set = true;
4386 }
4387
4388 PQclear(upgrade_res);
4389 destroyPQExpBuffer(upgrade_query);
4390
4391 return toast_set;
4392}
4393
4394static void
4395binary_upgrade_set_pg_class_oids(Archive *fout,
4396 PQExpBuffer upgrade_buffer, Oid pg_class_oid,
4397 bool is_index)
4398{
4399 PQExpBuffer upgrade_query = createPQExpBuffer();
4400 PGresult *upgrade_res;
4401 Oid pg_class_reltoastrelid;
4402 Oid pg_index_indexrelid;
4403
4404 appendPQExpBuffer(upgrade_query,
4405 "SELECT c.reltoastrelid, i.indexrelid "
4406 "FROM pg_catalog.pg_class c LEFT JOIN "
4407 "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
4408 "WHERE c.oid = '%u'::pg_catalog.oid;",
4409 pg_class_oid);
4410
4411 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4412
4413 pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid")));
4414 pg_index_indexrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid")));
4415
4416 appendPQExpBufferStr(upgrade_buffer,
4417 "\n-- For binary upgrade, must preserve pg_class oids\n");
4418
4419 if (!is_index)
4420 {
4421 appendPQExpBuffer(upgrade_buffer,
4422 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
4423 pg_class_oid);
4424 /* only tables have toast tables, not indexes */
4425 if (OidIsValid(pg_class_reltoastrelid))
4426 {
4427 /*
4428 * One complexity is that the table definition might not require
4429 * the creation of a TOAST table, and the TOAST table might have
4430 * been created long after table creation, when the table was
4431 * loaded with wide data. By setting the TOAST oid we force
4432 * creation of the TOAST heap and TOAST index by the backend so we
4433 * can cleanly copy the files during binary upgrade.
4434 */
4435
4436 appendPQExpBuffer(upgrade_buffer,
4437 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
4438 pg_class_reltoastrelid);
4439
4440 /* every toast table has an index */
4441 appendPQExpBuffer(upgrade_buffer,
4442 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
4443 pg_index_indexrelid);
4444 }
4445 }
4446 else
4447 appendPQExpBuffer(upgrade_buffer,
4448 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
4449 pg_class_oid);
4450
4451 appendPQExpBufferChar(upgrade_buffer, '\n');
4452
4453 PQclear(upgrade_res);
4454 destroyPQExpBuffer(upgrade_query);
4455}
4456
4457/*
4458 * If the DumpableObject is a member of an extension, add a suitable
4459 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
4460 *
4461 * For somewhat historical reasons, objname should already be quoted,
4462 * but not objnamespace (if any).
4463 */
4464static void
4465binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
4466 DumpableObject *dobj,
4467 const char *objtype,
4468 const char *objname,
4469 const char *objnamespace)
4470{
4471 DumpableObject *extobj = NULL;
4472 int i;
4473
4474 if (!dobj->ext_member)
4475 return;
4476
4477 /*
4478 * Find the parent extension. We could avoid this search if we wanted to
4479 * add a link field to DumpableObject, but the space costs of that would
4480 * be considerable. We assume that member objects could only have a
4481 * direct dependency on their own extension, not any others.
4482 */
4483 for (i = 0; i < dobj->nDeps; i++)
4484 {
4485 extobj = findObjectByDumpId(dobj->dependencies[i]);
4486 if (extobj && extobj->objType == DO_EXTENSION)
4487 break;
4488 extobj = NULL;
4489 }
4490 if (extobj == NULL)
4491 fatal("could not find parent extension for %s %s",
4492 objtype, objname);
4493
4494 appendPQExpBufferStr(upgrade_buffer,
4495 "\n-- For binary upgrade, handle extension membership the hard way\n");
4496 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
4497 fmtId(extobj->name),
4498 objtype);
4499 if (objnamespace && *objnamespace)
4500 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
4501 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
4502}
4503
4504/*
4505 * getNamespaces:
4506 * read all namespaces in the system catalogs and return them in the
4507 * NamespaceInfo* structure
4508 *
4509 * numNamespaces is set to the number of namespaces read in
4510 */
4511NamespaceInfo *
4512getNamespaces(Archive *fout, int *numNamespaces)
4513{
4514 DumpOptions *dopt = fout->dopt;
4515 PGresult *res;
4516 int ntups;
4517 int i;
4518 PQExpBuffer query;
4519 NamespaceInfo *nsinfo;
4520 int i_tableoid;
4521 int i_oid;
4522 int i_nspname;
4523 int i_rolname;
4524 int i_nspacl;
4525 int i_rnspacl;
4526 int i_initnspacl;
4527 int i_initrnspacl;
4528
4529 query = createPQExpBuffer();
4530
4531 /*
4532 * we fetch all namespaces including system ones, so that every object we
4533 * read in can be linked to a containing namespace.
4534 */
4535 if (fout->remoteVersion >= 90600)
4536 {
4537 PQExpBuffer acl_subquery = createPQExpBuffer();
4538 PQExpBuffer racl_subquery = createPQExpBuffer();
4539 PQExpBuffer init_acl_subquery = createPQExpBuffer();
4540 PQExpBuffer init_racl_subquery = createPQExpBuffer();
4541
4542 buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
4543 init_racl_subquery, "n.nspacl", "n.nspowner", "'n'",
4544 dopt->binary_upgrade);
4545
4546 appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
4547 "(%s nspowner) AS rolname, "
4548 "%s as nspacl, "
4549 "%s as rnspacl, "
4550 "%s as initnspacl, "
4551 "%s as initrnspacl "
4552 "FROM pg_namespace n "
4553 "LEFT JOIN pg_init_privs pip "
4554 "ON (n.oid = pip.objoid "
4555 "AND pip.classoid = 'pg_namespace'::regclass "
4556 "AND pip.objsubid = 0",
4557 username_subquery,
4558 acl_subquery->data,
4559 racl_subquery->data,
4560 init_acl_subquery->data,
4561 init_racl_subquery->data);
4562
4563 appendPQExpBuffer(query, ") ");
4564
4565 destroyPQExpBuffer(acl_subquery);
4566 destroyPQExpBuffer(racl_subquery);
4567 destroyPQExpBuffer(init_acl_subquery);
4568 destroyPQExpBuffer(init_racl_subquery);
4569 }
4570 else
4571 appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
4572 "(%s nspowner) AS rolname, "
4573 "nspacl, NULL as rnspacl, "
4574 "NULL AS initnspacl, NULL as initrnspacl "
4575 "FROM pg_namespace",
4576 username_subquery);
4577
4578 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4579
4580 ntups = PQntuples(res);
4581
4582 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
4583
4584 i_tableoid = PQfnumber(res, "tableoid");
4585 i_oid = PQfnumber(res, "oid");
4586 i_nspname = PQfnumber(res, "nspname");
4587 i_rolname = PQfnumber(res, "rolname");
4588 i_nspacl = PQfnumber(res, "nspacl");
4589 i_rnspacl = PQfnumber(res, "rnspacl");
4590 i_initnspacl = PQfnumber(res, "initnspacl");
4591 i_initrnspacl = PQfnumber(res, "initrnspacl");
4592
4593 for (i = 0; i < ntups; i++)
4594 {
4595 nsinfo[i].dobj.objType = DO_NAMESPACE;
4596 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4597 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4598 AssignDumpId(&nsinfo[i].dobj);
4599 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
4600 nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4601 nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
4602 nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl));
4603 nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl));
4604 nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl));
4605
4606 /* Decide whether to dump this namespace */
4607 selectDumpableNamespace(&nsinfo[i], fout);
4608
4609 /*
4610 * Do not try to dump ACL if the ACL is empty or the default.
4611 *
4612 * This is useful because, for some schemas/objects, the only
4613 * component we are going to try and dump is the ACL and if we can
4614 * remove that then 'dump' goes to zero/false and we don't consider
4615 * this object for dumping at all later on.
4616 */
4617 if (PQgetisnull(res, i, i_nspacl) && PQgetisnull(res, i, i_rnspacl) &&
4618 PQgetisnull(res, i, i_initnspacl) &&
4619 PQgetisnull(res, i, i_initrnspacl))
4620 nsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4621
4622 if (strlen(nsinfo[i].rolname) == 0)
4623 pg_log_warning("owner of schema \"%s\" appears to be invalid",
4624 nsinfo[i].dobj.name);
4625 }
4626
4627 PQclear(res);
4628 destroyPQExpBuffer(query);
4629
4630 *numNamespaces = ntups;
4631
4632 return nsinfo;
4633}
4634
4635/*
4636 * findNamespace:
4637 * given a namespace OID, look up the info read by getNamespaces
4638 */
4639static NamespaceInfo *
4640findNamespace(Archive *fout, Oid nsoid)
4641{
4642 NamespaceInfo *nsinfo;
4643
4644 nsinfo = findNamespaceByOid(nsoid);
4645 if (nsinfo == NULL)
4646 fatal("schema with OID %u does not exist", nsoid);
4647 return nsinfo;
4648}
4649
4650/*
4651 * getExtensions:
4652 * read all extensions in the system catalogs and return them in the
4653 * ExtensionInfo* structure
4654 *
4655 * numExtensions is set to the number of extensions read in
4656 */
4657ExtensionInfo *
4658getExtensions(Archive *fout, int *numExtensions)
4659{
4660 DumpOptions *dopt = fout->dopt;
4661 PGresult *res;
4662 int ntups;
4663 int i;
4664 PQExpBuffer query;
4665 ExtensionInfo *extinfo;
4666 int i_tableoid;
4667 int i_oid;
4668 int i_extname;
4669 int i_nspname;
4670 int i_extrelocatable;
4671 int i_extversion;
4672 int i_extconfig;
4673 int i_extcondition;
4674
4675 /*
4676 * Before 9.1, there are no extensions.
4677 */
4678 if (fout->remoteVersion < 90100)
4679 {
4680 *numExtensions = 0;
4681 return NULL;
4682 }
4683
4684 query = createPQExpBuffer();
4685
4686 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
4687 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
4688 "FROM pg_extension x "
4689 "JOIN pg_namespace n ON n.oid = x.extnamespace");
4690
4691 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4692
4693 ntups = PQntuples(res);
4694
4695 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
4696
4697 i_tableoid = PQfnumber(res, "tableoid");
4698 i_oid = PQfnumber(res, "oid");
4699 i_extname = PQfnumber(res, "extname");
4700 i_nspname = PQfnumber(res, "nspname");
4701 i_extrelocatable = PQfnumber(res, "extrelocatable");
4702 i_extversion = PQfnumber(res, "extversion");
4703 i_extconfig = PQfnumber(res, "extconfig");
4704 i_extcondition = PQfnumber(res, "extcondition");
4705
4706 for (i = 0; i < ntups; i++)
4707 {
4708 extinfo[i].dobj.objType = DO_EXTENSION;
4709 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4710 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4711 AssignDumpId(&extinfo[i].dobj);
4712 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
4713 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
4714 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
4715 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
4716 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
4717 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
4718
4719 /* Decide whether we want to dump it */
4720 selectDumpableExtension(&(extinfo[i]), dopt);
4721 }
4722
4723 PQclear(res);
4724 destroyPQExpBuffer(query);
4725
4726 *numExtensions = ntups;
4727
4728 return extinfo;
4729}
4730
4731/*
4732 * getTypes:
4733 * read all types in the system catalogs and return them in the
4734 * TypeInfo* structure
4735 *
4736 * numTypes is set to the number of types read in
4737 *
4738 * NB: this must run after getFuncs() because we assume we can do
4739 * findFuncByOid().
4740 */
4741TypeInfo *
4742getTypes(Archive *fout, int *numTypes)
4743{
4744 DumpOptions *dopt = fout->dopt;
4745 PGresult *res;
4746 int ntups;
4747 int i;
4748 PQExpBuffer query = createPQExpBuffer();
4749 TypeInfo *tyinfo;
4750 ShellTypeInfo *stinfo;
4751 int i_tableoid;
4752 int i_oid;
4753 int i_typname;
4754 int i_typnamespace;
4755 int i_typacl;
4756 int i_rtypacl;
4757 int i_inittypacl;
4758 int i_initrtypacl;
4759 int i_rolname;
4760 int i_typelem;
4761 int i_typrelid;
4762 int i_typrelkind;
4763 int i_typtype;
4764 int i_typisdefined;
4765 int i_isarray;
4766
4767 /*
4768 * we include even the built-in types because those may be used as array
4769 * elements by user-defined types
4770 *
4771 * we filter out the built-in types when we dump out the types
4772 *
4773 * same approach for undefined (shell) types and array types
4774 *
4775 * Note: as of 8.3 we can reliably detect whether a type is an
4776 * auto-generated array type by checking the element type's typarray.
4777 * (Before that the test is capable of generating false positives.) We
4778 * still check for name beginning with '_', though, so as to avoid the
4779 * cost of the subselect probe for all standard types. This would have to
4780 * be revisited if the backend ever allows renaming of array types.
4781 */
4782
4783 if (fout->remoteVersion >= 90600)
4784 {
4785 PQExpBuffer acl_subquery = createPQExpBuffer();
4786 PQExpBuffer racl_subquery = createPQExpBuffer();
4787 PQExpBuffer initacl_subquery = createPQExpBuffer();
4788 PQExpBuffer initracl_subquery = createPQExpBuffer();
4789
4790 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
4791 initracl_subquery, "t.typacl", "t.typowner", "'T'",
4792 dopt->binary_upgrade);
4793
4794 appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
4795 "t.typnamespace, "
4796 "%s AS typacl, "
4797 "%s AS rtypacl, "
4798 "%s AS inittypacl, "
4799 "%s AS initrtypacl, "
4800 "(%s t.typowner) AS rolname, "
4801 "t.typelem, t.typrelid, "
4802 "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
4803 "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
4804 "t.typtype, t.typisdefined, "
4805 "t.typname[0] = '_' AND t.typelem != 0 AND "
4806 "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
4807 "FROM pg_type t "
4808 "LEFT JOIN pg_init_privs pip ON "
4809 "(t.oid = pip.objoid "
4810 "AND pip.classoid = 'pg_type'::regclass "
4811 "AND pip.objsubid = 0) ",
4812 acl_subquery->data,
4813 racl_subquery->data,
4814 initacl_subquery->data,
4815 initracl_subquery->data,
4816 username_subquery);
4817
4818 destroyPQExpBuffer(acl_subquery);
4819 destroyPQExpBuffer(racl_subquery);
4820 destroyPQExpBuffer(initacl_subquery);
4821 destroyPQExpBuffer(initracl_subquery);
4822 }
4823 else if (fout->remoteVersion >= 90200)
4824 {
4825 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4826 "typnamespace, typacl, NULL as rtypacl, "
4827 "NULL AS inittypacl, NULL AS initrtypacl, "
4828 "(%s typowner) AS rolname, "
4829 "typelem, typrelid, "
4830 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4831 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4832 "typtype, typisdefined, "
4833 "typname[0] = '_' AND typelem != 0 AND "
4834 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
4835 "FROM pg_type",
4836 username_subquery);
4837 }
4838 else if (fout->remoteVersion >= 80300)
4839 {
4840 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4841 "typnamespace, NULL AS typacl, NULL as rtypacl, "
4842 "NULL AS inittypacl, NULL AS initrtypacl, "
4843 "(%s typowner) AS rolname, "
4844 "typelem, typrelid, "
4845 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4846 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4847 "typtype, typisdefined, "
4848 "typname[0] = '_' AND typelem != 0 AND "
4849 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
4850 "FROM pg_type",
4851 username_subquery);
4852 }
4853 else
4854 {
4855 appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
4856 "typnamespace, NULL AS typacl, NULL as rtypacl, "
4857 "NULL AS inittypacl, NULL AS initrtypacl, "
4858 "(%s typowner) AS rolname, "
4859 "typelem, typrelid, "
4860 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
4861 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
4862 "typtype, typisdefined, "
4863 "typname[0] = '_' AND typelem != 0 AS isarray "
4864 "FROM pg_type",
4865 username_subquery);
4866 }
4867
4868 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4869
4870 ntups = PQntuples(res);
4871
4872 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
4873
4874 i_tableoid = PQfnumber(res, "tableoid");
4875 i_oid = PQfnumber(res, "oid");
4876 i_typname = PQfnumber(res, "typname");
4877 i_typnamespace = PQfnumber(res, "typnamespace");
4878 i_typacl = PQfnumber(res, "typacl");
4879 i_rtypacl = PQfnumber(res, "rtypacl");
4880 i_inittypacl = PQfnumber(res, "inittypacl");
4881 i_initrtypacl = PQfnumber(res, "initrtypacl");
4882 i_rolname = PQfnumber(res, "rolname");
4883 i_typelem = PQfnumber(res, "typelem");
4884 i_typrelid = PQfnumber(res, "typrelid");
4885 i_typrelkind = PQfnumber(res, "typrelkind");
4886 i_typtype = PQfnumber(res, "typtype");
4887 i_typisdefined = PQfnumber(res, "typisdefined");
4888 i_isarray = PQfnumber(res, "isarray");
4889
4890 for (i = 0; i < ntups; i++)
4891 {
4892 tyinfo[i].dobj.objType = DO_TYPE;
4893 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
4894 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4895 AssignDumpId(&tyinfo[i].dobj);
4896 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
4897 tyinfo[i].dobj.namespace =
4898 findNamespace(fout,
4899 atooid(PQgetvalue(res, i, i_typnamespace)));
4900 tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
4901 tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl));
4902 tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl));
4903 tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl));
4904 tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl));
4905 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
4906 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
4907 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
4908 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
4909 tyinfo[i].shellType = NULL;
4910
4911 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
4912 tyinfo[i].isDefined = true;
4913 else
4914 tyinfo[i].isDefined = false;
4915
4916 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
4917 tyinfo[i].isArray = true;
4918 else
4919 tyinfo[i].isArray = false;
4920
4921 /* Decide whether we want to dump it */
4922 selectDumpableType(&tyinfo[i], fout);
4923
4924 /* Do not try to dump ACL if no ACL exists. */
4925 if (PQgetisnull(res, i, i_typacl) && PQgetisnull(res, i, i_rtypacl) &&
4926 PQgetisnull(res, i, i_inittypacl) &&
4927 PQgetisnull(res, i, i_initrtypacl))
4928 tyinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
4929
4930 /*
4931 * If it's a domain, fetch info about its constraints, if any
4932 */
4933 tyinfo[i].nDomChecks = 0;
4934 tyinfo[i].domChecks = NULL;
4935 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
4936 tyinfo[i].typtype == TYPTYPE_DOMAIN)
4937 getDomainConstraints(fout, &(tyinfo[i]));
4938
4939 /*
4940 * If it's a base type, make a DumpableObject representing a shell
4941 * definition of the type. We will need to dump that ahead of the I/O
4942 * functions for the type. Similarly, range types need a shell
4943 * definition in case they have a canonicalize function.
4944 *
4945 * Note: the shell type doesn't have a catId. You might think it
4946 * should copy the base type's catId, but then it might capture the
4947 * pg_depend entries for the type, which we don't want.
4948 */
4949 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
4950 (tyinfo[i].typtype == TYPTYPE_BASE ||
4951 tyinfo[i].typtype == TYPTYPE_RANGE))
4952 {
4953 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
4954 stinfo->dobj.objType = DO_SHELL_TYPE;
4955 stinfo->dobj.catId = nilCatalogId;
4956 AssignDumpId(&stinfo->dobj);
4957 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
4958 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
4959 stinfo->baseType = &(tyinfo[i]);
4960 tyinfo[i].shellType = stinfo;
4961
4962 /*
4963 * Initially mark the shell type as not to be dumped. We'll only
4964 * dump it if the I/O or canonicalize functions need to be dumped;
4965 * this is taken care of while sorting dependencies.
4966 */
4967 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
4968 }
4969
4970 if (strlen(tyinfo[i].rolname) == 0)
4971 pg_log_warning("owner of data type \"%s\" appears to be invalid",
4972 tyinfo[i].dobj.name);
4973 }
4974
4975 *numTypes = ntups;
4976
4977 PQclear(res);
4978
4979 destroyPQExpBuffer(query);
4980
4981 return tyinfo;
4982}
4983
4984/*
4985 * getOperators:
4986 * read all operators in the system catalogs and return them in the
4987 * OprInfo* structure
4988 *
4989 * numOprs is set to the number of operators read in
4990 */
4991OprInfo *
4992getOperators(Archive *fout, int *numOprs)
4993{
4994 PGresult *res;
4995 int ntups;
4996 int i;
4997 PQExpBuffer query = createPQExpBuffer();
4998 OprInfo *oprinfo;
4999 int i_tableoid;
5000 int i_oid;
5001 int i_oprname;
5002 int i_oprnamespace;
5003 int i_rolname;
5004 int i_oprkind;
5005 int i_oprcode;
5006
5007 /*
5008 * find all operators, including builtin operators; we filter out
5009 * system-defined operators at dump-out time.
5010 */
5011
5012 appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
5013 "oprnamespace, "
5014 "(%s oprowner) AS rolname, "
5015 "oprkind, "
5016 "oprcode::oid AS oprcode "
5017 "FROM pg_operator",
5018 username_subquery);
5019
5020 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5021
5022 ntups = PQntuples(res);
5023 *numOprs = ntups;
5024
5025 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
5026
5027 i_tableoid = PQfnumber(res, "tableoid");
5028 i_oid = PQfnumber(res, "oid");
5029 i_oprname = PQfnumber(res, "oprname");
5030 i_oprnamespace = PQfnumber(res, "oprnamespace");
5031 i_rolname = PQfnumber(res, "rolname");
5032 i_oprkind = PQfnumber(res, "oprkind");
5033 i_oprcode = PQfnumber(res, "oprcode");
5034
5035 for (i = 0; i < ntups; i++)
5036 {
5037 oprinfo[i].dobj.objType = DO_OPERATOR;
5038 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5039 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5040 AssignDumpId(&oprinfo[i].dobj);
5041 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
5042 oprinfo[i].dobj.namespace =
5043 findNamespace(fout,
5044 atooid(PQgetvalue(res, i, i_oprnamespace)));
5045 oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5046 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5047 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
5048
5049 /* Decide whether we want to dump it */
5050 selectDumpableObject(&(oprinfo[i].dobj), fout);
5051
5052 /* Operators do not currently have ACLs. */
5053 oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5054
5055 if (strlen(oprinfo[i].rolname) == 0)
5056 pg_log_warning("owner of operator \"%s\" appears to be invalid",
5057 oprinfo[i].dobj.name);
5058 }
5059
5060 PQclear(res);
5061
5062 destroyPQExpBuffer(query);
5063
5064 return oprinfo;
5065}
5066
5067/*
5068 * getCollations:
5069 * read all collations in the system catalogs and return them in the
5070 * CollInfo* structure
5071 *
5072 * numCollations is set to the number of collations read in
5073 */
5074CollInfo *
5075getCollations(Archive *fout, int *numCollations)
5076{
5077 PGresult *res;
5078 int ntups;
5079 int i;
5080 PQExpBuffer query;
5081 CollInfo *collinfo;
5082 int i_tableoid;
5083 int i_oid;
5084 int i_collname;
5085 int i_collnamespace;
5086 int i_rolname;
5087
5088 /* Collations didn't exist pre-9.1 */
5089 if (fout->remoteVersion < 90100)
5090 {
5091 *numCollations = 0;
5092 return NULL;
5093 }
5094
5095 query = createPQExpBuffer();
5096
5097 /*
5098 * find all collations, including builtin collations; we filter out
5099 * system-defined collations at dump-out time.
5100 */
5101
5102 appendPQExpBuffer(query, "SELECT tableoid, oid, collname, "
5103 "collnamespace, "
5104 "(%s collowner) AS rolname "
5105 "FROM pg_collation",
5106 username_subquery);
5107
5108 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5109
5110 ntups = PQntuples(res);
5111 *numCollations = ntups;
5112
5113 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
5114
5115 i_tableoid = PQfnumber(res, "tableoid");
5116 i_oid = PQfnumber(res, "oid");
5117 i_collname = PQfnumber(res, "collname");
5118 i_collnamespace = PQfnumber(res, "collnamespace");
5119 i_rolname = PQfnumber(res, "rolname");
5120
5121 for (i = 0; i < ntups; i++)
5122 {
5123 collinfo[i].dobj.objType = DO_COLLATION;
5124 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5125 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5126 AssignDumpId(&collinfo[i].dobj);
5127 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
5128 collinfo[i].dobj.namespace =
5129 findNamespace(fout,
5130 atooid(PQgetvalue(res, i, i_collnamespace)));
5131 collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5132
5133 /* Decide whether we want to dump it */
5134 selectDumpableObject(&(collinfo[i].dobj), fout);
5135
5136 /* Collations do not currently have ACLs. */
5137 collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5138 }
5139
5140 PQclear(res);
5141
5142 destroyPQExpBuffer(query);
5143
5144 return collinfo;
5145}
5146
5147/*
5148 * getConversions:
5149 * read all conversions in the system catalogs and return them in the
5150 * ConvInfo* structure
5151 *
5152 * numConversions is set to the number of conversions read in
5153 */
5154ConvInfo *
5155getConversions(Archive *fout, int *numConversions)
5156{
5157 PGresult *res;
5158 int ntups;
5159 int i;
5160 PQExpBuffer query;
5161 ConvInfo *convinfo;
5162 int i_tableoid;
5163 int i_oid;
5164 int i_conname;
5165 int i_connamespace;
5166 int i_rolname;
5167
5168 query = createPQExpBuffer();
5169
5170 /*
5171 * find all conversions, including builtin conversions; we filter out
5172 * system-defined conversions at dump-out time.
5173 */
5174
5175 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
5176 "connamespace, "
5177 "(%s conowner) AS rolname "
5178 "FROM pg_conversion",
5179 username_subquery);
5180
5181 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5182
5183 ntups = PQntuples(res);
5184 *numConversions = ntups;
5185
5186 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
5187
5188 i_tableoid = PQfnumber(res, "tableoid");
5189 i_oid = PQfnumber(res, "oid");
5190 i_conname = PQfnumber(res, "conname");
5191 i_connamespace = PQfnumber(res, "connamespace");
5192 i_rolname = PQfnumber(res, "rolname");
5193
5194 for (i = 0; i < ntups; i++)
5195 {
5196 convinfo[i].dobj.objType = DO_CONVERSION;
5197 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5198 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5199 AssignDumpId(&convinfo[i].dobj);
5200 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
5201 convinfo[i].dobj.namespace =
5202 findNamespace(fout,
5203 atooid(PQgetvalue(res, i, i_connamespace)));
5204 convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5205
5206 /* Decide whether we want to dump it */
5207 selectDumpableObject(&(convinfo[i].dobj), fout);
5208
5209 /* Conversions do not currently have ACLs. */
5210 convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5211 }
5212
5213 PQclear(res);
5214
5215 destroyPQExpBuffer(query);
5216
5217 return convinfo;
5218}
5219
5220/*
5221 * getAccessMethods:
5222 * read all user-defined access methods in the system catalogs and return
5223 * them in the AccessMethodInfo* structure
5224 *
5225 * numAccessMethods is set to the number of access methods read in
5226 */
5227AccessMethodInfo *
5228getAccessMethods(Archive *fout, int *numAccessMethods)
5229{
5230 PGresult *res;
5231 int ntups;
5232 int i;
5233 PQExpBuffer query;
5234 AccessMethodInfo *aminfo;
5235 int i_tableoid;
5236 int i_oid;
5237 int i_amname;
5238 int i_amhandler;
5239 int i_amtype;
5240
5241 /* Before 9.6, there are no user-defined access methods */
5242 if (fout->remoteVersion < 90600)
5243 {
5244 *numAccessMethods = 0;
5245 return NULL;
5246 }
5247
5248 query = createPQExpBuffer();
5249
5250 /* Select all access methods from pg_am table */
5251 appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
5252 "amhandler::pg_catalog.regproc AS amhandler "
5253 "FROM pg_am");
5254
5255 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5256
5257 ntups = PQntuples(res);
5258 *numAccessMethods = ntups;
5259
5260 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
5261
5262 i_tableoid = PQfnumber(res, "tableoid");
5263 i_oid = PQfnumber(res, "oid");
5264 i_amname = PQfnumber(res, "amname");
5265 i_amhandler = PQfnumber(res, "amhandler");
5266 i_amtype = PQfnumber(res, "amtype");
5267
5268 for (i = 0; i < ntups; i++)
5269 {
5270 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
5271 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5272 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5273 AssignDumpId(&aminfo[i].dobj);
5274 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
5275 aminfo[i].dobj.namespace = NULL;
5276 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
5277 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
5278
5279 /* Decide whether we want to dump it */
5280 selectDumpableAccessMethod(&(aminfo[i]), fout);
5281
5282 /* Access methods do not currently have ACLs. */
5283 aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5284 }
5285
5286 PQclear(res);
5287
5288 destroyPQExpBuffer(query);
5289
5290 return aminfo;
5291}
5292
5293
5294/*
5295 * getOpclasses:
5296 * read all opclasses in the system catalogs and return them in the
5297 * OpclassInfo* structure
5298 *
5299 * numOpclasses is set to the number of opclasses read in
5300 */
5301OpclassInfo *
5302getOpclasses(Archive *fout, int *numOpclasses)
5303{
5304 PGresult *res;
5305 int ntups;
5306 int i;
5307 PQExpBuffer query = createPQExpBuffer();
5308 OpclassInfo *opcinfo;
5309 int i_tableoid;
5310 int i_oid;
5311 int i_opcname;
5312 int i_opcnamespace;
5313 int i_rolname;
5314
5315 /*
5316 * find all opclasses, including builtin opclasses; we filter out
5317 * system-defined opclasses at dump-out time.
5318 */
5319
5320 appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
5321 "opcnamespace, "
5322 "(%s opcowner) AS rolname "
5323 "FROM pg_opclass",
5324 username_subquery);
5325
5326 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5327
5328 ntups = PQntuples(res);
5329 *numOpclasses = ntups;
5330
5331 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
5332
5333 i_tableoid = PQfnumber(res, "tableoid");
5334 i_oid = PQfnumber(res, "oid");
5335 i_opcname = PQfnumber(res, "opcname");
5336 i_opcnamespace = PQfnumber(res, "opcnamespace");
5337 i_rolname = PQfnumber(res, "rolname");
5338
5339 for (i = 0; i < ntups; i++)
5340 {
5341 opcinfo[i].dobj.objType = DO_OPCLASS;
5342 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5343 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5344 AssignDumpId(&opcinfo[i].dobj);
5345 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
5346 opcinfo[i].dobj.namespace =
5347 findNamespace(fout,
5348 atooid(PQgetvalue(res, i, i_opcnamespace)));
5349 opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5350
5351 /* Decide whether we want to dump it */
5352 selectDumpableObject(&(opcinfo[i].dobj), fout);
5353
5354 /* Op Classes do not currently have ACLs. */
5355 opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5356
5357 if (strlen(opcinfo[i].rolname) == 0)
5358 pg_log_warning("owner of operator class \"%s\" appears to be invalid",
5359 opcinfo[i].dobj.name);
5360 }
5361
5362 PQclear(res);
5363
5364 destroyPQExpBuffer(query);
5365
5366 return opcinfo;
5367}
5368
5369/*
5370 * getOpfamilies:
5371 * read all opfamilies in the system catalogs and return them in the
5372 * OpfamilyInfo* structure
5373 *
5374 * numOpfamilies is set to the number of opfamilies read in
5375 */
5376OpfamilyInfo *
5377getOpfamilies(Archive *fout, int *numOpfamilies)
5378{
5379 PGresult *res;
5380 int ntups;
5381 int i;
5382 PQExpBuffer query;
5383 OpfamilyInfo *opfinfo;
5384 int i_tableoid;
5385 int i_oid;
5386 int i_opfname;
5387 int i_opfnamespace;
5388 int i_rolname;
5389
5390 /* Before 8.3, there is no separate concept of opfamilies */
5391 if (fout->remoteVersion < 80300)
5392 {
5393 *numOpfamilies = 0;
5394 return NULL;
5395 }
5396
5397 query = createPQExpBuffer();
5398
5399 /*
5400 * find all opfamilies, including builtin opfamilies; we filter out
5401 * system-defined opfamilies at dump-out time.
5402 */
5403
5404 appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
5405 "opfnamespace, "
5406 "(%s opfowner) AS rolname "
5407 "FROM pg_opfamily",
5408 username_subquery);
5409
5410 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5411
5412 ntups = PQntuples(res);
5413 *numOpfamilies = ntups;
5414
5415 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
5416
5417 i_tableoid = PQfnumber(res, "tableoid");
5418 i_oid = PQfnumber(res, "oid");
5419 i_opfname = PQfnumber(res, "opfname");
5420 i_opfnamespace = PQfnumber(res, "opfnamespace");
5421 i_rolname = PQfnumber(res, "rolname");
5422
5423 for (i = 0; i < ntups; i++)
5424 {
5425 opfinfo[i].dobj.objType = DO_OPFAMILY;
5426 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5427 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5428 AssignDumpId(&opfinfo[i].dobj);
5429 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
5430 opfinfo[i].dobj.namespace =
5431 findNamespace(fout,
5432 atooid(PQgetvalue(res, i, i_opfnamespace)));
5433 opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5434
5435 /* Decide whether we want to dump it */
5436 selectDumpableObject(&(opfinfo[i].dobj), fout);
5437
5438 /* Extensions do not currently have ACLs. */
5439 opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5440
5441 if (strlen(opfinfo[i].rolname) == 0)
5442 pg_log_warning("owner of operator family \"%s\" appears to be invalid",
5443 opfinfo[i].dobj.name);
5444 }
5445
5446 PQclear(res);
5447
5448 destroyPQExpBuffer(query);
5449
5450 return opfinfo;
5451}
5452
5453/*
5454 * getAggregates:
5455 * read all the user-defined aggregates in the system catalogs and
5456 * return them in the AggInfo* structure
5457 *
5458 * numAggs is set to the number of aggregates read in
5459 */
5460AggInfo *
5461getAggregates(Archive *fout, int *numAggs)
5462{
5463 DumpOptions *dopt = fout->dopt;
5464 PGresult *res;
5465 int ntups;
5466 int i;
5467 PQExpBuffer query = createPQExpBuffer();
5468 AggInfo *agginfo;
5469 int i_tableoid;
5470 int i_oid;
5471 int i_aggname;
5472 int i_aggnamespace;
5473 int i_pronargs;
5474 int i_proargtypes;
5475 int i_rolname;
5476 int i_aggacl;
5477 int i_raggacl;
5478 int i_initaggacl;
5479 int i_initraggacl;
5480
5481 /*
5482 * Find all interesting aggregates. See comment in getFuncs() for the
5483 * rationale behind the filtering logic.
5484 */
5485 if (fout->remoteVersion >= 90600)
5486 {
5487 PQExpBuffer acl_subquery = createPQExpBuffer();
5488 PQExpBuffer racl_subquery = createPQExpBuffer();
5489 PQExpBuffer initacl_subquery = createPQExpBuffer();
5490 PQExpBuffer initracl_subquery = createPQExpBuffer();
5491 const char *agg_check;
5492
5493 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
5494 initracl_subquery, "p.proacl", "p.proowner", "'f'",
5495 dopt->binary_upgrade);
5496
5497 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
5498 : "p.proisagg");
5499
5500 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
5501 "p.proname AS aggname, "
5502 "p.pronamespace AS aggnamespace, "
5503 "p.pronargs, p.proargtypes, "
5504 "(%s p.proowner) AS rolname, "
5505 "%s AS aggacl, "
5506 "%s AS raggacl, "
5507 "%s AS initaggacl, "
5508 "%s AS initraggacl "
5509 "FROM pg_proc p "
5510 "LEFT JOIN pg_init_privs pip ON "
5511 "(p.oid = pip.objoid "
5512 "AND pip.classoid = 'pg_proc'::regclass "
5513 "AND pip.objsubid = 0) "
5514 "WHERE %s AND ("
5515 "p.pronamespace != "
5516 "(SELECT oid FROM pg_namespace "
5517 "WHERE nspname = 'pg_catalog') OR "
5518 "p.proacl IS DISTINCT FROM pip.initprivs",
5519 username_subquery,
5520 acl_subquery->data,
5521 racl_subquery->data,
5522 initacl_subquery->data,
5523 initracl_subquery->data,
5524 agg_check);
5525 if (dopt->binary_upgrade)
5526 appendPQExpBufferStr(query,
5527 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5528 "classid = 'pg_proc'::regclass AND "
5529 "objid = p.oid AND "
5530 "refclassid = 'pg_extension'::regclass AND "
5531 "deptype = 'e')");
5532 appendPQExpBufferChar(query, ')');
5533
5534 destroyPQExpBuffer(acl_subquery);
5535 destroyPQExpBuffer(racl_subquery);
5536 destroyPQExpBuffer(initacl_subquery);
5537 destroyPQExpBuffer(initracl_subquery);
5538 }
5539 else if (fout->remoteVersion >= 80200)
5540 {
5541 appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
5542 "pronamespace AS aggnamespace, "
5543 "pronargs, proargtypes, "
5544 "(%s proowner) AS rolname, "
5545 "proacl AS aggacl, "
5546 "NULL AS raggacl, "
5547 "NULL AS initaggacl, NULL AS initraggacl "
5548 "FROM pg_proc p "
5549 "WHERE proisagg AND ("
5550 "pronamespace != "
5551 "(SELECT oid FROM pg_namespace "
5552 "WHERE nspname = 'pg_catalog')",
5553 username_subquery);
5554 if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
5555 appendPQExpBufferStr(query,
5556 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5557 "classid = 'pg_proc'::regclass AND "
5558 "objid = p.oid AND "
5559 "refclassid = 'pg_extension'::regclass AND "
5560 "deptype = 'e')");
5561 appendPQExpBufferChar(query, ')');
5562 }
5563 else
5564 {
5565 appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
5566 "pronamespace AS aggnamespace, "
5567 "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END AS pronargs, "
5568 "proargtypes, "
5569 "(%s proowner) AS rolname, "
5570 "proacl AS aggacl, "
5571 "NULL AS raggacl, "
5572 "NULL AS initaggacl, NULL AS initraggacl "
5573 "FROM pg_proc "
5574 "WHERE proisagg "
5575 "AND pronamespace != "
5576 "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
5577 username_subquery);
5578 }
5579
5580 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5581
5582 ntups = PQntuples(res);
5583 *numAggs = ntups;
5584
5585 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
5586
5587 i_tableoid = PQfnumber(res, "tableoid");
5588 i_oid = PQfnumber(res, "oid");
5589 i_aggname = PQfnumber(res, "aggname");
5590 i_aggnamespace = PQfnumber(res, "aggnamespace");
5591 i_pronargs = PQfnumber(res, "pronargs");
5592 i_proargtypes = PQfnumber(res, "proargtypes");
5593 i_rolname = PQfnumber(res, "rolname");
5594 i_aggacl = PQfnumber(res, "aggacl");
5595 i_raggacl = PQfnumber(res, "raggacl");
5596 i_initaggacl = PQfnumber(res, "initaggacl");
5597 i_initraggacl = PQfnumber(res, "initraggacl");
5598
5599 for (i = 0; i < ntups; i++)
5600 {
5601 agginfo[i].aggfn.dobj.objType = DO_AGG;
5602 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5603 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5604 AssignDumpId(&agginfo[i].aggfn.dobj);
5605 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
5606 agginfo[i].aggfn.dobj.namespace =
5607 findNamespace(fout,
5608 atooid(PQgetvalue(res, i, i_aggnamespace)));
5609 agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5610 if (strlen(agginfo[i].aggfn.rolname) == 0)
5611 pg_log_warning("owner of aggregate function \"%s\" appears to be invalid",
5612 agginfo[i].aggfn.dobj.name);
5613 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
5614 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
5615 agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl));
5616 agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl));
5617 agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl));
5618 agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl));
5619 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
5620 if (agginfo[i].aggfn.nargs == 0)
5621 agginfo[i].aggfn.argtypes = NULL;
5622 else
5623 {
5624 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
5625 parseOidArray(PQgetvalue(res, i, i_proargtypes),
5626 agginfo[i].aggfn.argtypes,
5627 agginfo[i].aggfn.nargs);
5628 }
5629
5630 /* Decide whether we want to dump it */
5631 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
5632
5633 /* Do not try to dump ACL if no ACL exists. */
5634 if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) &&
5635 PQgetisnull(res, i, i_initaggacl) &&
5636 PQgetisnull(res, i, i_initraggacl))
5637 agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL;
5638 }
5639
5640 PQclear(res);
5641
5642 destroyPQExpBuffer(query);
5643
5644 return agginfo;
5645}
5646
5647/*
5648 * getFuncs:
5649 * read all the user-defined functions in the system catalogs and
5650 * return them in the FuncInfo* structure
5651 *
5652 * numFuncs is set to the number of functions read in
5653 */
5654FuncInfo *
5655getFuncs(Archive *fout, int *numFuncs)
5656{
5657 DumpOptions *dopt = fout->dopt;
5658 PGresult *res;
5659 int ntups;
5660 int i;
5661 PQExpBuffer query = createPQExpBuffer();
5662 FuncInfo *finfo;
5663 int i_tableoid;
5664 int i_oid;
5665 int i_proname;
5666 int i_pronamespace;
5667 int i_rolname;
5668 int i_prolang;
5669 int i_pronargs;
5670 int i_proargtypes;
5671 int i_prorettype;
5672 int i_proacl;
5673 int i_rproacl;
5674 int i_initproacl;
5675 int i_initrproacl;
5676
5677 /*
5678 * Find all interesting functions. This is a bit complicated:
5679 *
5680 * 1. Always exclude aggregates; those are handled elsewhere.
5681 *
5682 * 2. Always exclude functions that are internally dependent on something
5683 * else, since presumably those will be created as a result of creating
5684 * the something else. This currently acts only to suppress constructor
5685 * functions for range types (so we only need it in 9.2 and up). Note
5686 * this is OK only because the constructors don't have any dependencies
5687 * the range type doesn't have; otherwise we might not get creation
5688 * ordering correct.
5689 *
5690 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
5691 * they're members of extensions and we are in binary-upgrade mode then
5692 * include them, since we want to dump extension members individually in
5693 * that mode. Also, if they are used by casts or transforms then we need
5694 * to gather the information about them, though they won't be dumped if
5695 * they are built-in. Also, in 9.6 and up, include functions in
5696 * pg_catalog if they have an ACL different from what's shown in
5697 * pg_init_privs.
5698 */
5699 if (fout->remoteVersion >= 90600)
5700 {
5701 PQExpBuffer acl_subquery = createPQExpBuffer();
5702 PQExpBuffer racl_subquery = createPQExpBuffer();
5703 PQExpBuffer initacl_subquery = createPQExpBuffer();
5704 PQExpBuffer initracl_subquery = createPQExpBuffer();
5705 const char *not_agg_check;
5706
5707 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
5708 initracl_subquery, "p.proacl", "p.proowner", "'f'",
5709 dopt->binary_upgrade);
5710
5711 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
5712 : "NOT p.proisagg");
5713
5714 appendPQExpBuffer(query,
5715 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
5716 "p.pronargs, p.proargtypes, p.prorettype, "
5717 "%s AS proacl, "
5718 "%s AS rproacl, "
5719 "%s AS initproacl, "
5720 "%s AS initrproacl, "
5721 "p.pronamespace, "
5722 "(%s p.proowner) AS rolname "
5723 "FROM pg_proc p "
5724 "LEFT JOIN pg_init_privs pip ON "
5725 "(p.oid = pip.objoid "
5726 "AND pip.classoid = 'pg_proc'::regclass "
5727 "AND pip.objsubid = 0) "
5728 "WHERE %s"
5729 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
5730 "WHERE classid = 'pg_proc'::regclass AND "
5731 "objid = p.oid AND deptype = 'i')"
5732 "\n AND ("
5733 "\n pronamespace != "
5734 "(SELECT oid FROM pg_namespace "
5735 "WHERE nspname = 'pg_catalog')"
5736 "\n OR EXISTS (SELECT 1 FROM pg_cast"
5737 "\n WHERE pg_cast.oid > %u "
5738 "\n AND p.oid = pg_cast.castfunc)"
5739 "\n OR EXISTS (SELECT 1 FROM pg_transform"
5740 "\n WHERE pg_transform.oid > %u AND "
5741 "\n (p.oid = pg_transform.trffromsql"
5742 "\n OR p.oid = pg_transform.trftosql))",
5743 acl_subquery->data,
5744 racl_subquery->data,
5745 initacl_subquery->data,
5746 initracl_subquery->data,
5747 username_subquery,
5748 not_agg_check,
5749 g_last_builtin_oid,
5750 g_last_builtin_oid);
5751 if (dopt->binary_upgrade)
5752 appendPQExpBufferStr(query,
5753 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5754 "classid = 'pg_proc'::regclass AND "
5755 "objid = p.oid AND "
5756 "refclassid = 'pg_extension'::regclass AND "
5757 "deptype = 'e')");
5758 appendPQExpBufferStr(query,
5759 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
5760 appendPQExpBufferChar(query, ')');
5761
5762 destroyPQExpBuffer(acl_subquery);
5763 destroyPQExpBuffer(racl_subquery);
5764 destroyPQExpBuffer(initacl_subquery);
5765 destroyPQExpBuffer(initracl_subquery);
5766 }
5767 else
5768 {
5769 appendPQExpBuffer(query,
5770 "SELECT tableoid, oid, proname, prolang, "
5771 "pronargs, proargtypes, prorettype, proacl, "
5772 "NULL as rproacl, "
5773 "NULL as initproacl, NULL AS initrproacl, "
5774 "pronamespace, "
5775 "(%s proowner) AS rolname "
5776 "FROM pg_proc p "
5777 "WHERE NOT proisagg",
5778 username_subquery);
5779 if (fout->remoteVersion >= 90200)
5780 appendPQExpBufferStr(query,
5781 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
5782 "WHERE classid = 'pg_proc'::regclass AND "
5783 "objid = p.oid AND deptype = 'i')");
5784 appendPQExpBuffer(query,
5785 "\n AND ("
5786 "\n pronamespace != "
5787 "(SELECT oid FROM pg_namespace "
5788 "WHERE nspname = 'pg_catalog')"
5789 "\n OR EXISTS (SELECT 1 FROM pg_cast"
5790 "\n WHERE pg_cast.oid > '%u'::oid"
5791 "\n AND p.oid = pg_cast.castfunc)",
5792 g_last_builtin_oid);
5793
5794 if (fout->remoteVersion >= 90500)
5795 appendPQExpBuffer(query,
5796 "\n OR EXISTS (SELECT 1 FROM pg_transform"
5797 "\n WHERE pg_transform.oid > '%u'::oid"
5798 "\n AND (p.oid = pg_transform.trffromsql"
5799 "\n OR p.oid = pg_transform.trftosql))",
5800 g_last_builtin_oid);
5801
5802 if (dopt->binary_upgrade && fout->remoteVersion >= 90100)
5803 appendPQExpBufferStr(query,
5804 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
5805 "classid = 'pg_proc'::regclass AND "
5806 "objid = p.oid AND "
5807 "refclassid = 'pg_extension'::regclass AND "
5808 "deptype = 'e')");
5809 appendPQExpBufferChar(query, ')');
5810 }
5811
5812 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5813
5814 ntups = PQntuples(res);
5815
5816 *numFuncs = ntups;
5817
5818 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
5819
5820 i_tableoid = PQfnumber(res, "tableoid");
5821 i_oid = PQfnumber(res, "oid");
5822 i_proname = PQfnumber(res, "proname");
5823 i_pronamespace = PQfnumber(res, "pronamespace");
5824 i_rolname = PQfnumber(res, "rolname");
5825 i_prolang = PQfnumber(res, "prolang");
5826 i_pronargs = PQfnumber(res, "pronargs");
5827 i_proargtypes = PQfnumber(res, "proargtypes");
5828 i_prorettype = PQfnumber(res, "prorettype");
5829 i_proacl = PQfnumber(res, "proacl");
5830 i_rproacl = PQfnumber(res, "rproacl");
5831 i_initproacl = PQfnumber(res, "initproacl");
5832 i_initrproacl = PQfnumber(res, "initrproacl");
5833
5834 for (i = 0; i < ntups; i++)
5835 {
5836 finfo[i].dobj.objType = DO_FUNC;
5837 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5838 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5839 AssignDumpId(&finfo[i].dobj);
5840 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
5841 finfo[i].dobj.namespace =
5842 findNamespace(fout,
5843 atooid(PQgetvalue(res, i, i_pronamespace)));
5844 finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5845 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
5846 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
5847 finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl));
5848 finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl));
5849 finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl));
5850 finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl));
5851 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
5852 if (finfo[i].nargs == 0)
5853 finfo[i].argtypes = NULL;
5854 else
5855 {
5856 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
5857 parseOidArray(PQgetvalue(res, i, i_proargtypes),
5858 finfo[i].argtypes, finfo[i].nargs);
5859 }
5860
5861 /* Decide whether we want to dump it */
5862 selectDumpableObject(&(finfo[i].dobj), fout);
5863
5864 /* Do not try to dump ACL if no ACL exists. */
5865 if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) &&
5866 PQgetisnull(res, i, i_initproacl) &&
5867 PQgetisnull(res, i, i_initrproacl))
5868 finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
5869
5870 if (strlen(finfo[i].rolname) == 0)
5871 pg_log_warning("owner of function \"%s\" appears to be invalid",
5872 finfo[i].dobj.name);
5873 }
5874
5875 PQclear(res);
5876
5877 destroyPQExpBuffer(query);
5878
5879 return finfo;
5880}
5881
5882/*
5883 * getTables
5884 * read all the tables (no indexes)
5885 * in the system catalogs return them in the TableInfo* structure
5886 *
5887 * numTables is set to the number of tables read in
5888 */
5889TableInfo *
5890getTables(Archive *fout, int *numTables)
5891{
5892 DumpOptions *dopt = fout->dopt;
5893 PGresult *res;
5894 int ntups;
5895 int i;
5896 PQExpBuffer query = createPQExpBuffer();
5897 TableInfo *tblinfo;
5898 int i_reltableoid;
5899 int i_reloid;
5900 int i_relname;
5901 int i_relnamespace;
5902 int i_relkind;
5903 int i_relacl;
5904 int i_rrelacl;
5905 int i_initrelacl;
5906 int i_initrrelacl;
5907 int i_rolname;
5908 int i_relchecks;
5909 int i_relhastriggers;
5910 int i_relhasindex;
5911 int i_relhasrules;
5912 int i_relrowsec;
5913 int i_relforcerowsec;
5914 int i_relhasoids;
5915 int i_relfrozenxid;
5916 int i_relminmxid;
5917 int i_toastoid;
5918 int i_toastfrozenxid;
5919 int i_toastminmxid;
5920 int i_relpersistence;
5921 int i_relispopulated;
5922 int i_relreplident;
5923 int i_owning_tab;
5924 int i_owning_col;
5925 int i_reltablespace;
5926 int i_reloptions;
5927 int i_checkoption;
5928 int i_toastreloptions;
5929 int i_reloftype;
5930 int i_relpages;
5931 int i_is_identity_sequence;
5932 int i_changed_acl;
5933 int i_partkeydef;
5934 int i_ispartition;
5935 int i_partbound;
5936 int i_amname;
5937
5938 /*
5939 * Find all the tables and table-like objects.
5940 *
5941 * We include system catalogs, so that we can work if a user table is
5942 * defined to inherit from a system catalog (pretty weird, but...)
5943 *
5944 * We ignore relations that are not ordinary tables, sequences, views,
5945 * materialized views, composite types, or foreign tables.
5946 *
5947 * Composite-type table entries won't be dumped as such, but we have to
5948 * make a DumpableObject for them so that we can track dependencies of the
5949 * composite type (pg_depend entries for columns of the composite type
5950 * link to the pg_class entry not the pg_type entry).
5951 *
5952 * Note: in this phase we should collect only a minimal amount of
5953 * information about each table, basically just enough to decide if it is
5954 * interesting. We must fetch all tables in this phase because otherwise
5955 * we cannot correctly identify inherited columns, owned sequences, etc.
5956 *
5957 * We purposefully ignore toast OIDs for partitioned tables; the reason is
5958 * that versions 10 and 11 have them, but 12 does not, so emitting them
5959 * causes the upgrade to fail.
5960 */
5961
5962 if (fout->remoteVersion >= 90600)
5963 {
5964 char *partkeydef = "NULL";
5965 char *ispartition = "false";
5966 char *partbound = "NULL";
5967 char *relhasoids = "c.relhasoids";
5968
5969 PQExpBuffer acl_subquery = createPQExpBuffer();
5970 PQExpBuffer racl_subquery = createPQExpBuffer();
5971 PQExpBuffer initacl_subquery = createPQExpBuffer();
5972 PQExpBuffer initracl_subquery = createPQExpBuffer();
5973
5974 PQExpBuffer attacl_subquery = createPQExpBuffer();
5975 PQExpBuffer attracl_subquery = createPQExpBuffer();
5976 PQExpBuffer attinitacl_subquery = createPQExpBuffer();
5977 PQExpBuffer attinitracl_subquery = createPQExpBuffer();
5978
5979 /*
5980 * Collect the information about any partitioned tables, which were
5981 * added in PG10.
5982 */
5983
5984 if (fout->remoteVersion >= 100000)
5985 {
5986 partkeydef = "pg_get_partkeydef(c.oid)";
5987 ispartition = "c.relispartition";
5988 partbound = "pg_get_expr(c.relpartbound, c.oid)";
5989 }
5990
5991 /* In PG12 upwards WITH OIDS does not exist anymore. */
5992 if (fout->remoteVersion >= 120000)
5993 relhasoids = "'f'::bool";
5994
5995 /*
5996 * Left join to pick up dependency info linking sequences to their
5997 * owning column, if any (note this dependency is AUTO as of 8.2)
5998 *
5999 * Left join to detect if any privileges are still as-set-at-init, in
6000 * which case we won't dump out ACL commands for those.
6001 */
6002
6003 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
6004 initracl_subquery, "c.relacl", "c.relowner",
6005 "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6006 " THEN 's' ELSE 'r' END::\"char\"",
6007 dopt->binary_upgrade);
6008
6009 buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery,
6010 attinitracl_subquery, "at.attacl", "c.relowner", "'c'",
6011 dopt->binary_upgrade);
6012
6013 appendPQExpBuffer(query,
6014 "SELECT c.tableoid, c.oid, c.relname, "
6015 "%s AS relacl, %s as rrelacl, "
6016 "%s AS initrelacl, %s as initrrelacl, "
6017 "c.relkind, c.relnamespace, "
6018 "(%s c.relowner) AS rolname, "
6019 "c.relchecks, c.relhastriggers, "
6020 "c.relhasindex, c.relhasrules, %s AS relhasoids, "
6021 "c.relrowsecurity, c.relforcerowsecurity, "
6022 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
6023 "tc.relfrozenxid AS tfrozenxid, "
6024 "tc.relminmxid AS tminmxid, "
6025 "c.relpersistence, c.relispopulated, "
6026 "c.relreplident, c.relpages, am.amname, "
6027 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6028 "d.refobjid AS owning_tab, "
6029 "d.refobjsubid AS owning_col, "
6030 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6031 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6032 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6033 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
6034 "tc.reloptions AS toast_reloptions, "
6035 "c.relkind = '%c' AND EXISTS (SELECT 1 FROM pg_depend WHERE classid = 'pg_class'::regclass AND objid = c.oid AND objsubid = 0 AND refclassid = 'pg_class'::regclass AND deptype = 'i') AS is_identity_sequence, "
6036 "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON "
6037 "(c.oid = pip.objoid "
6038 "AND pip.classoid = 'pg_class'::regclass "
6039 "AND pip.objsubid = at.attnum)"
6040 "WHERE at.attrelid = c.oid AND ("
6041 "%s IS NOT NULL "
6042 "OR %s IS NOT NULL "
6043 "OR %s IS NOT NULL "
6044 "OR %s IS NOT NULL"
6045 "))"
6046 "AS changed_acl, "
6047 "%s AS partkeydef, "
6048 "%s AS ispartition, "
6049 "%s AS partbound "
6050 "FROM pg_class c "
6051 "LEFT JOIN pg_depend d ON "
6052 "(c.relkind = '%c' AND "
6053 "d.classid = c.tableoid AND d.objid = c.oid AND "
6054 "d.objsubid = 0 AND "
6055 "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i')) "
6056 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid AND c.relkind <> '%c') "
6057 "LEFT JOIN pg_am am ON (c.relam = am.oid) "
6058 "LEFT JOIN pg_init_privs pip ON "
6059 "(c.oid = pip.objoid "
6060 "AND pip.classoid = 'pg_class'::regclass "
6061 "AND pip.objsubid = 0) "
6062 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') "
6063 "ORDER BY c.oid",
6064 acl_subquery->data,
6065 racl_subquery->data,
6066 initacl_subquery->data,
6067 initracl_subquery->data,
6068 username_subquery,
6069 relhasoids,
6070 RELKIND_SEQUENCE,
6071 attacl_subquery->data,
6072 attracl_subquery->data,
6073 attinitacl_subquery->data,
6074 attinitracl_subquery->data,
6075 partkeydef,
6076 ispartition,
6077 partbound,
6078 RELKIND_SEQUENCE,
6079 RELKIND_PARTITIONED_TABLE,
6080 RELKIND_RELATION, RELKIND_SEQUENCE,
6081 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
6082 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
6083 RELKIND_PARTITIONED_TABLE);
6084
6085 destroyPQExpBuffer(acl_subquery);
6086 destroyPQExpBuffer(racl_subquery);
6087 destroyPQExpBuffer(initacl_subquery);
6088 destroyPQExpBuffer(initracl_subquery);
6089
6090 destroyPQExpBuffer(attacl_subquery);
6091 destroyPQExpBuffer(attracl_subquery);
6092 destroyPQExpBuffer(attinitacl_subquery);
6093 destroyPQExpBuffer(attinitracl_subquery);
6094 }
6095 else if (fout->remoteVersion >= 90500)
6096 {
6097 /*
6098 * Left join to pick up dependency info linking sequences to their
6099 * owning column, if any (note this dependency is AUTO as of 8.2)
6100 */
6101 appendPQExpBuffer(query,
6102 "SELECT c.tableoid, c.oid, c.relname, "
6103 "c.relacl, NULL as rrelacl, "
6104 "NULL AS initrelacl, NULL AS initrrelacl, "
6105 "c.relkind, "
6106 "c.relnamespace, "
6107 "(%s c.relowner) AS rolname, "
6108 "c.relchecks, c.relhastriggers, "
6109 "c.relhasindex, c.relhasrules, c.relhasoids, "
6110 "c.relrowsecurity, c.relforcerowsecurity, "
6111 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
6112 "tc.relfrozenxid AS tfrozenxid, "
6113 "tc.relminmxid AS tminmxid, "
6114 "c.relpersistence, c.relispopulated, "
6115 "c.relreplident, c.relpages, "
6116 "NULL AS amname, "
6117 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6118 "d.refobjid AS owning_tab, "
6119 "d.refobjsubid AS owning_col, "
6120 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6121 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6122 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6123 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
6124 "tc.reloptions AS toast_reloptions, "
6125 "NULL AS changed_acl, "
6126 "NULL AS partkeydef, "
6127 "false AS ispartition, "
6128 "NULL AS partbound "
6129 "FROM pg_class c "
6130 "LEFT JOIN pg_depend d ON "
6131 "(c.relkind = '%c' AND "
6132 "d.classid = c.tableoid AND d.objid = c.oid AND "
6133 "d.objsubid = 0 AND "
6134 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6135 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6136 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
6137 "ORDER BY c.oid",
6138 username_subquery,
6139 RELKIND_SEQUENCE,
6140 RELKIND_RELATION, RELKIND_SEQUENCE,
6141 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
6142 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
6143 }
6144 else if (fout->remoteVersion >= 90400)
6145 {
6146 /*
6147 * Left join to pick up dependency info linking sequences to their
6148 * owning column, if any (note this dependency is AUTO as of 8.2)
6149 */
6150 appendPQExpBuffer(query,
6151 "SELECT c.tableoid, c.oid, c.relname, "
6152 "c.relacl, NULL as rrelacl, "
6153 "NULL AS initrelacl, NULL AS initrrelacl, "
6154 "c.relkind, "
6155 "c.relnamespace, "
6156 "(%s c.relowner) AS rolname, "
6157 "c.relchecks, c.relhastriggers, "
6158 "c.relhasindex, c.relhasrules, c.relhasoids, "
6159 "'f'::bool AS relrowsecurity, "
6160 "'f'::bool AS relforcerowsecurity, "
6161 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
6162 "tc.relfrozenxid AS tfrozenxid, "
6163 "tc.relminmxid AS tminmxid, "
6164 "c.relpersistence, c.relispopulated, "
6165 "c.relreplident, c.relpages, "
6166 "NULL AS amname, "
6167 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6168 "d.refobjid AS owning_tab, "
6169 "d.refobjsubid AS owning_col, "
6170 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6171 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6172 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6173 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
6174 "tc.reloptions AS toast_reloptions, "
6175 "NULL AS changed_acl, "
6176 "NULL AS partkeydef, "
6177 "false AS ispartition, "
6178 "NULL AS partbound "
6179 "FROM pg_class c "
6180 "LEFT JOIN pg_depend d ON "
6181 "(c.relkind = '%c' AND "
6182 "d.classid = c.tableoid AND d.objid = c.oid AND "
6183 "d.objsubid = 0 AND "
6184 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6185 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6186 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
6187 "ORDER BY c.oid",
6188 username_subquery,
6189 RELKIND_SEQUENCE,
6190 RELKIND_RELATION, RELKIND_SEQUENCE,
6191 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
6192 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
6193 }
6194 else if (fout->remoteVersion >= 90300)
6195 {
6196 /*
6197 * Left join to pick up dependency info linking sequences to their
6198 * owning column, if any (note this dependency is AUTO as of 8.2)
6199 */
6200 appendPQExpBuffer(query,
6201 "SELECT c.tableoid, c.oid, c.relname, "
6202 "c.relacl, NULL as rrelacl, "
6203 "NULL AS initrelacl, NULL AS initrrelacl, "
6204 "c.relkind, "
6205 "c.relnamespace, "
6206 "(%s c.relowner) AS rolname, "
6207 "c.relchecks, c.relhastriggers, "
6208 "c.relhasindex, c.relhasrules, c.relhasoids, "
6209 "'f'::bool AS relrowsecurity, "
6210 "'f'::bool AS relforcerowsecurity, "
6211 "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
6212 "tc.relfrozenxid AS tfrozenxid, "
6213 "tc.relminmxid AS tminmxid, "
6214 "c.relpersistence, c.relispopulated, "
6215 "'d' AS relreplident, c.relpages, "
6216 "NULL AS amname, "
6217 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6218 "d.refobjid AS owning_tab, "
6219 "d.refobjsubid AS owning_col, "
6220 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6221 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6222 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6223 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
6224 "tc.reloptions AS toast_reloptions, "
6225 "NULL AS changed_acl, "
6226 "NULL AS partkeydef, "
6227 "false AS ispartition, "
6228 "NULL AS partbound "
6229 "FROM pg_class c "
6230 "LEFT JOIN pg_depend d ON "
6231 "(c.relkind = '%c' AND "
6232 "d.classid = c.tableoid AND d.objid = c.oid AND "
6233 "d.objsubid = 0 AND "
6234 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6235 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6236 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
6237 "ORDER BY c.oid",
6238 username_subquery,
6239 RELKIND_SEQUENCE,
6240 RELKIND_RELATION, RELKIND_SEQUENCE,
6241 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
6242 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
6243 }
6244 else if (fout->remoteVersion >= 90100)
6245 {
6246 /*
6247 * Left join to pick up dependency info linking sequences to their
6248 * owning column, if any (note this dependency is AUTO as of 8.2)
6249 */
6250 appendPQExpBuffer(query,
6251 "SELECT c.tableoid, c.oid, c.relname, "
6252 "c.relacl, NULL as rrelacl, "
6253 "NULL AS initrelacl, NULL AS initrrelacl, "
6254 "c.relkind, "
6255 "c.relnamespace, "
6256 "(%s c.relowner) AS rolname, "
6257 "c.relchecks, c.relhastriggers, "
6258 "c.relhasindex, c.relhasrules, c.relhasoids, "
6259 "'f'::bool AS relrowsecurity, "
6260 "'f'::bool AS relforcerowsecurity, "
6261 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6262 "tc.relfrozenxid AS tfrozenxid, "
6263 "0 AS tminmxid, "
6264 "c.relpersistence, 't' as relispopulated, "
6265 "'d' AS relreplident, c.relpages, "
6266 "NULL AS amname, "
6267 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6268 "d.refobjid AS owning_tab, "
6269 "d.refobjsubid AS owning_col, "
6270 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6271 "c.reloptions AS reloptions, "
6272 "tc.reloptions AS toast_reloptions, "
6273 "NULL AS changed_acl, "
6274 "NULL AS partkeydef, "
6275 "false AS ispartition, "
6276 "NULL AS partbound "
6277 "FROM pg_class c "
6278 "LEFT JOIN pg_depend d ON "
6279 "(c.relkind = '%c' AND "
6280 "d.classid = c.tableoid AND d.objid = c.oid AND "
6281 "d.objsubid = 0 AND "
6282 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6283 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6284 "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
6285 "ORDER BY c.oid",
6286 username_subquery,
6287 RELKIND_SEQUENCE,
6288 RELKIND_RELATION, RELKIND_SEQUENCE,
6289 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
6290 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
6291 }
6292 else if (fout->remoteVersion >= 90000)
6293 {
6294 /*
6295 * Left join to pick up dependency info linking sequences to their
6296 * owning column, if any (note this dependency is AUTO as of 8.2)
6297 */
6298 appendPQExpBuffer(query,
6299 "SELECT c.tableoid, c.oid, c.relname, "
6300 "c.relacl, NULL as rrelacl, "
6301 "NULL AS initrelacl, NULL AS initrrelacl, "
6302 "c.relkind, "
6303 "c.relnamespace, "
6304 "(%s c.relowner) AS rolname, "
6305 "c.relchecks, c.relhastriggers, "
6306 "c.relhasindex, c.relhasrules, c.relhasoids, "
6307 "'f'::bool AS relrowsecurity, "
6308 "'f'::bool AS relforcerowsecurity, "
6309 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6310 "tc.relfrozenxid AS tfrozenxid, "
6311 "0 AS tminmxid, "
6312 "'p' AS relpersistence, 't' as relispopulated, "
6313 "'d' AS relreplident, c.relpages, "
6314 "NULL AS amname, "
6315 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
6316 "d.refobjid AS owning_tab, "
6317 "d.refobjsubid AS owning_col, "
6318 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6319 "c.reloptions AS reloptions, "
6320 "tc.reloptions AS toast_reloptions, "
6321 "NULL AS changed_acl, "
6322 "NULL AS partkeydef, "
6323 "false AS ispartition, "
6324 "NULL AS partbound "
6325 "FROM pg_class c "
6326 "LEFT JOIN pg_depend d ON "
6327 "(c.relkind = '%c' AND "
6328 "d.classid = c.tableoid AND d.objid = c.oid AND "
6329 "d.objsubid = 0 AND "
6330 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6331 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6332 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6333 "ORDER BY c.oid",
6334 username_subquery,
6335 RELKIND_SEQUENCE,
6336 RELKIND_RELATION, RELKIND_SEQUENCE,
6337 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6338 }
6339 else if (fout->remoteVersion >= 80400)
6340 {
6341 /*
6342 * Left join to pick up dependency info linking sequences to their
6343 * owning column, if any (note this dependency is AUTO as of 8.2)
6344 */
6345 appendPQExpBuffer(query,
6346 "SELECT c.tableoid, c.oid, c.relname, "
6347 "c.relacl, NULL as rrelacl, "
6348 "NULL AS initrelacl, NULL AS initrrelacl, "
6349 "c.relkind, "
6350 "c.relnamespace, "
6351 "(%s c.relowner) AS rolname, "
6352 "c.relchecks, c.relhastriggers, "
6353 "c.relhasindex, c.relhasrules, c.relhasoids, "
6354 "'f'::bool AS relrowsecurity, "
6355 "'f'::bool AS relforcerowsecurity, "
6356 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6357 "tc.relfrozenxid AS tfrozenxid, "
6358 "0 AS tminmxid, "
6359 "'p' AS relpersistence, 't' as relispopulated, "
6360 "'d' AS relreplident, c.relpages, "
6361 "NULL AS amname, "
6362 "NULL AS reloftype, "
6363 "d.refobjid AS owning_tab, "
6364 "d.refobjsubid AS owning_col, "
6365 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6366 "c.reloptions AS reloptions, "
6367 "tc.reloptions AS toast_reloptions, "
6368 "NULL AS changed_acl, "
6369 "NULL AS partkeydef, "
6370 "false AS ispartition, "
6371 "NULL AS partbound "
6372 "FROM pg_class c "
6373 "LEFT JOIN pg_depend d ON "
6374 "(c.relkind = '%c' AND "
6375 "d.classid = c.tableoid AND d.objid = c.oid AND "
6376 "d.objsubid = 0 AND "
6377 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6378 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6379 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6380 "ORDER BY c.oid",
6381 username_subquery,
6382 RELKIND_SEQUENCE,
6383 RELKIND_RELATION, RELKIND_SEQUENCE,
6384 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6385 }
6386 else if (fout->remoteVersion >= 80200)
6387 {
6388 /*
6389 * Left join to pick up dependency info linking sequences to their
6390 * owning column, if any (note this dependency is AUTO as of 8.2)
6391 */
6392 appendPQExpBuffer(query,
6393 "SELECT c.tableoid, c.oid, c.relname, "
6394 "c.relacl, NULL as rrelacl, "
6395 "NULL AS initrelacl, NULL AS initrrelacl, "
6396 "c.relkind, "
6397 "c.relnamespace, "
6398 "(%s c.relowner) AS rolname, "
6399 "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
6400 "c.relhasindex, c.relhasrules, c.relhasoids, "
6401 "'f'::bool AS relrowsecurity, "
6402 "'f'::bool AS relforcerowsecurity, "
6403 "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
6404 "tc.relfrozenxid AS tfrozenxid, "
6405 "0 AS tminmxid, "
6406 "'p' AS relpersistence, 't' as relispopulated, "
6407 "'d' AS relreplident, c.relpages, "
6408 "NULL AS amname, "
6409 "NULL AS reloftype, "
6410 "d.refobjid AS owning_tab, "
6411 "d.refobjsubid AS owning_col, "
6412 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6413 "c.reloptions AS reloptions, "
6414 "NULL AS toast_reloptions, "
6415 "NULL AS changed_acl, "
6416 "NULL AS partkeydef, "
6417 "false AS ispartition, "
6418 "NULL AS partbound "
6419 "FROM pg_class c "
6420 "LEFT JOIN pg_depend d ON "
6421 "(c.relkind = '%c' AND "
6422 "d.classid = c.tableoid AND d.objid = c.oid AND "
6423 "d.objsubid = 0 AND "
6424 "d.refclassid = c.tableoid AND d.deptype = 'a') "
6425 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
6426 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
6427 "ORDER BY c.oid",
6428 username_subquery,
6429 RELKIND_SEQUENCE,
6430 RELKIND_RELATION, RELKIND_SEQUENCE,
6431 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6432 }
6433 else
6434 {
6435 /*
6436 * Left join to pick up dependency info linking sequences to their
6437 * owning column, if any
6438 */
6439 appendPQExpBuffer(query,
6440 "SELECT c.tableoid, c.oid, relname, "
6441 "relacl, NULL as rrelacl, "
6442 "NULL AS initrelacl, NULL AS initrrelacl, "
6443 "relkind, relnamespace, "
6444 "(%s relowner) AS rolname, "
6445 "relchecks, (reltriggers <> 0) AS relhastriggers, "
6446 "relhasindex, relhasrules, relhasoids, "
6447 "'f'::bool AS relrowsecurity, "
6448 "'f'::bool AS relforcerowsecurity, "
6449 "0 AS relfrozenxid, 0 AS relminmxid,"
6450 "0 AS toid, "
6451 "0 AS tfrozenxid, 0 AS tminmxid,"
6452 "'p' AS relpersistence, 't' as relispopulated, "
6453 "'d' AS relreplident, relpages, "
6454 "NULL AS amname, "
6455 "NULL AS reloftype, "
6456 "d.refobjid AS owning_tab, "
6457 "d.refobjsubid AS owning_col, "
6458 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
6459 "NULL AS reloptions, "
6460 "NULL AS toast_reloptions, "
6461 "NULL AS changed_acl, "
6462 "NULL AS partkeydef, "
6463 "false AS ispartition, "
6464 "NULL AS partbound "
6465 "FROM pg_class c "
6466 "LEFT JOIN pg_depend d ON "
6467 "(c.relkind = '%c' AND "
6468 "d.classid = c.tableoid AND d.objid = c.oid AND "
6469 "d.objsubid = 0 AND "
6470 "d.refclassid = c.tableoid AND d.deptype = 'i') "
6471 "WHERE relkind in ('%c', '%c', '%c', '%c') "
6472 "ORDER BY c.oid",
6473 username_subquery,
6474 RELKIND_SEQUENCE,
6475 RELKIND_RELATION, RELKIND_SEQUENCE,
6476 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
6477 }
6478
6479 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6480
6481 ntups = PQntuples(res);
6482
6483 *numTables = ntups;
6484
6485 /*
6486 * Extract data from result and lock dumpable tables. We do the locking
6487 * before anything else, to minimize the window wherein a table could
6488 * disappear under us.
6489 *
6490 * Note that we have to save info about all tables here, even when dumping
6491 * only one, because we don't yet know which tables might be inheritance
6492 * ancestors of the target table.
6493 */
6494 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
6495
6496 i_reltableoid = PQfnumber(res, "tableoid");
6497 i_reloid = PQfnumber(res, "oid");
6498 i_relname = PQfnumber(res, "relname");
6499 i_relnamespace = PQfnumber(res, "relnamespace");
6500 i_relacl = PQfnumber(res, "relacl");
6501 i_rrelacl = PQfnumber(res, "rrelacl");
6502 i_initrelacl = PQfnumber(res, "initrelacl");
6503 i_initrrelacl = PQfnumber(res, "initrrelacl");
6504 i_relkind = PQfnumber(res, "relkind");
6505 i_rolname = PQfnumber(res, "rolname");
6506 i_relchecks = PQfnumber(res, "relchecks");
6507 i_relhastriggers = PQfnumber(res, "relhastriggers");
6508 i_relhasindex = PQfnumber(res, "relhasindex");
6509 i_relhasrules = PQfnumber(res, "relhasrules");
6510 i_relrowsec = PQfnumber(res, "relrowsecurity");
6511 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
6512 i_relhasoids = PQfnumber(res, "relhasoids");
6513 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
6514 i_relminmxid = PQfnumber(res, "relminmxid");
6515 i_toastoid = PQfnumber(res, "toid");
6516 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
6517 i_toastminmxid = PQfnumber(res, "tminmxid");
6518 i_relpersistence = PQfnumber(res, "relpersistence");
6519 i_relispopulated = PQfnumber(res, "relispopulated");
6520 i_relreplident = PQfnumber(res, "relreplident");
6521 i_relpages = PQfnumber(res, "relpages");
6522 i_owning_tab = PQfnumber(res, "owning_tab");
6523 i_owning_col = PQfnumber(res, "owning_col");
6524 i_reltablespace = PQfnumber(res, "reltablespace");
6525 i_reloptions = PQfnumber(res, "reloptions");
6526 i_checkoption = PQfnumber(res, "checkoption");
6527 i_toastreloptions = PQfnumber(res, "toast_reloptions");
6528 i_reloftype = PQfnumber(res, "reloftype");
6529 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
6530 i_changed_acl = PQfnumber(res, "changed_acl");
6531 i_partkeydef = PQfnumber(res, "partkeydef");
6532 i_ispartition = PQfnumber(res, "ispartition");
6533 i_partbound = PQfnumber(res, "partbound");
6534 i_amname = PQfnumber(res, "amname");
6535
6536 if (dopt->lockWaitTimeout)
6537 {
6538 /*
6539 * Arrange to fail instead of waiting forever for a table lock.
6540 *
6541 * NB: this coding assumes that the only queries issued within the
6542 * following loop are LOCK TABLEs; else the timeout may be undesirably
6543 * applied to other things too.
6544 */
6545 resetPQExpBuffer(query);
6546 appendPQExpBufferStr(query, "SET statement_timeout = ");
6547 appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
6548 ExecuteSqlStatement(fout, query->data);
6549 }
6550
6551 for (i = 0; i < ntups; i++)
6552 {
6553 tblinfo[i].dobj.objType = DO_TABLE;
6554 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
6555 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
6556 AssignDumpId(&tblinfo[i].dobj);
6557 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
6558 tblinfo[i].dobj.namespace =
6559 findNamespace(fout,
6560 atooid(PQgetvalue(res, i, i_relnamespace)));
6561 tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
6562 tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl));
6563 tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl));
6564 tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl));
6565 tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl));
6566 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
6567 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
6568 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
6569 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
6570 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
6571 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
6572 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
6573 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
6574 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
6575 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
6576 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
6577 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
6578 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
6579 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
6580 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
6581 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
6582 if (PQgetisnull(res, i, i_reloftype))
6583 tblinfo[i].reloftype = NULL;
6584 else
6585 tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype));
6586 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
6587 if (PQgetisnull(res, i, i_owning_tab))
6588 {
6589 tblinfo[i].owning_tab = InvalidOid;
6590 tblinfo[i].owning_col = 0;
6591 }
6592 else
6593 {
6594 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
6595 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
6596 }
6597 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
6598 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
6599 if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption))
6600 tblinfo[i].checkoption = NULL;
6601 else
6602 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
6603 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
6604 if (PQgetisnull(res, i, i_amname))
6605 tblinfo[i].amname = NULL;
6606 else
6607 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
6608
6609 /* other fields were zeroed above */
6610
6611 /*
6612 * Decide whether we want to dump this table.
6613 */
6614 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
6615 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
6616 else
6617 selectDumpableTable(&tblinfo[i], fout);
6618
6619 /*
6620 * If the table-level and all column-level ACLs for this table are
6621 * unchanged, then we don't need to worry about including the ACLs for
6622 * this table. If any column-level ACLs have been changed, the
6623 * 'changed_acl' column from the query will indicate that.
6624 *
6625 * This can result in a significant performance improvement in cases
6626 * where we are only looking to dump out the ACL (eg: pg_catalog).
6627 */
6628 if (PQgetisnull(res, i, i_relacl) && PQgetisnull(res, i, i_rrelacl) &&
6629 PQgetisnull(res, i, i_initrelacl) &&
6630 PQgetisnull(res, i, i_initrrelacl) &&
6631 strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0)
6632 tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
6633
6634 tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
6635 tblinfo[i].dummy_view = false; /* might get set during sort */
6636 tblinfo[i].postponed_def = false; /* might get set during sort */
6637
6638 tblinfo[i].is_identity_sequence = (i_is_identity_sequence >= 0 &&
6639 strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
6640
6641 /* Partition key string or NULL */
6642 tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef));
6643 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
6644 tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound));
6645
6646 /*
6647 * Read-lock target tables to make sure they aren't DROPPED or altered
6648 * in schema before we get around to dumping them.
6649 *
6650 * Note that we don't explicitly lock parents of the target tables; we
6651 * assume our lock on the child is enough to prevent schema
6652 * alterations to parent tables.
6653 *
6654 * NOTE: it'd be kinda nice to lock other relations too, not only
6655 * plain or partitioned tables, but the backend doesn't presently
6656 * allow that.
6657 *
6658 * We only need to lock the table for certain components; see
6659 * pg_dump.h
6660 */
6661 if (tblinfo[i].dobj.dump &&
6662 (tblinfo[i].relkind == RELKIND_RELATION ||
6663 tblinfo->relkind == RELKIND_PARTITIONED_TABLE) &&
6664 (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK))
6665 {
6666 resetPQExpBuffer(query);
6667 appendPQExpBuffer(query,
6668 "LOCK TABLE %s IN ACCESS SHARE MODE",
6669 fmtQualifiedDumpable(&tblinfo[i]));
6670 ExecuteSqlStatement(fout, query->data);
6671 }
6672
6673 /* Emit notice if join for owner failed */
6674 if (strlen(tblinfo[i].rolname) == 0)
6675 pg_log_warning("owner of table \"%s\" appears to be invalid",
6676 tblinfo[i].dobj.name);
6677 }
6678
6679 if (dopt->lockWaitTimeout)
6680 {
6681 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
6682 }
6683
6684 PQclear(res);
6685
6686 destroyPQExpBuffer(query);
6687
6688 return tblinfo;
6689}
6690
6691/*
6692 * getOwnedSeqs
6693 * identify owned sequences and mark them as dumpable if owning table is
6694 *
6695 * We used to do this in getTables(), but it's better to do it after the
6696 * index used by findTableByOid() has been set up.
6697 */
6698void
6699getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
6700{
6701 int i;
6702
6703 /*
6704 * Force sequences that are "owned" by table columns to be dumped whenever
6705 * their owning table is being dumped.
6706 */
6707 for (i = 0; i < numTables; i++)
6708 {
6709 TableInfo *seqinfo = &tblinfo[i];
6710 TableInfo *owning_tab;
6711
6712 if (!OidIsValid(seqinfo->owning_tab))
6713 continue; /* not an owned sequence */
6714
6715 owning_tab = findTableByOid(seqinfo->owning_tab);
6716 if (owning_tab == NULL)
6717 fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
6718 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
6719
6720 /*
6721 * Only dump identity sequences if we're going to dump the table that
6722 * it belongs to.
6723 */
6724 if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE &&
6725 seqinfo->is_identity_sequence)
6726 {
6727 seqinfo->dobj.dump = DUMP_COMPONENT_NONE;
6728 continue;
6729 }
6730
6731 /*
6732 * Otherwise we need to dump the components that are being dumped for
6733 * the table and any components which the sequence is explicitly
6734 * marked with.
6735 *
6736 * We can't simply use the set of components which are being dumped
6737 * for the table as the table might be in an extension (and only the
6738 * non-extension components, eg: ACLs if changed, security labels, and
6739 * policies, are being dumped) while the sequence is not (and
6740 * therefore the definition and other components should also be
6741 * dumped).
6742 *
6743 * If the sequence is part of the extension then it should be properly
6744 * marked by checkExtensionMembership() and this will be a no-op as
6745 * the table will be equivalently marked.
6746 */
6747 seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump;
6748
6749 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
6750 seqinfo->interesting = true;
6751 }
6752}
6753
6754/*
6755 * getInherits
6756 * read all the inheritance information
6757 * from the system catalogs return them in the InhInfo* structure
6758 *
6759 * numInherits is set to the number of pairs read in
6760 */
6761InhInfo *
6762getInherits(Archive *fout, int *numInherits)
6763{
6764 PGresult *res;
6765 int ntups;
6766 int i;
6767 PQExpBuffer query = createPQExpBuffer();
6768 InhInfo *inhinfo;
6769
6770 int i_inhrelid;
6771 int i_inhparent;
6772
6773 /*
6774 * Find all the inheritance information, excluding implicit inheritance
6775 * via partitioning. We handle that case using getPartitions(), because
6776 * we want more information about partitions than just the parent-child
6777 * relationship.
6778 */
6779 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
6780
6781 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6782
6783 ntups = PQntuples(res);
6784
6785 *numInherits = ntups;
6786
6787 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
6788
6789 i_inhrelid = PQfnumber(res, "inhrelid");
6790 i_inhparent = PQfnumber(res, "inhparent");
6791
6792 for (i = 0; i < ntups; i++)
6793 {
6794 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
6795 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
6796 }
6797
6798 PQclear(res);
6799
6800 destroyPQExpBuffer(query);
6801
6802 return inhinfo;
6803}
6804
6805/*
6806 * getIndexes
6807 * get information about every index on a dumpable table
6808 *
6809 * Note: index data is not returned directly to the caller, but it
6810 * does get entered into the DumpableObject tables.
6811 */
6812void
6813getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
6814{
6815 int i,
6816 j;
6817 PQExpBuffer query = createPQExpBuffer();
6818 PGresult *res;
6819 IndxInfo *indxinfo;
6820 ConstraintInfo *constrinfo;
6821 int i_tableoid,
6822 i_oid,
6823 i_indexname,
6824 i_parentidx,
6825 i_indexdef,
6826 i_indnkeyatts,
6827 i_indnatts,
6828 i_indkey,
6829 i_indisclustered,
6830 i_indisreplident,
6831 i_contype,
6832 i_conname,
6833 i_condeferrable,
6834 i_condeferred,
6835 i_contableoid,
6836 i_conoid,
6837 i_condef,
6838 i_tablespace,
6839 i_indreloptions,
6840 i_indstatcols,
6841 i_indstatvals;
6842 int ntups;
6843
6844 for (i = 0; i < numTables; i++)
6845 {
6846 TableInfo *tbinfo = &tblinfo[i];
6847
6848 if (!tbinfo->hasindex)
6849 continue;
6850
6851 /*
6852 * Ignore indexes of tables whose definitions are not to be dumped.
6853 *
6854 * We also need indexes on partitioned tables which have partitions to
6855 * be dumped, in order to dump the indexes on the partitions.
6856 */
6857 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6858 !tbinfo->interesting)
6859 continue;
6860
6861 pg_log_info("reading indexes for table \"%s.%s\"",
6862 tbinfo->dobj.namespace->dobj.name,
6863 tbinfo->dobj.name);
6864
6865 /*
6866 * The point of the messy-looking outer join is to find a constraint
6867 * that is related by an internal dependency link to the index. If we
6868 * find one, create a CONSTRAINT entry linked to the INDEX entry. We
6869 * assume an index won't have more than one internal dependency.
6870 *
6871 * As of 9.0 we don't need to look at pg_depend but can check for a
6872 * match to pg_constraint.conindid. The check on conrelid is
6873 * redundant but useful because that column is indexed while conindid
6874 * is not.
6875 */
6876 resetPQExpBuffer(query);
6877 if (fout->remoteVersion >= 110000)
6878 {
6879 appendPQExpBuffer(query,
6880 "SELECT t.tableoid, t.oid, "
6881 "t.relname AS indexname, "
6882 "inh.inhparent AS parentidx, "
6883 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6884 "i.indnkeyatts AS indnkeyatts, "
6885 "i.indnatts AS indnatts, "
6886 "i.indkey, i.indisclustered, "
6887 "i.indisreplident, "
6888 "c.contype, c.conname, "
6889 "c.condeferrable, c.condeferred, "
6890 "c.tableoid AS contableoid, "
6891 "c.oid AS conoid, "
6892 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
6893 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6894 "t.reloptions AS indreloptions, "
6895 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
6896 " FROM pg_catalog.pg_attribute "
6897 " WHERE attrelid = i.indexrelid AND "
6898 " attstattarget >= 0) AS indstatcols,"
6899 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
6900 " FROM pg_catalog.pg_attribute "
6901 " WHERE attrelid = i.indexrelid AND "
6902 " attstattarget >= 0) AS indstatvals "
6903 "FROM pg_catalog.pg_index i "
6904 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6905 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
6906 "LEFT JOIN pg_catalog.pg_constraint c "
6907 "ON (i.indrelid = c.conrelid AND "
6908 "i.indexrelid = c.conindid AND "
6909 "c.contype IN ('p','u','x')) "
6910 "LEFT JOIN pg_catalog.pg_inherits inh "
6911 "ON (inh.inhrelid = indexrelid) "
6912 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6913 "AND (i.indisvalid OR t2.relkind = 'p') "
6914 "AND i.indisready "
6915 "ORDER BY indexname",
6916 tbinfo->dobj.catId.oid);
6917 }
6918 else if (fout->remoteVersion >= 90400)
6919 {
6920 /*
6921 * the test on indisready is necessary in 9.2, and harmless in
6922 * earlier/later versions
6923 */
6924 appendPQExpBuffer(query,
6925 "SELECT t.tableoid, t.oid, "
6926 "t.relname AS indexname, "
6927 "0 AS parentidx, "
6928 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6929 "i.indnatts AS indnkeyatts, "
6930 "i.indnatts AS indnatts, "
6931 "i.indkey, i.indisclustered, "
6932 "i.indisreplident, "
6933 "c.contype, c.conname, "
6934 "c.condeferrable, c.condeferred, "
6935 "c.tableoid AS contableoid, "
6936 "c.oid AS conoid, "
6937 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
6938 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6939 "t.reloptions AS indreloptions, "
6940 "'' AS indstatcols, "
6941 "'' AS indstatvals "
6942 "FROM pg_catalog.pg_index i "
6943 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6944 "LEFT JOIN pg_catalog.pg_constraint c "
6945 "ON (i.indrelid = c.conrelid AND "
6946 "i.indexrelid = c.conindid AND "
6947 "c.contype IN ('p','u','x')) "
6948 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6949 "AND i.indisvalid AND i.indisready "
6950 "ORDER BY indexname",
6951 tbinfo->dobj.catId.oid);
6952 }
6953 else if (fout->remoteVersion >= 90000)
6954 {
6955 /*
6956 * the test on indisready is necessary in 9.2, and harmless in
6957 * earlier/later versions
6958 */
6959 appendPQExpBuffer(query,
6960 "SELECT t.tableoid, t.oid, "
6961 "t.relname AS indexname, "
6962 "0 AS parentidx, "
6963 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6964 "i.indnatts AS indnkeyatts, "
6965 "i.indnatts AS indnatts, "
6966 "i.indkey, i.indisclustered, "
6967 "false AS indisreplident, "
6968 "c.contype, c.conname, "
6969 "c.condeferrable, c.condeferred, "
6970 "c.tableoid AS contableoid, "
6971 "c.oid AS conoid, "
6972 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
6973 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
6974 "t.reloptions AS indreloptions, "
6975 "'' AS indstatcols, "
6976 "'' AS indstatvals "
6977 "FROM pg_catalog.pg_index i "
6978 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
6979 "LEFT JOIN pg_catalog.pg_constraint c "
6980 "ON (i.indrelid = c.conrelid AND "
6981 "i.indexrelid = c.conindid AND "
6982 "c.contype IN ('p','u','x')) "
6983 "WHERE i.indrelid = '%u'::pg_catalog.oid "
6984 "AND i.indisvalid AND i.indisready "
6985 "ORDER BY indexname",
6986 tbinfo->dobj.catId.oid);
6987 }
6988 else if (fout->remoteVersion >= 80200)
6989 {
6990 appendPQExpBuffer(query,
6991 "SELECT t.tableoid, t.oid, "
6992 "t.relname AS indexname, "
6993 "0 AS parentidx, "
6994 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
6995 "i.indnatts AS indnkeyatts, "
6996 "i.indnatts AS indnatts, "
6997 "i.indkey, i.indisclustered, "
6998 "false AS indisreplident, "
6999 "c.contype, c.conname, "
7000 "c.condeferrable, c.condeferred, "
7001 "c.tableoid AS contableoid, "
7002 "c.oid AS conoid, "
7003 "null AS condef, "
7004 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7005 "t.reloptions AS indreloptions, "
7006 "'' AS indstatcols, "
7007 "'' AS indstatvals "
7008 "FROM pg_catalog.pg_index i "
7009 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7010 "LEFT JOIN pg_catalog.pg_depend d "
7011 "ON (d.classid = t.tableoid "
7012 "AND d.objid = t.oid "
7013 "AND d.deptype = 'i') "
7014 "LEFT JOIN pg_catalog.pg_constraint c "
7015 "ON (d.refclassid = c.tableoid "
7016 "AND d.refobjid = c.oid) "
7017 "WHERE i.indrelid = '%u'::pg_catalog.oid "
7018 "AND i.indisvalid "
7019 "ORDER BY indexname",
7020 tbinfo->dobj.catId.oid);
7021 }
7022 else
7023 {
7024 appendPQExpBuffer(query,
7025 "SELECT t.tableoid, t.oid, "
7026 "t.relname AS indexname, "
7027 "0 AS parentidx, "
7028 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7029 "t.relnatts AS indnkeyatts, "
7030 "t.relnatts AS indnatts, "
7031 "i.indkey, i.indisclustered, "
7032 "false AS indisreplident, "
7033 "c.contype, c.conname, "
7034 "c.condeferrable, c.condeferred, "
7035 "c.tableoid AS contableoid, "
7036 "c.oid AS conoid, "
7037 "null AS condef, "
7038 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7039 "null AS indreloptions, "
7040 "'' AS indstatcols, "
7041 "'' AS indstatvals "
7042 "FROM pg_catalog.pg_index i "
7043 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7044 "LEFT JOIN pg_catalog.pg_depend d "
7045 "ON (d.classid = t.tableoid "
7046 "AND d.objid = t.oid "
7047 "AND d.deptype = 'i') "
7048 "LEFT JOIN pg_catalog.pg_constraint c "
7049 "ON (d.refclassid = c.tableoid "
7050 "AND d.refobjid = c.oid) "
7051 "WHERE i.indrelid = '%u'::pg_catalog.oid "
7052 "ORDER BY indexname",
7053 tbinfo->dobj.catId.oid);
7054 }
7055
7056 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7057
7058 ntups = PQntuples(res);
7059
7060 i_tableoid = PQfnumber(res, "tableoid");
7061 i_oid = PQfnumber(res, "oid");
7062 i_indexname = PQfnumber(res, "indexname");
7063 i_parentidx = PQfnumber(res, "parentidx");
7064 i_indexdef = PQfnumber(res, "indexdef");
7065 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7066 i_indnatts = PQfnumber(res, "indnatts");
7067 i_indkey = PQfnumber(res, "indkey");
7068 i_indisclustered = PQfnumber(res, "indisclustered");
7069 i_indisreplident = PQfnumber(res, "indisreplident");
7070 i_contype = PQfnumber(res, "contype");
7071 i_conname = PQfnumber(res, "conname");
7072 i_condeferrable = PQfnumber(res, "condeferrable");
7073 i_condeferred = PQfnumber(res, "condeferred");
7074 i_contableoid = PQfnumber(res, "contableoid");
7075 i_conoid = PQfnumber(res, "conoid");
7076 i_condef = PQfnumber(res, "condef");
7077 i_tablespace = PQfnumber(res, "tablespace");
7078 i_indreloptions = PQfnumber(res, "indreloptions");
7079 i_indstatcols = PQfnumber(res, "indstatcols");
7080 i_indstatvals = PQfnumber(res, "indstatvals");
7081
7082 tbinfo->indexes = indxinfo =
7083 (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7084 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7085 tbinfo->numIndexes = ntups;
7086
7087 for (j = 0; j < ntups; j++)
7088 {
7089 char contype;
7090
7091 indxinfo[j].dobj.objType = DO_INDEX;
7092 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7093 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7094 AssignDumpId(&indxinfo[j].dobj);
7095 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7096 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7097 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7098 indxinfo[j].indextable = tbinfo;
7099 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7100 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7101 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7102 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7103 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7104 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7105 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7106 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7107 parseOidArray(PQgetvalue(res, j, i_indkey),
7108 indxinfo[j].indkeys, indxinfo[j].indnattrs);
7109 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7110 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7111 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7112 contype = *(PQgetvalue(res, j, i_contype));
7113
7114 if (contype == 'p' || contype == 'u' || contype == 'x')
7115 {
7116 /*
7117 * If we found a constraint matching the index, create an
7118 * entry for it.
7119 */
7120 constrinfo[j].dobj.objType = DO_CONSTRAINT;
7121 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7122 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7123 AssignDumpId(&constrinfo[j].dobj);
7124 constrinfo[j].dobj.dump = tbinfo->dobj.dump;
7125 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7126 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7127 constrinfo[j].contable = tbinfo;
7128 constrinfo[j].condomain = NULL;
7129 constrinfo[j].contype = contype;
7130 if (contype == 'x')
7131 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7132 else
7133 constrinfo[j].condef = NULL;
7134 constrinfo[j].confrelid = InvalidOid;
7135 constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
7136 constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7137 constrinfo[j].condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7138 constrinfo[j].conislocal = true;
7139 constrinfo[j].separate = true;
7140
7141 indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
7142 }
7143 else
7144 {
7145 /* Plain secondary index */
7146 indxinfo[j].indexconstraint = 0;
7147 }
7148 }
7149
7150 PQclear(res);
7151 }
7152
7153 destroyPQExpBuffer(query);
7154}
7155
7156/*
7157 * getExtendedStatistics
7158 * get information about extended-statistics objects.
7159 *
7160 * Note: extended statistics data is not returned directly to the caller, but
7161 * it does get entered into the DumpableObject tables.
7162 */
7163void
7164getExtendedStatistics(Archive *fout)
7165{
7166 PQExpBuffer query;
7167 PGresult *res;
7168 StatsExtInfo *statsextinfo;
7169 int ntups;
7170 int i_tableoid;
7171 int i_oid;
7172 int i_stxname;
7173 int i_stxnamespace;
7174 int i_rolname;
7175 int i;
7176
7177 /* Extended statistics were new in v10 */
7178 if (fout->remoteVersion < 100000)
7179 return;
7180
7181 query = createPQExpBuffer();
7182
7183 appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, "
7184 "stxnamespace, (%s stxowner) AS rolname "
7185 "FROM pg_catalog.pg_statistic_ext",
7186 username_subquery);
7187
7188 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7189
7190 ntups = PQntuples(res);
7191
7192 i_tableoid = PQfnumber(res, "tableoid");
7193 i_oid = PQfnumber(res, "oid");
7194 i_stxname = PQfnumber(res, "stxname");
7195 i_stxnamespace = PQfnumber(res, "stxnamespace");
7196 i_rolname = PQfnumber(res, "rolname");
7197
7198 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7199
7200 for (i = 0; i < ntups; i++)
7201 {
7202 statsextinfo[i].dobj.objType = DO_STATSEXT;
7203 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7204 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7205 AssignDumpId(&statsextinfo[i].dobj);
7206 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7207 statsextinfo[i].dobj.namespace =
7208 findNamespace(fout,
7209 atooid(PQgetvalue(res, i, i_stxnamespace)));
7210 statsextinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
7211
7212 /* Decide whether we want to dump it */
7213 selectDumpableObject(&(statsextinfo[i].dobj), fout);
7214
7215 /* Stats objects do not currently have ACLs. */
7216 statsextinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7217 }
7218
7219 PQclear(res);
7220 destroyPQExpBuffer(query);
7221}
7222
7223/*
7224 * getConstraints
7225 *
7226 * Get info about constraints on dumpable tables.
7227 *
7228 * Currently handles foreign keys only.
7229 * Unique and primary key constraints are handled with indexes,
7230 * while check constraints are processed in getTableAttrs().
7231 */
7232void
7233getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7234{
7235 int i,
7236 j;
7237 ConstraintInfo *constrinfo;
7238 PQExpBuffer query;
7239 PGresult *res;
7240 int i_contableoid,
7241 i_conoid,
7242 i_conname,
7243 i_confrelid,
7244 i_condef;
7245 int ntups;
7246
7247 query = createPQExpBuffer();
7248
7249 for (i = 0; i < numTables; i++)
7250 {
7251 TableInfo *tbinfo = &tblinfo[i];
7252
7253 /*
7254 * For partitioned tables, foreign keys have no triggers so they must
7255 * be included anyway in case some foreign keys are defined.
7256 */
7257 if ((!tbinfo->hastriggers &&
7258 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7259 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7260 continue;
7261
7262 pg_log_info("reading foreign key constraints for table \"%s.%s\"",
7263 tbinfo->dobj.namespace->dobj.name,
7264 tbinfo->dobj.name);
7265
7266 resetPQExpBuffer(query);
7267 if (fout->remoteVersion >= 110000)
7268 appendPQExpBuffer(query,
7269 "SELECT tableoid, oid, conname, confrelid, "
7270 "pg_catalog.pg_get_constraintdef(oid) AS condef "
7271 "FROM pg_catalog.pg_constraint "
7272 "WHERE conrelid = '%u'::pg_catalog.oid "
7273 "AND conparentid = 0 "
7274 "AND contype = 'f'",
7275 tbinfo->dobj.catId.oid);
7276 else
7277 appendPQExpBuffer(query,
7278 "SELECT tableoid, oid, conname, confrelid, "
7279 "pg_catalog.pg_get_constraintdef(oid) AS condef "
7280 "FROM pg_catalog.pg_constraint "
7281 "WHERE conrelid = '%u'::pg_catalog.oid "
7282 "AND contype = 'f'",
7283 tbinfo->dobj.catId.oid);
7284 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7285
7286 ntups = PQntuples(res);
7287
7288 i_contableoid = PQfnumber(res, "tableoid");
7289 i_conoid = PQfnumber(res, "oid");
7290 i_conname = PQfnumber(res, "conname");
7291 i_confrelid = PQfnumber(res, "confrelid");
7292 i_condef = PQfnumber(res, "condef");
7293
7294 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7295
7296 for (j = 0; j < ntups; j++)
7297 {
7298 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7299 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7300 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7301 AssignDumpId(&constrinfo[j].dobj);
7302 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7303 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7304 constrinfo[j].contable = tbinfo;
7305 constrinfo[j].condomain = NULL;
7306 constrinfo[j].contype = 'f';
7307 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7308 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7309 constrinfo[j].conindex = 0;
7310 constrinfo[j].condeferrable = false;
7311 constrinfo[j].condeferred = false;
7312 constrinfo[j].conislocal = true;
7313 constrinfo[j].separate = true;
7314 }
7315
7316 PQclear(res);
7317 }
7318
7319 destroyPQExpBuffer(query);
7320}
7321
7322/*
7323 * getDomainConstraints
7324 *
7325 * Get info about constraints on a domain.
7326 */
7327static void
7328getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
7329{
7330 int i;
7331 ConstraintInfo *constrinfo;
7332 PQExpBuffer query;
7333 PGresult *res;
7334 int i_tableoid,
7335 i_oid,
7336 i_conname,
7337 i_consrc;
7338 int ntups;
7339
7340 query = createPQExpBuffer();
7341
7342 if (fout->remoteVersion >= 90100)
7343 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
7344 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
7345 "convalidated "
7346 "FROM pg_catalog.pg_constraint "
7347 "WHERE contypid = '%u'::pg_catalog.oid "
7348 "ORDER BY conname",
7349 tyinfo->dobj.catId.oid);
7350
7351 else
7352 appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
7353 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
7354 "true as convalidated "
7355 "FROM pg_catalog.pg_constraint "
7356 "WHERE contypid = '%u'::pg_catalog.oid "
7357 "ORDER BY conname",
7358 tyinfo->dobj.catId.oid);
7359
7360 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7361
7362 ntups = PQntuples(res);
7363
7364 i_tableoid = PQfnumber(res, "tableoid");
7365 i_oid = PQfnumber(res, "oid");
7366 i_conname = PQfnumber(res, "conname");
7367 i_consrc = PQfnumber(res, "consrc");
7368
7369 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7370
7371 tyinfo->nDomChecks = ntups;
7372 tyinfo->domChecks = constrinfo;
7373
7374 for (i = 0; i < ntups; i++)
7375 {
7376 bool validated = PQgetvalue(res, i, 4)[0] == 't';
7377
7378 constrinfo[i].dobj.objType = DO_CONSTRAINT;
7379 constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7380 constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7381 AssignDumpId(&constrinfo[i].dobj);
7382 constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
7383 constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
7384 constrinfo[i].contable = NULL;
7385 constrinfo[i].condomain = tyinfo;
7386 constrinfo[i].contype = 'c';
7387 constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
7388 constrinfo[i].confrelid = InvalidOid;
7389 constrinfo[i].conindex = 0;
7390 constrinfo[i].condeferrable = false;
7391 constrinfo[i].condeferred = false;
7392 constrinfo[i].conislocal = true;
7393
7394 constrinfo[i].separate = !validated;
7395
7396 /*
7397 * Make the domain depend on the constraint, ensuring it won't be
7398 * output till any constraint dependencies are OK. If the constraint
7399 * has not been validated, it's going to be dumped after the domain
7400 * anyway, so this doesn't matter.
7401 */
7402 if (validated)
7403 addObjectDependency(&tyinfo->dobj,
7404 constrinfo[i].dobj.dumpId);
7405 }
7406
7407 PQclear(res);
7408
7409 destroyPQExpBuffer(query);
7410}
7411
7412/*
7413 * getRules
7414 * get basic information about every rule in the system
7415 *
7416 * numRules is set to the number of rules read in
7417 */
7418RuleInfo *
7419getRules(Archive *fout, int *numRules)
7420{
7421 PGresult *res;
7422 int ntups;
7423 int i;
7424 PQExpBuffer query = createPQExpBuffer();
7425 RuleInfo *ruleinfo;
7426 int i_tableoid;
7427 int i_oid;
7428 int i_rulename;
7429 int i_ruletable;
7430 int i_ev_type;
7431 int i_is_instead;
7432 int i_ev_enabled;
7433
7434 if (fout->remoteVersion >= 80300)
7435 {
7436 appendPQExpBufferStr(query, "SELECT "
7437 "tableoid, oid, rulename, "
7438 "ev_class AS ruletable, ev_type, is_instead, "
7439 "ev_enabled "
7440 "FROM pg_rewrite "
7441 "ORDER BY oid");
7442 }
7443 else
7444 {
7445 appendPQExpBufferStr(query, "SELECT "
7446 "tableoid, oid, rulename, "
7447 "ev_class AS ruletable, ev_type, is_instead, "
7448 "'O'::char AS ev_enabled "
7449 "FROM pg_rewrite "
7450 "ORDER BY oid");
7451 }
7452
7453 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7454
7455 ntups = PQntuples(res);
7456
7457 *numRules = ntups;
7458
7459 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
7460
7461 i_tableoid = PQfnumber(res, "tableoid");
7462 i_oid = PQfnumber(res, "oid");
7463 i_rulename = PQfnumber(res, "rulename");
7464 i_ruletable = PQfnumber(res, "ruletable");
7465 i_ev_type = PQfnumber(res, "ev_type");
7466 i_is_instead = PQfnumber(res, "is_instead");
7467 i_ev_enabled = PQfnumber(res, "ev_enabled");
7468
7469 for (i = 0; i < ntups; i++)
7470 {
7471 Oid ruletableoid;
7472
7473 ruleinfo[i].dobj.objType = DO_RULE;
7474 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7475 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7476 AssignDumpId(&ruleinfo[i].dobj);
7477 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
7478 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
7479 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
7480 if (ruleinfo[i].ruletable == NULL)
7481 fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
7482 ruletableoid, ruleinfo[i].dobj.catId.oid);
7483 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
7484 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
7485 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
7486 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
7487 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
7488 if (ruleinfo[i].ruletable)
7489 {
7490 /*
7491 * If the table is a view or materialized view, force its ON
7492 * SELECT rule to be sorted before the view itself --- this
7493 * ensures that any dependencies for the rule affect the table's
7494 * positioning. Other rules are forced to appear after their
7495 * table.
7496 */
7497 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
7498 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
7499 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
7500 {
7501 addObjectDependency(&ruleinfo[i].ruletable->dobj,
7502 ruleinfo[i].dobj.dumpId);
7503 /* We'll merge the rule into CREATE VIEW, if possible */
7504 ruleinfo[i].separate = false;
7505 }
7506 else
7507 {
7508 addObjectDependency(&ruleinfo[i].dobj,
7509 ruleinfo[i].ruletable->dobj.dumpId);
7510 ruleinfo[i].separate = true;
7511 }
7512 }
7513 else
7514 ruleinfo[i].separate = true;
7515 }
7516
7517 PQclear(res);
7518
7519 destroyPQExpBuffer(query);
7520
7521 return ruleinfo;
7522}
7523
7524/*
7525 * getTriggers
7526 * get information about every trigger on a dumpable table
7527 *
7528 * Note: trigger data is not returned directly to the caller, but it
7529 * does get entered into the DumpableObject tables.
7530 */
7531void
7532getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
7533{
7534 int i,
7535 j;
7536 PQExpBuffer query = createPQExpBuffer();
7537 PGresult *res;
7538 TriggerInfo *tginfo;
7539 int i_tableoid,
7540 i_oid,
7541 i_tgname,
7542 i_tgfname,
7543 i_tgtype,
7544 i_tgnargs,
7545 i_tgargs,
7546 i_tgisconstraint,
7547 i_tgconstrname,
7548 i_tgconstrrelid,
7549 i_tgconstrrelname,
7550 i_tgenabled,
7551 i_tgdeferrable,
7552 i_tginitdeferred,
7553 i_tgdef;
7554 int ntups;
7555
7556 for (i = 0; i < numTables; i++)
7557 {
7558 TableInfo *tbinfo = &tblinfo[i];
7559
7560 if (!tbinfo->hastriggers ||
7561 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7562 continue;
7563
7564 pg_log_info("reading triggers for table \"%s.%s\"",
7565 tbinfo->dobj.namespace->dobj.name,
7566 tbinfo->dobj.name);
7567
7568 resetPQExpBuffer(query);
7569 if (fout->remoteVersion >= 90000)
7570 {
7571 /*
7572 * NB: think not to use pretty=true in pg_get_triggerdef. It
7573 * could result in non-forward-compatible dumps of WHEN clauses
7574 * due to under-parenthesization.
7575 */
7576 appendPQExpBuffer(query,
7577 "SELECT tgname, "
7578 "tgfoid::pg_catalog.regproc AS tgfname, "
7579 "pg_catalog.pg_get_triggerdef(oid, false) AS tgdef, "
7580 "tgenabled, tableoid, oid "
7581 "FROM pg_catalog.pg_trigger t "
7582 "WHERE tgrelid = '%u'::pg_catalog.oid "
7583 "AND NOT tgisinternal",
7584 tbinfo->dobj.catId.oid);
7585 }
7586 else if (fout->remoteVersion >= 80300)
7587 {
7588 /*
7589 * We ignore triggers that are tied to a foreign-key constraint
7590 */
7591 appendPQExpBuffer(query,
7592 "SELECT tgname, "
7593 "tgfoid::pg_catalog.regproc AS tgfname, "
7594 "tgtype, tgnargs, tgargs, tgenabled, "
7595 "tgisconstraint, tgconstrname, tgdeferrable, "
7596 "tgconstrrelid, tginitdeferred, tableoid, oid, "
7597 "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
7598 "FROM pg_catalog.pg_trigger t "
7599 "WHERE tgrelid = '%u'::pg_catalog.oid "
7600 "AND tgconstraint = 0",
7601 tbinfo->dobj.catId.oid);
7602 }
7603 else
7604 {
7605 /*
7606 * We ignore triggers that are tied to a foreign-key constraint,
7607 * but in these versions we have to grovel through pg_constraint
7608 * to find out
7609 */
7610 appendPQExpBuffer(query,
7611 "SELECT tgname, "
7612 "tgfoid::pg_catalog.regproc AS tgfname, "
7613 "tgtype, tgnargs, tgargs, tgenabled, "
7614 "tgisconstraint, tgconstrname, tgdeferrable, "
7615 "tgconstrrelid, tginitdeferred, tableoid, oid, "
7616 "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
7617 "FROM pg_catalog.pg_trigger t "
7618 "WHERE tgrelid = '%u'::pg_catalog.oid "
7619 "AND (NOT tgisconstraint "
7620 " OR NOT EXISTS"
7621 " (SELECT 1 FROM pg_catalog.pg_depend d "
7622 " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
7623 " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
7624 tbinfo->dobj.catId.oid);
7625 }
7626
7627 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7628
7629 ntups = PQntuples(res);
7630
7631 i_tableoid = PQfnumber(res, "tableoid");
7632 i_oid = PQfnumber(res, "oid");
7633 i_tgname = PQfnumber(res, "tgname");
7634 i_tgfname = PQfnumber(res, "tgfname");
7635 i_tgtype = PQfnumber(res, "tgtype");
7636 i_tgnargs = PQfnumber(res, "tgnargs");
7637 i_tgargs = PQfnumber(res, "tgargs");
7638 i_tgisconstraint = PQfnumber(res, "tgisconstraint");
7639 i_tgconstrname = PQfnumber(res, "tgconstrname");
7640 i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
7641 i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
7642 i_tgenabled = PQfnumber(res, "tgenabled");
7643 i_tgdeferrable = PQfnumber(res, "tgdeferrable");
7644 i_tginitdeferred = PQfnumber(res, "tginitdeferred");
7645 i_tgdef = PQfnumber(res, "tgdef");
7646
7647 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
7648
7649 tbinfo->numTriggers = ntups;
7650 tbinfo->triggers = tginfo;
7651
7652 for (j = 0; j < ntups; j++)
7653 {
7654 tginfo[j].dobj.objType = DO_TRIGGER;
7655 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7656 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7657 AssignDumpId(&tginfo[j].dobj);
7658 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
7659 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
7660 tginfo[j].tgtable = tbinfo;
7661 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
7662 if (i_tgdef >= 0)
7663 {
7664 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
7665
7666 /* remaining fields are not valid if we have tgdef */
7667 tginfo[j].tgfname = NULL;
7668 tginfo[j].tgtype = 0;
7669 tginfo[j].tgnargs = 0;
7670 tginfo[j].tgargs = NULL;
7671 tginfo[j].tgisconstraint = false;
7672 tginfo[j].tgdeferrable = false;
7673 tginfo[j].tginitdeferred = false;
7674 tginfo[j].tgconstrname = NULL;
7675 tginfo[j].tgconstrrelid = InvalidOid;
7676 tginfo[j].tgconstrrelname = NULL;
7677 }
7678 else
7679 {
7680 tginfo[j].tgdef = NULL;
7681
7682 tginfo[j].tgfname = pg_strdup(PQgetvalue(res, j, i_tgfname));
7683 tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
7684 tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
7685 tginfo[j].tgargs = pg_strdup(PQgetvalue(res, j, i_tgargs));
7686 tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
7687 tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
7688 tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
7689
7690 if (tginfo[j].tgisconstraint)
7691 {
7692 tginfo[j].tgconstrname = pg_strdup(PQgetvalue(res, j, i_tgconstrname));
7693 tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
7694 if (OidIsValid(tginfo[j].tgconstrrelid))
7695 {
7696 if (PQgetisnull(res, j, i_tgconstrrelname))
7697 fatal("query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)",
7698 tginfo[j].dobj.name,
7699 tbinfo->dobj.name,
7700 tginfo[j].tgconstrrelid);
7701 tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
7702 }
7703 else
7704 tginfo[j].tgconstrrelname = NULL;
7705 }
7706 else
7707 {
7708 tginfo[j].tgconstrname = NULL;
7709 tginfo[j].tgconstrrelid = InvalidOid;
7710 tginfo[j].tgconstrrelname = NULL;
7711 }
7712 }
7713 }
7714
7715 PQclear(res);
7716 }
7717
7718 destroyPQExpBuffer(query);
7719}
7720
7721/*
7722 * getEventTriggers
7723 * get information about event triggers
7724 */
7725EventTriggerInfo *
7726getEventTriggers(Archive *fout, int *numEventTriggers)
7727{
7728 int i;
7729 PQExpBuffer query;
7730 PGresult *res;
7731 EventTriggerInfo *evtinfo;
7732 int i_tableoid,
7733 i_oid,
7734 i_evtname,
7735 i_evtevent,
7736 i_evtowner,
7737 i_evttags,
7738 i_evtfname,
7739 i_evtenabled;
7740 int ntups;
7741
7742 /* Before 9.3, there are no event triggers */
7743 if (fout->remoteVersion < 90300)
7744 {
7745 *numEventTriggers = 0;
7746 return NULL;
7747 }
7748
7749 query = createPQExpBuffer();
7750
7751 appendPQExpBuffer(query,
7752 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
7753 "evtevent, (%s evtowner) AS evtowner, "
7754 "array_to_string(array("
7755 "select quote_literal(x) "
7756 " from unnest(evttags) as t(x)), ', ') as evttags, "
7757 "e.evtfoid::regproc as evtfname "
7758 "FROM pg_event_trigger e "
7759 "ORDER BY e.oid",
7760 username_subquery);
7761
7762 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7763
7764 ntups = PQntuples(res);
7765
7766 *numEventTriggers = ntups;
7767
7768 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
7769
7770 i_tableoid = PQfnumber(res, "tableoid");
7771 i_oid = PQfnumber(res, "oid");
7772 i_evtname = PQfnumber(res, "evtname");
7773 i_evtevent = PQfnumber(res, "evtevent");
7774 i_evtowner = PQfnumber(res, "evtowner");
7775 i_evttags = PQfnumber(res, "evttags");
7776 i_evtfname = PQfnumber(res, "evtfname");
7777 i_evtenabled = PQfnumber(res, "evtenabled");
7778
7779 for (i = 0; i < ntups; i++)
7780 {
7781 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
7782 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7783 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7784 AssignDumpId(&evtinfo[i].dobj);
7785 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
7786 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
7787 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
7788 evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner));
7789 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
7790 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
7791 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
7792
7793 /* Decide whether we want to dump it */
7794 selectDumpableObject(&(evtinfo[i].dobj), fout);
7795
7796 /* Event Triggers do not currently have ACLs. */
7797 evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7798 }
7799
7800 PQclear(res);
7801
7802 destroyPQExpBuffer(query);
7803
7804 return evtinfo;
7805}
7806
7807/*
7808 * getProcLangs
7809 * get basic information about every procedural language in the system
7810 *
7811 * numProcLangs is set to the number of langs read in
7812 *
7813 * NB: this must run after getFuncs() because we assume we can do
7814 * findFuncByOid().
7815 */
7816ProcLangInfo *
7817getProcLangs(Archive *fout, int *numProcLangs)
7818{
7819 DumpOptions *dopt = fout->dopt;
7820 PGresult *res;
7821 int ntups;
7822 int i;
7823 PQExpBuffer query = createPQExpBuffer();
7824 ProcLangInfo *planginfo;
7825 int i_tableoid;
7826 int i_oid;
7827 int i_lanname;
7828 int i_lanpltrusted;
7829 int i_lanplcallfoid;
7830 int i_laninline;
7831 int i_lanvalidator;
7832 int i_lanacl;
7833 int i_rlanacl;
7834 int i_initlanacl;
7835 int i_initrlanacl;
7836 int i_lanowner;
7837
7838 if (fout->remoteVersion >= 90600)
7839 {
7840 PQExpBuffer acl_subquery = createPQExpBuffer();
7841 PQExpBuffer racl_subquery = createPQExpBuffer();
7842 PQExpBuffer initacl_subquery = createPQExpBuffer();
7843 PQExpBuffer initracl_subquery = createPQExpBuffer();
7844
7845 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
7846 initracl_subquery, "l.lanacl", "l.lanowner", "'l'",
7847 dopt->binary_upgrade);
7848
7849 /* pg_language has a laninline column */
7850 appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
7851 "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
7852 "l.laninline, l.lanvalidator, "
7853 "%s AS lanacl, "
7854 "%s AS rlanacl, "
7855 "%s AS initlanacl, "
7856 "%s AS initrlanacl, "
7857 "(%s l.lanowner) AS lanowner "
7858 "FROM pg_language l "
7859 "LEFT JOIN pg_init_privs pip ON "
7860 "(l.oid = pip.objoid "
7861 "AND pip.classoid = 'pg_language'::regclass "
7862 "AND pip.objsubid = 0) "
7863 "WHERE l.lanispl "
7864 "ORDER BY l.oid",
7865 acl_subquery->data,
7866 racl_subquery->data,
7867 initacl_subquery->data,
7868 initracl_subquery->data,
7869 username_subquery);
7870
7871 destroyPQExpBuffer(acl_subquery);
7872 destroyPQExpBuffer(racl_subquery);
7873 destroyPQExpBuffer(initacl_subquery);
7874 destroyPQExpBuffer(initracl_subquery);
7875 }
7876 else if (fout->remoteVersion >= 90000)
7877 {
7878 /* pg_language has a laninline column */
7879 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7880 "lanname, lanpltrusted, lanplcallfoid, "
7881 "laninline, lanvalidator, lanacl, NULL AS rlanacl, "
7882 "NULL AS initlanacl, NULL AS initrlanacl, "
7883 "(%s lanowner) AS lanowner "
7884 "FROM pg_language "
7885 "WHERE lanispl "
7886 "ORDER BY oid",
7887 username_subquery);
7888 }
7889 else if (fout->remoteVersion >= 80300)
7890 {
7891 /* pg_language has a lanowner column */
7892 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7893 "lanname, lanpltrusted, lanplcallfoid, "
7894 "0 AS laninline, lanvalidator, lanacl, "
7895 "NULL AS rlanacl, "
7896 "NULL AS initlanacl, NULL AS initrlanacl, "
7897 "(%s lanowner) AS lanowner "
7898 "FROM pg_language "
7899 "WHERE lanispl "
7900 "ORDER BY oid",
7901 username_subquery);
7902 }
7903 else if (fout->remoteVersion >= 80100)
7904 {
7905 /* Languages are owned by the bootstrap superuser, OID 10 */
7906 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7907 "lanname, lanpltrusted, lanplcallfoid, "
7908 "0 AS laninline, lanvalidator, lanacl, "
7909 "NULL AS rlanacl, "
7910 "NULL AS initlanacl, NULL AS initrlanacl, "
7911 "(%s '10') AS lanowner "
7912 "FROM pg_language "
7913 "WHERE lanispl "
7914 "ORDER BY oid",
7915 username_subquery);
7916 }
7917 else
7918 {
7919 /* Languages are owned by the bootstrap superuser, sysid 1 */
7920 appendPQExpBuffer(query, "SELECT tableoid, oid, "
7921 "lanname, lanpltrusted, lanplcallfoid, "
7922 "0 AS laninline, lanvalidator, lanacl, "
7923 "NULL AS rlanacl, "
7924 "NULL AS initlanacl, NULL AS initrlanacl, "
7925 "(%s '1') AS lanowner "
7926 "FROM pg_language "
7927 "WHERE lanispl "
7928 "ORDER BY oid",
7929 username_subquery);
7930 }
7931
7932 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7933
7934 ntups = PQntuples(res);
7935
7936 *numProcLangs = ntups;
7937
7938 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
7939
7940 i_tableoid = PQfnumber(res, "tableoid");
7941 i_oid = PQfnumber(res, "oid");
7942 i_lanname = PQfnumber(res, "lanname");
7943 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
7944 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
7945 i_laninline = PQfnumber(res, "laninline");
7946 i_lanvalidator = PQfnumber(res, "lanvalidator");
7947 i_lanacl = PQfnumber(res, "lanacl");
7948 i_rlanacl = PQfnumber(res, "rlanacl");
7949 i_initlanacl = PQfnumber(res, "initlanacl");
7950 i_initrlanacl = PQfnumber(res, "initrlanacl");
7951 i_lanowner = PQfnumber(res, "lanowner");
7952
7953 for (i = 0; i < ntups; i++)
7954 {
7955 planginfo[i].dobj.objType = DO_PROCLANG;
7956 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7957 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7958 AssignDumpId(&planginfo[i].dobj);
7959
7960 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
7961 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
7962 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
7963 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
7964 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
7965 planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl));
7966 planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl));
7967 planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl));
7968 planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl));
7969 planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner));
7970
7971 /* Decide whether we want to dump it */
7972 selectDumpableProcLang(&(planginfo[i]), fout);
7973
7974 /* Do not try to dump ACL if no ACL exists. */
7975 if (PQgetisnull(res, i, i_lanacl) && PQgetisnull(res, i, i_rlanacl) &&
7976 PQgetisnull(res, i, i_initlanacl) &&
7977 PQgetisnull(res, i, i_initrlanacl))
7978 planginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
7979 }
7980
7981 PQclear(res);
7982
7983 destroyPQExpBuffer(query);
7984
7985 return planginfo;
7986}
7987
7988/*
7989 * getCasts
7990 * get basic information about every cast in the system
7991 *
7992 * numCasts is set to the number of casts read in
7993 */
7994CastInfo *
7995getCasts(Archive *fout, int *numCasts)
7996{
7997 PGresult *res;
7998 int ntups;
7999 int i;
8000 PQExpBuffer query = createPQExpBuffer();
8001 CastInfo *castinfo;
8002 int i_tableoid;
8003 int i_oid;
8004 int i_castsource;
8005 int i_casttarget;
8006 int i_castfunc;
8007 int i_castcontext;
8008 int i_castmethod;
8009
8010 if (fout->remoteVersion >= 80400)
8011 {
8012 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8013 "castsource, casttarget, castfunc, castcontext, "
8014 "castmethod "
8015 "FROM pg_cast ORDER BY 3,4");
8016 }
8017 else
8018 {
8019 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8020 "castsource, casttarget, castfunc, castcontext, "
8021 "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod "
8022 "FROM pg_cast ORDER BY 3,4");
8023 }
8024
8025 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8026
8027 ntups = PQntuples(res);
8028
8029 *numCasts = ntups;
8030
8031 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8032
8033 i_tableoid = PQfnumber(res, "tableoid");
8034 i_oid = PQfnumber(res, "oid");
8035 i_castsource = PQfnumber(res, "castsource");
8036 i_casttarget = PQfnumber(res, "casttarget");
8037 i_castfunc = PQfnumber(res, "castfunc");
8038 i_castcontext = PQfnumber(res, "castcontext");
8039 i_castmethod = PQfnumber(res, "castmethod");
8040
8041 for (i = 0; i < ntups; i++)
8042 {
8043 PQExpBufferData namebuf;
8044 TypeInfo *sTypeInfo;
8045 TypeInfo *tTypeInfo;
8046
8047 castinfo[i].dobj.objType = DO_CAST;
8048 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8049 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8050 AssignDumpId(&castinfo[i].dobj);
8051 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8052 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8053 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8054 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8055 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8056
8057 /*
8058 * Try to name cast as concatenation of typnames. This is only used
8059 * for purposes of sorting. If we fail to find either type, the name
8060 * will be an empty string.
8061 */
8062 initPQExpBuffer(&namebuf);
8063 sTypeInfo = findTypeByOid(castinfo[i].castsource);
8064 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8065 if (sTypeInfo && tTypeInfo)
8066 appendPQExpBuffer(&namebuf, "%s %s",
8067 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8068 castinfo[i].dobj.name = namebuf.data;
8069
8070 /* Decide whether we want to dump it */
8071 selectDumpableCast(&(castinfo[i]), fout);
8072
8073 /* Casts do not currently have ACLs. */
8074 castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8075 }
8076
8077 PQclear(res);
8078
8079 destroyPQExpBuffer(query);
8080
8081 return castinfo;
8082}
8083
8084static char *
8085get_language_name(Archive *fout, Oid langid)
8086{
8087 PQExpBuffer query;
8088 PGresult *res;
8089 char *lanname;
8090
8091 query = createPQExpBuffer();
8092 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8093 res = ExecuteSqlQueryForSingleRow(fout, query->data);
8094 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8095 destroyPQExpBuffer(query);
8096 PQclear(res);
8097
8098 return lanname;
8099}
8100
8101/*
8102 * getTransforms
8103 * get basic information about every transform in the system
8104 *
8105 * numTransforms is set to the number of transforms read in
8106 */
8107TransformInfo *
8108getTransforms(Archive *fout, int *numTransforms)
8109{
8110 PGresult *res;
8111 int ntups;
8112 int i;
8113 PQExpBuffer query;
8114 TransformInfo *transforminfo;
8115 int i_tableoid;
8116 int i_oid;
8117 int i_trftype;
8118 int i_trflang;
8119 int i_trffromsql;
8120 int i_trftosql;
8121
8122 /* Transforms didn't exist pre-9.5 */
8123 if (fout->remoteVersion < 90500)
8124 {
8125 *numTransforms = 0;
8126 return NULL;
8127 }
8128
8129 query = createPQExpBuffer();
8130
8131 appendPQExpBuffer(query, "SELECT tableoid, oid, "
8132 "trftype, trflang, trffromsql::oid, trftosql::oid "
8133 "FROM pg_transform "
8134 "ORDER BY 3,4");
8135
8136 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8137
8138 ntups = PQntuples(res);
8139
8140 *numTransforms = ntups;
8141
8142 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8143
8144 i_tableoid = PQfnumber(res, "tableoid");
8145 i_oid = PQfnumber(res, "oid");
8146 i_trftype = PQfnumber(res, "trftype");
8147 i_trflang = PQfnumber(res, "trflang");
8148 i_trffromsql = PQfnumber(res, "trffromsql");
8149 i_trftosql = PQfnumber(res, "trftosql");
8150
8151 for (i = 0; i < ntups; i++)
8152 {
8153 PQExpBufferData namebuf;
8154 TypeInfo *typeInfo;
8155 char *lanname;
8156
8157 transforminfo[i].dobj.objType = DO_TRANSFORM;
8158 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8159 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8160 AssignDumpId(&transforminfo[i].dobj);
8161 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8162 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8163 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8164 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8165
8166 /*
8167 * Try to name transform as concatenation of type and language name.
8168 * This is only used for purposes of sorting. If we fail to find
8169 * either, the name will be an empty string.
8170 */
8171 initPQExpBuffer(&namebuf);
8172 typeInfo = findTypeByOid(transforminfo[i].trftype);
8173 lanname = get_language_name(fout, transforminfo[i].trflang);
8174 if (typeInfo && lanname)
8175 appendPQExpBuffer(&namebuf, "%s %s",
8176 typeInfo->dobj.name, lanname);
8177 transforminfo[i].dobj.name = namebuf.data;
8178 free(lanname);
8179
8180 /* Decide whether we want to dump it */
8181 selectDumpableObject(&(transforminfo[i].dobj), fout);
8182 }
8183
8184 PQclear(res);
8185
8186 destroyPQExpBuffer(query);
8187
8188 return transforminfo;
8189}
8190
8191/*
8192 * getTableAttrs -
8193 * for each interesting table, read info about its attributes
8194 * (names, types, default values, CHECK constraints, etc)
8195 *
8196 * This is implemented in a very inefficient way right now, looping
8197 * through the tblinfo and doing a join per table to find the attrs and their
8198 * types. However, because we want type names and so forth to be named
8199 * relative to the schema of each table, we couldn't do it in just one
8200 * query. (Maybe one query per schema?)
8201 *
8202 * modifies tblinfo
8203 */
8204void
8205getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8206{
8207 DumpOptions *dopt = fout->dopt;
8208 int i,
8209 j;
8210 PQExpBuffer q = createPQExpBuffer();
8211 int i_attnum;
8212 int i_attname;
8213 int i_atttypname;
8214 int i_atttypmod;
8215 int i_attstattarget;
8216 int i_attstorage;
8217 int i_typstorage;
8218 int i_attnotnull;
8219 int i_atthasdef;
8220 int i_attidentity;
8221 int i_attgenerated;
8222 int i_attisdropped;
8223 int i_attlen;
8224 int i_attalign;
8225 int i_attislocal;
8226 int i_attoptions;
8227 int i_attcollation;
8228 int i_attfdwoptions;
8229 int i_attmissingval;
8230 PGresult *res;
8231 int ntups;
8232 bool hasdefaults;
8233
8234 for (i = 0; i < numTables; i++)
8235 {
8236 TableInfo *tbinfo = &tblinfo[i];
8237
8238 /* Don't bother to collect info for sequences */
8239 if (tbinfo->relkind == RELKIND_SEQUENCE)
8240 continue;
8241
8242 /* Don't bother with uninteresting tables, either */
8243 if (!tbinfo->interesting)
8244 continue;
8245
8246 /* find all the user attributes and their types */
8247
8248 /*
8249 * we must read the attribute names in attribute number order! because
8250 * we will use the attnum to index into the attnames array later.
8251 */
8252 pg_log_info("finding the columns and types of table \"%s.%s\"",
8253 tbinfo->dobj.namespace->dobj.name,
8254 tbinfo->dobj.name);
8255
8256 resetPQExpBuffer(q);
8257
8258 appendPQExpBuffer(q,
8259 "SELECT\n"
8260 "a.attnum,\n"
8261 "a.attname,\n"
8262 "a.atttypmod,\n"
8263 "a.attstattarget,\n"
8264 "a.attstorage,\n"
8265 "t.typstorage,\n"
8266 "a.attnotnull,\n"
8267 "a.atthasdef,\n"
8268 "a.attisdropped,\n"
8269 "a.attlen,\n"
8270 "a.attalign,\n"
8271 "a.attislocal,\n"
8272 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n");
8273
8274 if (fout->remoteVersion >= 120000)
8275 appendPQExpBuffer(q,
8276 "a.attgenerated,\n");
8277 else
8278 appendPQExpBuffer(q,
8279 "'' AS attgenerated,\n");
8280
8281 if (fout->remoteVersion >= 110000)
8282 appendPQExpBuffer(q,
8283 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8284 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8285 else
8286 appendPQExpBuffer(q,
8287 "NULL AS attmissingval,\n");
8288
8289 if (fout->remoteVersion >= 100000)
8290 appendPQExpBuffer(q,
8291 "a.attidentity,\n");
8292 else
8293 appendPQExpBuffer(q,
8294 "'' AS attidentity,\n");
8295
8296 if (fout->remoteVersion >= 90200)
8297 appendPQExpBuffer(q,
8298 "pg_catalog.array_to_string(ARRAY("
8299 "SELECT pg_catalog.quote_ident(option_name) || "
8300 "' ' || pg_catalog.quote_literal(option_value) "
8301 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8302 "ORDER BY option_name"
8303 "), E',\n ') AS attfdwoptions,\n");
8304 else
8305 appendPQExpBuffer(q,
8306 "'' AS attfdwoptions,\n");
8307
8308 if (fout->remoteVersion >= 90100)
8309 {
8310 /*
8311 * Since we only want to dump COLLATE clauses for attributes whose
8312 * collation is different from their type's default, we use a CASE
8313 * here to suppress uninteresting attcollations cheaply.
8314 */
8315 appendPQExpBuffer(q,
8316 "CASE WHEN a.attcollation <> t.typcollation "
8317 "THEN a.attcollation ELSE 0 END AS attcollation,\n");
8318 }
8319 else
8320 appendPQExpBuffer(q,
8321 "0 AS attcollation,\n");
8322
8323 if (fout->remoteVersion >= 90000)
8324 appendPQExpBuffer(q,
8325 "array_to_string(a.attoptions, ', ') AS attoptions\n");
8326 else
8327 appendPQExpBuffer(q,
8328 "'' AS attoptions\n");
8329
8330 /* need left join here to not fail on dropped columns ... */
8331 appendPQExpBuffer(q,
8332 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
8333 "ON a.atttypid = t.oid\n"
8334 "WHERE a.attrelid = '%u'::pg_catalog.oid "
8335 "AND a.attnum > 0::pg_catalog.int2\n"
8336 "ORDER BY a.attnum",
8337 tbinfo->dobj.catId.oid);
8338
8339 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8340
8341 ntups = PQntuples(res);
8342
8343 i_attnum = PQfnumber(res, "attnum");
8344 i_attname = PQfnumber(res, "attname");
8345 i_atttypname = PQfnumber(res, "atttypname");
8346 i_atttypmod = PQfnumber(res, "atttypmod");
8347 i_attstattarget = PQfnumber(res, "attstattarget");
8348 i_attstorage = PQfnumber(res, "attstorage");
8349 i_typstorage = PQfnumber(res, "typstorage");
8350 i_attnotnull = PQfnumber(res, "attnotnull");
8351 i_atthasdef = PQfnumber(res, "atthasdef");
8352 i_attidentity = PQfnumber(res, "attidentity");
8353 i_attgenerated = PQfnumber(res, "attgenerated");
8354 i_attisdropped = PQfnumber(res, "attisdropped");
8355 i_attlen = PQfnumber(res, "attlen");
8356 i_attalign = PQfnumber(res, "attalign");
8357 i_attislocal = PQfnumber(res, "attislocal");
8358 i_attoptions = PQfnumber(res, "attoptions");
8359 i_attcollation = PQfnumber(res, "attcollation");
8360 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8361 i_attmissingval = PQfnumber(res, "attmissingval");
8362
8363 tbinfo->numatts = ntups;
8364 tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
8365 tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *));
8366 tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int));
8367 tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int));
8368 tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char));
8369 tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char));
8370 tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char));
8371 tbinfo->attgenerated = (char *) pg_malloc(ntups * sizeof(char));
8372 tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool));
8373 tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int));
8374 tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char));
8375 tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool));
8376 tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
8377 tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
8378 tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
8379 tbinfo->attmissingval = (char **) pg_malloc(ntups * sizeof(char *));
8380 tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
8381 tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
8382 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
8383 hasdefaults = false;
8384
8385 for (j = 0; j < ntups; j++)
8386 {
8387 if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
8388 fatal("invalid column numbering in table \"%s\"",
8389 tbinfo->dobj.name);
8390 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, i_attname));
8391 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname));
8392 tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
8393 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
8394 tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
8395 tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
8396 tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity));
8397 tbinfo->attgenerated[j] = *(PQgetvalue(res, j, i_attgenerated));
8398 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
8399 tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
8400 tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen));
8401 tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
8402 tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
8403 tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
8404 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
8405 tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
8406 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
8407 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_attmissingval));
8408 tbinfo->attrdefs[j] = NULL; /* fix below */
8409 if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
8410 hasdefaults = true;
8411 /* these flags will be set in flagInhAttrs() */
8412 tbinfo->inhNotNull[j] = false;
8413 }
8414
8415 PQclear(res);
8416
8417 /*
8418 * Get info about column defaults
8419 */
8420 if (hasdefaults)
8421 {
8422 AttrDefInfo *attrdefs;
8423 int numDefaults;
8424
8425 pg_log_info("finding default expressions of table \"%s.%s\"",
8426 tbinfo->dobj.namespace->dobj.name,
8427 tbinfo->dobj.name);
8428
8429 printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
8430 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
8431 "FROM pg_catalog.pg_attrdef "
8432 "WHERE adrelid = '%u'::pg_catalog.oid",
8433 tbinfo->dobj.catId.oid);
8434
8435 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8436
8437 numDefaults = PQntuples(res);
8438 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
8439
8440 for (j = 0; j < numDefaults; j++)
8441 {
8442 int adnum;
8443
8444 adnum = atoi(PQgetvalue(res, j, 2));
8445
8446 if (adnum <= 0 || adnum > ntups)
8447 fatal("invalid adnum value %d for table \"%s\"",
8448 adnum, tbinfo->dobj.name);
8449
8450 /*
8451 * dropped columns shouldn't have defaults, but just in case,
8452 * ignore 'em
8453 */
8454 if (tbinfo->attisdropped[adnum - 1])
8455 continue;
8456
8457 attrdefs[j].dobj.objType = DO_ATTRDEF;
8458 attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
8459 attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
8460 AssignDumpId(&attrdefs[j].dobj);
8461 attrdefs[j].adtable = tbinfo;
8462 attrdefs[j].adnum = adnum;
8463 attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3));
8464
8465 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
8466 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
8467
8468 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
8469
8470 /*
8471 * Defaults on a VIEW must always be dumped as separate ALTER
8472 * TABLE commands. Defaults on regular tables are dumped as
8473 * part of the CREATE TABLE if possible, which it won't be if
8474 * the column is not going to be emitted explicitly.
8475 */
8476 if (tbinfo->relkind == RELKIND_VIEW)
8477 {
8478 attrdefs[j].separate = true;
8479 }
8480 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
8481 {
8482 /* column will be suppressed, print default separately */
8483 attrdefs[j].separate = true;
8484 }
8485 else
8486 {
8487 attrdefs[j].separate = false;
8488
8489 /*
8490 * Mark the default as needing to appear before the table,
8491 * so that any dependencies it has must be emitted before
8492 * the CREATE TABLE. If this is not possible, we'll
8493 * change to "separate" mode while sorting dependencies.
8494 */
8495 addObjectDependency(&tbinfo->dobj,
8496 attrdefs[j].dobj.dumpId);
8497 }
8498
8499 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
8500 }
8501 PQclear(res);
8502 }
8503
8504 /*
8505 * Get info about table CHECK constraints
8506 */
8507 if (tbinfo->ncheck > 0)
8508 {
8509 ConstraintInfo *constrs;
8510 int numConstrs;
8511
8512 pg_log_info("finding check constraints for table \"%s.%s\"",
8513 tbinfo->dobj.namespace->dobj.name,
8514 tbinfo->dobj.name);
8515
8516 resetPQExpBuffer(q);
8517 if (fout->remoteVersion >= 90200)
8518 {
8519 /*
8520 * convalidated is new in 9.2 (actually, it is there in 9.1,
8521 * but it wasn't ever false for check constraints until 9.2).
8522 */
8523 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8524 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8525 "conislocal, convalidated "
8526 "FROM pg_catalog.pg_constraint "
8527 "WHERE conrelid = '%u'::pg_catalog.oid "
8528 " AND contype = 'c' "
8529 "ORDER BY conname",
8530 tbinfo->dobj.catId.oid);
8531 }
8532 else if (fout->remoteVersion >= 80400)
8533 {
8534 /* conislocal is new in 8.4 */
8535 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8536 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8537 "conislocal, true AS convalidated "
8538 "FROM pg_catalog.pg_constraint "
8539 "WHERE conrelid = '%u'::pg_catalog.oid "
8540 " AND contype = 'c' "
8541 "ORDER BY conname",
8542 tbinfo->dobj.catId.oid);
8543 }
8544 else
8545 {
8546 appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
8547 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8548 "true AS conislocal, true AS convalidated "
8549 "FROM pg_catalog.pg_constraint "
8550 "WHERE conrelid = '%u'::pg_catalog.oid "
8551 " AND contype = 'c' "
8552 "ORDER BY conname",
8553 tbinfo->dobj.catId.oid);
8554 }
8555
8556 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8557
8558 numConstrs = PQntuples(res);
8559 if (numConstrs != tbinfo->ncheck)
8560 {
8561 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
8562 "expected %d check constraints on table \"%s\" but found %d",
8563 tbinfo->ncheck),
8564 tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
8565 pg_log_error("(The system catalogs might be corrupted.)");
8566 exit_nicely(1);
8567 }
8568
8569 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
8570 tbinfo->checkexprs = constrs;
8571
8572 for (j = 0; j < numConstrs; j++)
8573 {
8574 bool validated = PQgetvalue(res, j, 5)[0] == 't';
8575
8576 constrs[j].dobj.objType = DO_CONSTRAINT;
8577 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
8578 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
8579 AssignDumpId(&constrs[j].dobj);
8580 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, 2));
8581 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
8582 constrs[j].contable = tbinfo;
8583 constrs[j].condomain = NULL;
8584 constrs[j].contype = 'c';
8585 constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3));
8586 constrs[j].confrelid = InvalidOid;
8587 constrs[j].conindex = 0;
8588 constrs[j].condeferrable = false;
8589 constrs[j].condeferred = false;
8590 constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
8591
8592 /*
8593 * An unvalidated constraint needs to be dumped separately, so
8594 * that potentially-violating existing data is loaded before
8595 * the constraint.
8596 */
8597 constrs[j].separate = !validated;
8598
8599 constrs[j].dobj.dump = tbinfo->dobj.dump;
8600
8601 /*
8602 * Mark the constraint as needing to appear before the table
8603 * --- this is so that any other dependencies of the
8604 * constraint will be emitted before we try to create the
8605 * table. If the constraint is to be dumped separately, it
8606 * will be dumped after data is loaded anyway, so don't do it.
8607 * (There's an automatic dependency in the opposite direction
8608 * anyway, so don't need to add one manually here.)
8609 */
8610 if (!constrs[j].separate)
8611 addObjectDependency(&tbinfo->dobj,
8612 constrs[j].dobj.dumpId);
8613
8614 /*
8615 * If the constraint is inherited, this will be detected later
8616 * (in pre-8.4 databases). We also detect later if the
8617 * constraint must be split out from the table definition.
8618 */
8619 }
8620 PQclear(res);
8621 }
8622 }
8623
8624 destroyPQExpBuffer(q);
8625}
8626
8627/*
8628 * Test whether a column should be printed as part of table's CREATE TABLE.
8629 * Column number is zero-based.
8630 *
8631 * Normally this is always true, but it's false for dropped columns, as well
8632 * as those that were inherited without any local definition. (If we print
8633 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
8634 * For partitions, it's always true, because we want the partitions to be
8635 * created independently and ATTACH PARTITION used afterwards.
8636 *
8637 * In binary_upgrade mode, we must print all columns and fix the attislocal/
8638 * attisdropped state later, so as to keep control of the physical column
8639 * order.
8640 *
8641 * This function exists because there are scattered nonobvious places that
8642 * must be kept in sync with this decision.
8643 */
8644bool
8645shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno)
8646{
8647 if (dopt->binary_upgrade)
8648 return true;
8649 if (tbinfo->attisdropped[colno])
8650 return false;
8651 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
8652}
8653
8654
8655/*
8656 * getTSParsers:
8657 * read all text search parsers in the system catalogs and return them
8658 * in the TSParserInfo* structure
8659 *
8660 * numTSParsers is set to the number of parsers read in
8661 */
8662TSParserInfo *
8663getTSParsers(Archive *fout, int *numTSParsers)
8664{
8665 PGresult *res;
8666 int ntups;
8667 int i;
8668 PQExpBuffer query;
8669 TSParserInfo *prsinfo;
8670 int i_tableoid;
8671 int i_oid;
8672 int i_prsname;
8673 int i_prsnamespace;
8674 int i_prsstart;
8675 int i_prstoken;
8676 int i_prsend;
8677 int i_prsheadline;
8678 int i_prslextype;
8679
8680 /* Before 8.3, there is no built-in text search support */
8681 if (fout->remoteVersion < 80300)
8682 {
8683 *numTSParsers = 0;
8684 return NULL;
8685 }
8686
8687 query = createPQExpBuffer();
8688
8689 /*
8690 * find all text search objects, including builtin ones; we filter out
8691 * system-defined objects at dump-out time.
8692 */
8693
8694 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
8695 "prsstart::oid, prstoken::oid, "
8696 "prsend::oid, prsheadline::oid, prslextype::oid "
8697 "FROM pg_ts_parser");
8698
8699 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8700
8701 ntups = PQntuples(res);
8702 *numTSParsers = ntups;
8703
8704 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
8705
8706 i_tableoid = PQfnumber(res, "tableoid");
8707 i_oid = PQfnumber(res, "oid");
8708 i_prsname = PQfnumber(res, "prsname");
8709 i_prsnamespace = PQfnumber(res, "prsnamespace");
8710 i_prsstart = PQfnumber(res, "prsstart");
8711 i_prstoken = PQfnumber(res, "prstoken");
8712 i_prsend = PQfnumber(res, "prsend");
8713 i_prsheadline = PQfnumber(res, "prsheadline");
8714 i_prslextype = PQfnumber(res, "prslextype");
8715
8716 for (i = 0; i < ntups; i++)
8717 {
8718 prsinfo[i].dobj.objType = DO_TSPARSER;
8719 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8720 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8721 AssignDumpId(&prsinfo[i].dobj);
8722 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
8723 prsinfo[i].dobj.namespace =
8724 findNamespace(fout,
8725 atooid(PQgetvalue(res, i, i_prsnamespace)));
8726 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
8727 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
8728 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
8729 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
8730 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
8731
8732 /* Decide whether we want to dump it */
8733 selectDumpableObject(&(prsinfo[i].dobj), fout);
8734
8735 /* Text Search Parsers do not currently have ACLs. */
8736 prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8737 }
8738
8739 PQclear(res);
8740
8741 destroyPQExpBuffer(query);
8742
8743 return prsinfo;
8744}
8745
8746/*
8747 * getTSDictionaries:
8748 * read all text search dictionaries in the system catalogs and return them
8749 * in the TSDictInfo* structure
8750 *
8751 * numTSDicts is set to the number of dictionaries read in
8752 */
8753TSDictInfo *
8754getTSDictionaries(Archive *fout, int *numTSDicts)
8755{
8756 PGresult *res;
8757 int ntups;
8758 int i;
8759 PQExpBuffer query;
8760 TSDictInfo *dictinfo;
8761 int i_tableoid;
8762 int i_oid;
8763 int i_dictname;
8764 int i_dictnamespace;
8765 int i_rolname;
8766 int i_dicttemplate;
8767 int i_dictinitoption;
8768
8769 /* Before 8.3, there is no built-in text search support */
8770 if (fout->remoteVersion < 80300)
8771 {
8772 *numTSDicts = 0;
8773 return NULL;
8774 }
8775
8776 query = createPQExpBuffer();
8777
8778 appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
8779 "dictnamespace, (%s dictowner) AS rolname, "
8780 "dicttemplate, dictinitoption "
8781 "FROM pg_ts_dict",
8782 username_subquery);
8783
8784 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8785
8786 ntups = PQntuples(res);
8787 *numTSDicts = ntups;
8788
8789 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
8790
8791 i_tableoid = PQfnumber(res, "tableoid");
8792 i_oid = PQfnumber(res, "oid");
8793 i_dictname = PQfnumber(res, "dictname");
8794 i_dictnamespace = PQfnumber(res, "dictnamespace");
8795 i_rolname = PQfnumber(res, "rolname");
8796 i_dictinitoption = PQfnumber(res, "dictinitoption");
8797 i_dicttemplate = PQfnumber(res, "dicttemplate");
8798
8799 for (i = 0; i < ntups; i++)
8800 {
8801 dictinfo[i].dobj.objType = DO_TSDICT;
8802 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8803 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8804 AssignDumpId(&dictinfo[i].dobj);
8805 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
8806 dictinfo[i].dobj.namespace =
8807 findNamespace(fout,
8808 atooid(PQgetvalue(res, i, i_dictnamespace)));
8809 dictinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8810 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
8811 if (PQgetisnull(res, i, i_dictinitoption))
8812 dictinfo[i].dictinitoption = NULL;
8813 else
8814 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
8815
8816 /* Decide whether we want to dump it */
8817 selectDumpableObject(&(dictinfo[i].dobj), fout);
8818
8819 /* Text Search Dictionaries do not currently have ACLs. */
8820 dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8821 }
8822
8823 PQclear(res);
8824
8825 destroyPQExpBuffer(query);
8826
8827 return dictinfo;
8828}
8829
8830/*
8831 * getTSTemplates:
8832 * read all text search templates in the system catalogs and return them
8833 * in the TSTemplateInfo* structure
8834 *
8835 * numTSTemplates is set to the number of templates read in
8836 */
8837TSTemplateInfo *
8838getTSTemplates(Archive *fout, int *numTSTemplates)
8839{
8840 PGresult *res;
8841 int ntups;
8842 int i;
8843 PQExpBuffer query;
8844 TSTemplateInfo *tmplinfo;
8845 int i_tableoid;
8846 int i_oid;
8847 int i_tmplname;
8848 int i_tmplnamespace;
8849 int i_tmplinit;
8850 int i_tmpllexize;
8851
8852 /* Before 8.3, there is no built-in text search support */
8853 if (fout->remoteVersion < 80300)
8854 {
8855 *numTSTemplates = 0;
8856 return NULL;
8857 }
8858
8859 query = createPQExpBuffer();
8860
8861 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
8862 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
8863 "FROM pg_ts_template");
8864
8865 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8866
8867 ntups = PQntuples(res);
8868 *numTSTemplates = ntups;
8869
8870 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
8871
8872 i_tableoid = PQfnumber(res, "tableoid");
8873 i_oid = PQfnumber(res, "oid");
8874 i_tmplname = PQfnumber(res, "tmplname");
8875 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
8876 i_tmplinit = PQfnumber(res, "tmplinit");
8877 i_tmpllexize = PQfnumber(res, "tmpllexize");
8878
8879 for (i = 0; i < ntups; i++)
8880 {
8881 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
8882 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8883 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8884 AssignDumpId(&tmplinfo[i].dobj);
8885 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
8886 tmplinfo[i].dobj.namespace =
8887 findNamespace(fout,
8888 atooid(PQgetvalue(res, i, i_tmplnamespace)));
8889 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
8890 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
8891
8892 /* Decide whether we want to dump it */
8893 selectDumpableObject(&(tmplinfo[i].dobj), fout);
8894
8895 /* Text Search Templates do not currently have ACLs. */
8896 tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8897 }
8898
8899 PQclear(res);
8900
8901 destroyPQExpBuffer(query);
8902
8903 return tmplinfo;
8904}
8905
8906/*
8907 * getTSConfigurations:
8908 * read all text search configurations in the system catalogs and return
8909 * them in the TSConfigInfo* structure
8910 *
8911 * numTSConfigs is set to the number of configurations read in
8912 */
8913TSConfigInfo *
8914getTSConfigurations(Archive *fout, int *numTSConfigs)
8915{
8916 PGresult *res;
8917 int ntups;
8918 int i;
8919 PQExpBuffer query;
8920 TSConfigInfo *cfginfo;
8921 int i_tableoid;
8922 int i_oid;
8923 int i_cfgname;
8924 int i_cfgnamespace;
8925 int i_rolname;
8926 int i_cfgparser;
8927
8928 /* Before 8.3, there is no built-in text search support */
8929 if (fout->remoteVersion < 80300)
8930 {
8931 *numTSConfigs = 0;
8932 return NULL;
8933 }
8934
8935 query = createPQExpBuffer();
8936
8937 appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
8938 "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
8939 "FROM pg_ts_config",
8940 username_subquery);
8941
8942 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8943
8944 ntups = PQntuples(res);
8945 *numTSConfigs = ntups;
8946
8947 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
8948
8949 i_tableoid = PQfnumber(res, "tableoid");
8950 i_oid = PQfnumber(res, "oid");
8951 i_cfgname = PQfnumber(res, "cfgname");
8952 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
8953 i_rolname = PQfnumber(res, "rolname");
8954 i_cfgparser = PQfnumber(res, "cfgparser");
8955
8956 for (i = 0; i < ntups; i++)
8957 {
8958 cfginfo[i].dobj.objType = DO_TSCONFIG;
8959 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8960 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8961 AssignDumpId(&cfginfo[i].dobj);
8962 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
8963 cfginfo[i].dobj.namespace =
8964 findNamespace(fout,
8965 atooid(PQgetvalue(res, i, i_cfgnamespace)));
8966 cfginfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
8967 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
8968
8969 /* Decide whether we want to dump it */
8970 selectDumpableObject(&(cfginfo[i].dobj), fout);
8971
8972 /* Text Search Configurations do not currently have ACLs. */
8973 cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
8974 }
8975
8976 PQclear(res);
8977
8978 destroyPQExpBuffer(query);
8979
8980 return cfginfo;
8981}
8982
8983/*
8984 * getForeignDataWrappers:
8985 * read all foreign-data wrappers in the system catalogs and return
8986 * them in the FdwInfo* structure
8987 *
8988 * numForeignDataWrappers is set to the number of fdws read in
8989 */
8990FdwInfo *
8991getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
8992{
8993 DumpOptions *dopt = fout->dopt;
8994 PGresult *res;
8995 int ntups;
8996 int i;
8997 PQExpBuffer query;
8998 FdwInfo *fdwinfo;
8999 int i_tableoid;
9000 int i_oid;
9001 int i_fdwname;
9002 int i_rolname;
9003 int i_fdwhandler;
9004 int i_fdwvalidator;
9005 int i_fdwacl;
9006 int i_rfdwacl;
9007 int i_initfdwacl;
9008 int i_initrfdwacl;
9009 int i_fdwoptions;
9010
9011 /* Before 8.4, there are no foreign-data wrappers */
9012 if (fout->remoteVersion < 80400)
9013 {
9014 *numForeignDataWrappers = 0;
9015 return NULL;
9016 }
9017
9018 query = createPQExpBuffer();
9019
9020 if (fout->remoteVersion >= 90600)
9021 {
9022 PQExpBuffer acl_subquery = createPQExpBuffer();
9023 PQExpBuffer racl_subquery = createPQExpBuffer();
9024 PQExpBuffer initacl_subquery = createPQExpBuffer();
9025 PQExpBuffer initracl_subquery = createPQExpBuffer();
9026
9027 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
9028 initracl_subquery, "f.fdwacl", "f.fdwowner", "'F'",
9029 dopt->binary_upgrade);
9030
9031 appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, "
9032 "(%s f.fdwowner) AS rolname, "
9033 "f.fdwhandler::pg_catalog.regproc, "
9034 "f.fdwvalidator::pg_catalog.regproc, "
9035 "%s AS fdwacl, "
9036 "%s AS rfdwacl, "
9037 "%s AS initfdwacl, "
9038 "%s AS initrfdwacl, "
9039 "array_to_string(ARRAY("
9040 "SELECT quote_ident(option_name) || ' ' || "
9041 "quote_literal(option_value) "
9042 "FROM pg_options_to_table(f.fdwoptions) "
9043 "ORDER BY option_name"
9044 "), E',\n ') AS fdwoptions "
9045 "FROM pg_foreign_data_wrapper f "
9046 "LEFT JOIN pg_init_privs pip ON "
9047 "(f.oid = pip.objoid "
9048 "AND pip.classoid = 'pg_foreign_data_wrapper'::regclass "
9049 "AND pip.objsubid = 0) ",
9050 username_subquery,
9051 acl_subquery->data,
9052 racl_subquery->data,
9053 initacl_subquery->data,
9054 initracl_subquery->data);
9055
9056 destroyPQExpBuffer(acl_subquery);
9057 destroyPQExpBuffer(racl_subquery);
9058 destroyPQExpBuffer(initacl_subquery);
9059 destroyPQExpBuffer(initracl_subquery);
9060 }
9061 else if (fout->remoteVersion >= 90100)
9062 {
9063 appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
9064 "(%s fdwowner) AS rolname, "
9065 "fdwhandler::pg_catalog.regproc, "
9066 "fdwvalidator::pg_catalog.regproc, fdwacl, "
9067 "NULL as rfdwacl, "
9068 "NULL as initfdwacl, NULL AS initrfdwacl, "
9069 "array_to_string(ARRAY("
9070 "SELECT quote_ident(option_name) || ' ' || "
9071 "quote_literal(option_value) "
9072 "FROM pg_options_to_table(fdwoptions) "
9073 "ORDER BY option_name"
9074 "), E',\n ') AS fdwoptions "
9075 "FROM pg_foreign_data_wrapper",
9076 username_subquery);
9077 }
9078 else
9079 {
9080 appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
9081 "(%s fdwowner) AS rolname, "
9082 "'-' AS fdwhandler, "
9083 "fdwvalidator::pg_catalog.regproc, fdwacl, "
9084 "NULL as rfdwacl, "
9085 "NULL as initfdwacl, NULL AS initrfdwacl, "
9086 "array_to_string(ARRAY("
9087 "SELECT quote_ident(option_name) || ' ' || "
9088 "quote_literal(option_value) "
9089 "FROM pg_options_to_table(fdwoptions) "
9090 "ORDER BY option_name"
9091 "), E',\n ') AS fdwoptions "
9092 "FROM pg_foreign_data_wrapper",
9093 username_subquery);
9094 }
9095
9096 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9097
9098 ntups = PQntuples(res);
9099 *numForeignDataWrappers = ntups;
9100
9101 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9102
9103 i_tableoid = PQfnumber(res, "tableoid");
9104 i_oid = PQfnumber(res, "oid");
9105 i_fdwname = PQfnumber(res, "fdwname");
9106 i_rolname = PQfnumber(res, "rolname");
9107 i_fdwhandler = PQfnumber(res, "fdwhandler");
9108 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9109 i_fdwacl = PQfnumber(res, "fdwacl");
9110 i_rfdwacl = PQfnumber(res, "rfdwacl");
9111 i_initfdwacl = PQfnumber(res, "initfdwacl");
9112 i_initrfdwacl = PQfnumber(res, "initrfdwacl");
9113 i_fdwoptions = PQfnumber(res, "fdwoptions");
9114
9115 for (i = 0; i < ntups; i++)
9116 {
9117 fdwinfo[i].dobj.objType = DO_FDW;
9118 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9119 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9120 AssignDumpId(&fdwinfo[i].dobj);
9121 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9122 fdwinfo[i].dobj.namespace = NULL;
9123 fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
9124 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9125 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9126 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9127 fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9128 fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl));
9129 fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl));
9130 fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl));
9131
9132 /* Decide whether we want to dump it */
9133 selectDumpableObject(&(fdwinfo[i].dobj), fout);
9134
9135 /* Do not try to dump ACL if no ACL exists. */
9136 if (PQgetisnull(res, i, i_fdwacl) && PQgetisnull(res, i, i_rfdwacl) &&
9137 PQgetisnull(res, i, i_initfdwacl) &&
9138 PQgetisnull(res, i, i_initrfdwacl))
9139 fdwinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
9140 }
9141
9142 PQclear(res);
9143
9144 destroyPQExpBuffer(query);
9145
9146 return fdwinfo;
9147}
9148
9149/*
9150 * getForeignServers:
9151 * read all foreign servers in the system catalogs and return
9152 * them in the ForeignServerInfo * structure
9153 *
9154 * numForeignServers is set to the number of servers read in
9155 */
9156ForeignServerInfo *
9157getForeignServers(Archive *fout, int *numForeignServers)
9158{
9159 DumpOptions *dopt = fout->dopt;
9160 PGresult *res;
9161 int ntups;
9162 int i;
9163 PQExpBuffer query;
9164 ForeignServerInfo *srvinfo;
9165 int i_tableoid;
9166 int i_oid;
9167 int i_srvname;
9168 int i_rolname;
9169 int i_srvfdw;
9170 int i_srvtype;
9171 int i_srvversion;
9172 int i_srvacl;
9173 int i_rsrvacl;
9174 int i_initsrvacl;
9175 int i_initrsrvacl;
9176 int i_srvoptions;
9177
9178 /* Before 8.4, there are no foreign servers */
9179 if (fout->remoteVersion < 80400)
9180 {
9181 *numForeignServers = 0;
9182 return NULL;
9183 }
9184
9185 query = createPQExpBuffer();
9186
9187 if (fout->remoteVersion >= 90600)
9188 {
9189 PQExpBuffer acl_subquery = createPQExpBuffer();
9190 PQExpBuffer racl_subquery = createPQExpBuffer();
9191 PQExpBuffer initacl_subquery = createPQExpBuffer();
9192 PQExpBuffer initracl_subquery = createPQExpBuffer();
9193
9194 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
9195 initracl_subquery, "f.srvacl", "f.srvowner", "'S'",
9196 dopt->binary_upgrade);
9197
9198 appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, "
9199 "(%s f.srvowner) AS rolname, "
9200 "f.srvfdw, f.srvtype, f.srvversion, "
9201 "%s AS srvacl, "
9202 "%s AS rsrvacl, "
9203 "%s AS initsrvacl, "
9204 "%s AS initrsrvacl, "
9205 "array_to_string(ARRAY("
9206 "SELECT quote_ident(option_name) || ' ' || "
9207 "quote_literal(option_value) "
9208 "FROM pg_options_to_table(f.srvoptions) "
9209 "ORDER BY option_name"
9210 "), E',\n ') AS srvoptions "
9211 "FROM pg_foreign_server f "
9212 "LEFT JOIN pg_init_privs pip "
9213 "ON (f.oid = pip.objoid "
9214 "AND pip.classoid = 'pg_foreign_server'::regclass "
9215 "AND pip.objsubid = 0) ",
9216 username_subquery,
9217 acl_subquery->data,
9218 racl_subquery->data,
9219 initacl_subquery->data,
9220 initracl_subquery->data);
9221
9222 destroyPQExpBuffer(acl_subquery);
9223 destroyPQExpBuffer(racl_subquery);
9224 destroyPQExpBuffer(initacl_subquery);
9225 destroyPQExpBuffer(initracl_subquery);
9226 }
9227 else
9228 {
9229 appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
9230 "(%s srvowner) AS rolname, "
9231 "srvfdw, srvtype, srvversion, srvacl, "
9232 "NULL AS rsrvacl, "
9233 "NULL AS initsrvacl, NULL AS initrsrvacl, "
9234 "array_to_string(ARRAY("
9235 "SELECT quote_ident(option_name) || ' ' || "
9236 "quote_literal(option_value) "
9237 "FROM pg_options_to_table(srvoptions) "
9238 "ORDER BY option_name"
9239 "), E',\n ') AS srvoptions "
9240 "FROM pg_foreign_server",
9241 username_subquery);
9242 }
9243
9244 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9245
9246 ntups = PQntuples(res);
9247 *numForeignServers = ntups;
9248
9249 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9250
9251 i_tableoid = PQfnumber(res, "tableoid");
9252 i_oid = PQfnumber(res, "oid");
9253 i_srvname = PQfnumber(res, "srvname");
9254 i_rolname = PQfnumber(res, "rolname");
9255 i_srvfdw = PQfnumber(res, "srvfdw");
9256 i_srvtype = PQfnumber(res, "srvtype");
9257 i_srvversion = PQfnumber(res, "srvversion");
9258 i_srvacl = PQfnumber(res, "srvacl");
9259 i_rsrvacl = PQfnumber(res, "rsrvacl");
9260 i_initsrvacl = PQfnumber(res, "initsrvacl");
9261 i_initrsrvacl = PQfnumber(res, "initrsrvacl");
9262 i_srvoptions = PQfnumber(res, "srvoptions");
9263
9264 for (i = 0; i < ntups; i++)
9265 {
9266 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9267 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9268 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9269 AssignDumpId(&srvinfo[i].dobj);
9270 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9271 srvinfo[i].dobj.namespace = NULL;
9272 srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
9273 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9274 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9275 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9276 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9277 srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9278 srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl));
9279 srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl));
9280 srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl));
9281
9282 /* Decide whether we want to dump it */
9283 selectDumpableObject(&(srvinfo[i].dobj), fout);
9284
9285 /* Do not try to dump ACL if no ACL exists. */
9286 if (PQgetisnull(res, i, i_srvacl) && PQgetisnull(res, i, i_rsrvacl) &&
9287 PQgetisnull(res, i, i_initsrvacl) &&
9288 PQgetisnull(res, i, i_initrsrvacl))
9289 srvinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
9290 }
9291
9292 PQclear(res);
9293
9294 destroyPQExpBuffer(query);
9295
9296 return srvinfo;
9297}
9298
9299/*
9300 * getDefaultACLs:
9301 * read all default ACL information in the system catalogs and return
9302 * them in the DefaultACLInfo structure
9303 *
9304 * numDefaultACLs is set to the number of ACLs read in
9305 */
9306DefaultACLInfo *
9307getDefaultACLs(Archive *fout, int *numDefaultACLs)
9308{
9309 DumpOptions *dopt = fout->dopt;
9310 DefaultACLInfo *daclinfo;
9311 PQExpBuffer query;
9312 PGresult *res;
9313 int i_oid;
9314 int i_tableoid;
9315 int i_defaclrole;
9316 int i_defaclnamespace;
9317 int i_defaclobjtype;
9318 int i_defaclacl;
9319 int i_rdefaclacl;
9320 int i_initdefaclacl;
9321 int i_initrdefaclacl;
9322 int i,
9323 ntups;
9324
9325 if (fout->remoteVersion < 90000)
9326 {
9327 *numDefaultACLs = 0;
9328 return NULL;
9329 }
9330
9331 query = createPQExpBuffer();
9332
9333 if (fout->remoteVersion >= 90600)
9334 {
9335 PQExpBuffer acl_subquery = createPQExpBuffer();
9336 PQExpBuffer racl_subquery = createPQExpBuffer();
9337 PQExpBuffer initacl_subquery = createPQExpBuffer();
9338 PQExpBuffer initracl_subquery = createPQExpBuffer();
9339
9340 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
9341 initracl_subquery, "defaclacl", "defaclrole",
9342 "CASE WHEN defaclobjtype = 'S' THEN 's' ELSE defaclobjtype END::\"char\"",
9343 dopt->binary_upgrade);
9344
9345 appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, "
9346 "(%s d.defaclrole) AS defaclrole, "
9347 "d.defaclnamespace, "
9348 "d.defaclobjtype, "
9349 "%s AS defaclacl, "
9350 "%s AS rdefaclacl, "
9351 "%s AS initdefaclacl, "
9352 "%s AS initrdefaclacl "
9353 "FROM pg_default_acl d "
9354 "LEFT JOIN pg_init_privs pip ON "
9355 "(d.oid = pip.objoid "
9356 "AND pip.classoid = 'pg_default_acl'::regclass "
9357 "AND pip.objsubid = 0) ",
9358 username_subquery,
9359 acl_subquery->data,
9360 racl_subquery->data,
9361 initacl_subquery->data,
9362 initracl_subquery->data);
9363 }
9364 else
9365 {
9366 appendPQExpBuffer(query, "SELECT oid, tableoid, "
9367 "(%s defaclrole) AS defaclrole, "
9368 "defaclnamespace, "
9369 "defaclobjtype, "
9370 "defaclacl, "
9371 "NULL AS rdefaclacl, "
9372 "NULL AS initdefaclacl, "
9373 "NULL AS initrdefaclacl "
9374 "FROM pg_default_acl",
9375 username_subquery);
9376 }
9377
9378 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9379
9380 ntups = PQntuples(res);
9381 *numDefaultACLs = ntups;
9382
9383 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9384
9385 i_oid = PQfnumber(res, "oid");
9386 i_tableoid = PQfnumber(res, "tableoid");
9387 i_defaclrole = PQfnumber(res, "defaclrole");
9388 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9389 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9390 i_defaclacl = PQfnumber(res, "defaclacl");
9391 i_rdefaclacl = PQfnumber(res, "rdefaclacl");
9392 i_initdefaclacl = PQfnumber(res, "initdefaclacl");
9393 i_initrdefaclacl = PQfnumber(res, "initrdefaclacl");
9394
9395 for (i = 0; i < ntups; i++)
9396 {
9397 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9398
9399 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9400 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9401 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9402 AssignDumpId(&daclinfo[i].dobj);
9403 /* cheesy ... is it worth coming up with a better object name? */
9404 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9405
9406 if (nspid != InvalidOid)
9407 daclinfo[i].dobj.namespace = findNamespace(fout, nspid);
9408 else
9409 daclinfo[i].dobj.namespace = NULL;
9410
9411 daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole));
9412 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9413 daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9414 daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl));
9415 daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl));
9416 daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl));
9417
9418 /* Decide whether we want to dump it */
9419 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9420 }
9421
9422 PQclear(res);
9423
9424 destroyPQExpBuffer(query);
9425
9426 return daclinfo;
9427}
9428
9429/*
9430 * dumpComment --
9431 *
9432 * This routine is used to dump any comments associated with the
9433 * object handed to this routine. The routine takes the object type
9434 * and object name (ready to print, except for schema decoration), plus
9435 * the namespace and owner of the object (for labeling the ArchiveEntry),
9436 * plus catalog ID and subid which are the lookup key for pg_description,
9437 * plus the dump ID for the object (for setting a dependency).
9438 * If a matching pg_description entry is found, it is dumped.
9439 *
9440 * Note: in some cases, such as comments for triggers and rules, the "type"
9441 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
9442 * but it doesn't seem worth complicating the API for all callers to make
9443 * it cleaner.
9444 *
9445 * Note: although this routine takes a dumpId for dependency purposes,
9446 * that purpose is just to mark the dependency in the emitted dump file
9447 * for possible future use by pg_restore. We do NOT use it for determining
9448 * ordering of the comment in the dump file, because this routine is called
9449 * after dependency sorting occurs. This routine should be called just after
9450 * calling ArchiveEntry() for the specified object.
9451 */
9452static void
9453dumpComment(Archive *fout, const char *type, const char *name,
9454 const char *namespace, const char *owner,
9455 CatalogId catalogId, int subid, DumpId dumpId)
9456{
9457 DumpOptions *dopt = fout->dopt;
9458 CommentItem *comments;
9459 int ncomments;
9460
9461 /* do nothing, if --no-comments is supplied */
9462 if (dopt->no_comments)
9463 return;
9464
9465 /* Comments are schema not data ... except blob comments are data */
9466 if (strcmp(type, "LARGE OBJECT") != 0)
9467 {
9468 if (dopt->dataOnly)
9469 return;
9470 }
9471 else
9472 {
9473 /* We do dump blob comments in binary-upgrade mode */
9474 if (dopt->schemaOnly && !dopt->binary_upgrade)
9475 return;
9476 }
9477
9478 /* Search for comments associated with catalogId, using table */
9479 ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
9480 &comments);
9481
9482 /* Is there one matching the subid? */
9483 while (ncomments > 0)
9484 {
9485 if (comments->objsubid == subid)
9486 break;
9487 comments++;
9488 ncomments--;
9489 }
9490
9491 /* If a comment exists, build COMMENT ON statement */
9492 if (ncomments > 0)
9493 {
9494 PQExpBuffer query = createPQExpBuffer();
9495 PQExpBuffer tag = createPQExpBuffer();
9496
9497 appendPQExpBuffer(query, "COMMENT ON %s ", type);
9498 if (namespace && *namespace)
9499 appendPQExpBuffer(query, "%s.", fmtId(namespace));
9500 appendPQExpBuffer(query, "%s IS ", name);
9501 appendStringLiteralAH(query, comments->descr, fout);
9502 appendPQExpBufferStr(query, ";\n");
9503
9504 appendPQExpBuffer(tag, "%s %s", type, name);
9505
9506 /*
9507 * We mark comments as SECTION_NONE because they really belong in the
9508 * same section as their parent, whether that is pre-data or
9509 * post-data.
9510 */
9511 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9512 ARCHIVE_OPTS(.tag = tag->data,
9513 .namespace = namespace,
9514 .owner = owner,
9515 .description = "COMMENT",
9516 .section = SECTION_NONE,
9517 .createStmt = query->data,
9518 .deps = &dumpId,
9519 .nDeps = 1));
9520
9521 destroyPQExpBuffer(query);
9522 destroyPQExpBuffer(tag);
9523 }
9524}
9525
9526/*
9527 * dumpTableComment --
9528 *
9529 * As above, but dump comments for both the specified table (or view)
9530 * and its columns.
9531 */
9532static void
9533dumpTableComment(Archive *fout, TableInfo *tbinfo,
9534 const char *reltypename)
9535{
9536 DumpOptions *dopt = fout->dopt;
9537 CommentItem *comments;
9538 int ncomments;
9539 PQExpBuffer query;
9540 PQExpBuffer tag;
9541
9542 /* do nothing, if --no-comments is supplied */
9543 if (dopt->no_comments)
9544 return;
9545
9546 /* Comments are SCHEMA not data */
9547 if (dopt->dataOnly)
9548 return;
9549
9550 /* Search for comments associated with relation, using table */
9551 ncomments = findComments(fout,
9552 tbinfo->dobj.catId.tableoid,
9553 tbinfo->dobj.catId.oid,
9554 &comments);
9555
9556 /* If comments exist, build COMMENT ON statements */
9557 if (ncomments <= 0)
9558 return;
9559
9560 query = createPQExpBuffer();
9561 tag = createPQExpBuffer();
9562
9563 while (ncomments > 0)
9564 {
9565 const char *descr = comments->descr;
9566 int objsubid = comments->objsubid;
9567
9568 if (objsubid == 0)
9569 {
9570 resetPQExpBuffer(tag);
9571 appendPQExpBuffer(tag, "%s %s", reltypename,
9572 fmtId(tbinfo->dobj.name));
9573
9574 resetPQExpBuffer(query);
9575 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
9576 fmtQualifiedDumpable(tbinfo));
9577 appendStringLiteralAH(query, descr, fout);
9578 appendPQExpBufferStr(query, ";\n");
9579
9580 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9581 ARCHIVE_OPTS(.tag = tag->data,
9582 .namespace = tbinfo->dobj.namespace->dobj.name,
9583 .owner = tbinfo->rolname,
9584 .description = "COMMENT",
9585 .section = SECTION_NONE,
9586 .createStmt = query->data,
9587 .deps = &(tbinfo->dobj.dumpId),
9588 .nDeps = 1));
9589 }
9590 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
9591 {
9592 resetPQExpBuffer(tag);
9593 appendPQExpBuffer(tag, "COLUMN %s.",
9594 fmtId(tbinfo->dobj.name));
9595 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
9596
9597 resetPQExpBuffer(query);
9598 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
9599 fmtQualifiedDumpable(tbinfo));
9600 appendPQExpBuffer(query, "%s IS ",
9601 fmtId(tbinfo->attnames[objsubid - 1]));
9602 appendStringLiteralAH(query, descr, fout);
9603 appendPQExpBufferStr(query, ";\n");
9604
9605 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9606 ARCHIVE_OPTS(.tag = tag->data,
9607 .namespace = tbinfo->dobj.namespace->dobj.name,
9608 .owner = tbinfo->rolname,
9609 .description = "COMMENT",
9610 .section = SECTION_NONE,
9611 .createStmt = query->data,
9612 .deps = &(tbinfo->dobj.dumpId),
9613 .nDeps = 1));
9614 }
9615
9616 comments++;
9617 ncomments--;
9618 }
9619
9620 destroyPQExpBuffer(query);
9621 destroyPQExpBuffer(tag);
9622}
9623
9624/*
9625 * findComments --
9626 *
9627 * Find the comment(s), if any, associated with the given object. All the
9628 * objsubid values associated with the given classoid/objoid are found with
9629 * one search.
9630 */
9631static int
9632findComments(Archive *fout, Oid classoid, Oid objoid,
9633 CommentItem **items)
9634{
9635 /* static storage for table of comments */
9636 static CommentItem *comments = NULL;
9637 static int ncomments = -1;
9638
9639 CommentItem *middle = NULL;
9640 CommentItem *low;
9641 CommentItem *high;
9642 int nmatch;
9643
9644 /* Get comments if we didn't already */
9645 if (ncomments < 0)
9646 ncomments = collectComments(fout, &comments);
9647
9648 /*
9649 * Do binary search to find some item matching the object.
9650 */
9651 low = &comments[0];
9652 high = &comments[ncomments - 1];
9653 while (low <= high)
9654 {
9655 middle = low + (high - low) / 2;
9656
9657 if (classoid < middle->classoid)
9658 high = middle - 1;
9659 else if (classoid > middle->classoid)
9660 low = middle + 1;
9661 else if (objoid < middle->objoid)
9662 high = middle - 1;
9663 else if (objoid > middle->objoid)
9664 low = middle + 1;
9665 else
9666 break; /* found a match */
9667 }
9668
9669 if (low > high) /* no matches */
9670 {
9671 *items = NULL;
9672 return 0;
9673 }
9674
9675 /*
9676 * Now determine how many items match the object. The search loop
9677 * invariant still holds: only items between low and high inclusive could
9678 * match.
9679 */
9680 nmatch = 1;
9681 while (middle > low)
9682 {
9683 if (classoid != middle[-1].classoid ||
9684 objoid != middle[-1].objoid)
9685 break;
9686 middle--;
9687 nmatch++;
9688 }
9689
9690 *items = middle;
9691
9692 middle += nmatch;
9693 while (middle <= high)
9694 {
9695 if (classoid != middle->classoid ||
9696 objoid != middle->objoid)
9697 break;
9698 middle++;
9699 nmatch++;
9700 }
9701
9702 return nmatch;
9703}
9704
9705/*
9706 * collectComments --
9707 *
9708 * Construct a table of all comments available for database objects.
9709 * We used to do per-object queries for the comments, but it's much faster
9710 * to pull them all over at once, and on most databases the memory cost
9711 * isn't high.
9712 *
9713 * The table is sorted by classoid/objid/objsubid for speed in lookup.
9714 */
9715static int
9716collectComments(Archive *fout, CommentItem **items)
9717{
9718 PGresult *res;
9719 PQExpBuffer query;
9720 int i_description;
9721 int i_classoid;
9722 int i_objoid;
9723 int i_objsubid;
9724 int ntups;
9725 int i;
9726 CommentItem *comments;
9727
9728 query = createPQExpBuffer();
9729
9730 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
9731 "FROM pg_catalog.pg_description "
9732 "ORDER BY classoid, objoid, objsubid");
9733
9734 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9735
9736 /* Construct lookup table containing OIDs in numeric form */
9737
9738 i_description = PQfnumber(res, "description");
9739 i_classoid = PQfnumber(res, "classoid");
9740 i_objoid = PQfnumber(res, "objoid");
9741 i_objsubid = PQfnumber(res, "objsubid");
9742
9743 ntups = PQntuples(res);
9744
9745 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
9746
9747 for (i = 0; i < ntups; i++)
9748 {
9749 comments[i].descr = PQgetvalue(res, i, i_description);
9750 comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
9751 comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
9752 comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
9753 }
9754
9755 /* Do NOT free the PGresult since we are keeping pointers into it */
9756 destroyPQExpBuffer(query);
9757
9758 *items = comments;
9759 return ntups;
9760}
9761
9762/*
9763 * dumpDumpableObject
9764 *
9765 * This routine and its subsidiaries are responsible for creating
9766 * ArchiveEntries (TOC objects) for each object to be dumped.
9767 */
9768static void
9769dumpDumpableObject(Archive *fout, DumpableObject *dobj)
9770{
9771 switch (dobj->objType)
9772 {
9773 case DO_NAMESPACE:
9774 dumpNamespace(fout, (NamespaceInfo *) dobj);
9775 break;
9776 case DO_EXTENSION:
9777 dumpExtension(fout, (ExtensionInfo *) dobj);
9778 break;
9779 case DO_TYPE:
9780 dumpType(fout, (TypeInfo *) dobj);
9781 break;
9782 case DO_SHELL_TYPE:
9783 dumpShellType(fout, (ShellTypeInfo *) dobj);
9784 break;
9785 case DO_FUNC:
9786 dumpFunc(fout, (FuncInfo *) dobj);
9787 break;
9788 case DO_AGG:
9789 dumpAgg(fout, (AggInfo *) dobj);
9790 break;
9791 case DO_OPERATOR:
9792 dumpOpr(fout, (OprInfo *) dobj);
9793 break;
9794 case DO_ACCESS_METHOD:
9795 dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
9796 break;
9797 case DO_OPCLASS:
9798 dumpOpclass(fout, (OpclassInfo *) dobj);
9799 break;
9800 case DO_OPFAMILY:
9801 dumpOpfamily(fout, (OpfamilyInfo *) dobj);
9802 break;
9803 case DO_COLLATION:
9804 dumpCollation(fout, (CollInfo *) dobj);
9805 break;
9806 case DO_CONVERSION:
9807 dumpConversion(fout, (ConvInfo *) dobj);
9808 break;
9809 case DO_TABLE:
9810 dumpTable(fout, (TableInfo *) dobj);
9811 break;
9812 case DO_ATTRDEF:
9813 dumpAttrDef(fout, (AttrDefInfo *) dobj);
9814 break;
9815 case DO_INDEX:
9816 dumpIndex(fout, (IndxInfo *) dobj);
9817 break;
9818 case DO_INDEX_ATTACH:
9819 dumpIndexAttach(fout, (IndexAttachInfo *) dobj);
9820 break;
9821 case DO_STATSEXT:
9822 dumpStatisticsExt(fout, (StatsExtInfo *) dobj);
9823 break;
9824 case DO_REFRESH_MATVIEW:
9825 refreshMatViewData(fout, (TableDataInfo *) dobj);
9826 break;
9827 case DO_RULE:
9828 dumpRule(fout, (RuleInfo *) dobj);
9829 break;
9830 case DO_TRIGGER:
9831 dumpTrigger(fout, (TriggerInfo *) dobj);
9832 break;
9833 case DO_EVENT_TRIGGER:
9834 dumpEventTrigger(fout, (EventTriggerInfo *) dobj);
9835 break;
9836 case DO_CONSTRAINT:
9837 dumpConstraint(fout, (ConstraintInfo *) dobj);
9838 break;
9839 case DO_FK_CONSTRAINT:
9840 dumpConstraint(fout, (ConstraintInfo *) dobj);
9841 break;
9842 case DO_PROCLANG:
9843 dumpProcLang(fout, (ProcLangInfo *) dobj);
9844 break;
9845 case DO_CAST:
9846 dumpCast(fout, (CastInfo *) dobj);
9847 break;
9848 case DO_TRANSFORM:
9849 dumpTransform(fout, (TransformInfo *) dobj);
9850 break;
9851 case DO_SEQUENCE_SET:
9852 dumpSequenceData(fout, (TableDataInfo *) dobj);
9853 break;
9854 case DO_TABLE_DATA:
9855 dumpTableData(fout, (TableDataInfo *) dobj);
9856 break;
9857 case DO_DUMMY_TYPE:
9858 /* table rowtypes and array types are never dumped separately */
9859 break;
9860 case DO_TSPARSER:
9861 dumpTSParser(fout, (TSParserInfo *) dobj);
9862 break;
9863 case DO_TSDICT:
9864 dumpTSDictionary(fout, (TSDictInfo *) dobj);
9865 break;
9866 case DO_TSTEMPLATE:
9867 dumpTSTemplate(fout, (TSTemplateInfo *) dobj);
9868 break;
9869 case DO_TSCONFIG:
9870 dumpTSConfig(fout, (TSConfigInfo *) dobj);
9871 break;
9872 case DO_FDW:
9873 dumpForeignDataWrapper(fout, (FdwInfo *) dobj);
9874 break;
9875 case DO_FOREIGN_SERVER:
9876 dumpForeignServer(fout, (ForeignServerInfo *) dobj);
9877 break;
9878 case DO_DEFAULT_ACL:
9879 dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
9880 break;
9881 case DO_BLOB:
9882 dumpBlob(fout, (BlobInfo *) dobj);
9883 break;
9884 case DO_BLOB_DATA:
9885 if (dobj->dump & DUMP_COMPONENT_DATA)
9886 {
9887 TocEntry *te;
9888
9889 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
9890 ARCHIVE_OPTS(.tag = dobj->name,
9891 .description = "BLOBS",
9892 .section = SECTION_DATA,
9893 .dumpFn = dumpBlobs));
9894
9895 /*
9896 * Set the TocEntry's dataLength in case we are doing a
9897 * parallel dump and want to order dump jobs by table size.
9898 * (We need some size estimate for every TocEntry with a
9899 * DataDumper function.) We don't currently have any cheap
9900 * way to estimate the size of blobs, but it doesn't matter;
9901 * let's just set the size to a large value so parallel dumps
9902 * will launch this job first. If there's lots of blobs, we
9903 * win, and if there aren't, we don't lose much. (If you want
9904 * to improve on this, really what you should be thinking
9905 * about is allowing blob dumping to be parallelized, not just
9906 * getting a smarter estimate for the single TOC entry.)
9907 */
9908 te->dataLength = MaxBlockNumber;
9909 }
9910 break;
9911 case DO_POLICY:
9912 dumpPolicy(fout, (PolicyInfo *) dobj);
9913 break;
9914 case DO_PUBLICATION:
9915 dumpPublication(fout, (PublicationInfo *) dobj);
9916 break;
9917 case DO_PUBLICATION_REL:
9918 dumpPublicationTable(fout, (PublicationRelInfo *) dobj);
9919 break;
9920 case DO_SUBSCRIPTION:
9921 dumpSubscription(fout, (SubscriptionInfo *) dobj);
9922 break;
9923 case DO_PRE_DATA_BOUNDARY:
9924 case DO_POST_DATA_BOUNDARY:
9925 /* never dumped, nothing to do */
9926 break;
9927 }
9928}
9929
9930/*
9931 * dumpNamespace
9932 * writes out to fout the queries to recreate a user-defined namespace
9933 */
9934static void
9935dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
9936{
9937 DumpOptions *dopt = fout->dopt;
9938 PQExpBuffer q;
9939 PQExpBuffer delq;
9940 char *qnspname;
9941
9942 /* Skip if not to be dumped */
9943 if (!nspinfo->dobj.dump || dopt->dataOnly)
9944 return;
9945
9946 q = createPQExpBuffer();
9947 delq = createPQExpBuffer();
9948
9949 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
9950
9951 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
9952
9953 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
9954
9955 if (dopt->binary_upgrade)
9956 binary_upgrade_extension_member(q, &nspinfo->dobj,
9957 "SCHEMA", qnspname, NULL);
9958
9959 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
9960 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
9961 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
9962 .owner = nspinfo->rolname,
9963 .description = "SCHEMA",
9964 .section = SECTION_PRE_DATA,
9965 .createStmt = q->data,
9966 .dropStmt = delq->data));
9967
9968 /* Dump Schema Comments and Security Labels */
9969 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
9970 dumpComment(fout, "SCHEMA", qnspname,
9971 NULL, nspinfo->rolname,
9972 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
9973
9974 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
9975 dumpSecLabel(fout, "SCHEMA", qnspname,
9976 NULL, nspinfo->rolname,
9977 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
9978
9979 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
9980 dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
9981 qnspname, NULL, NULL,
9982 nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl,
9983 nspinfo->initnspacl, nspinfo->initrnspacl);
9984
9985 free(qnspname);
9986
9987 destroyPQExpBuffer(q);
9988 destroyPQExpBuffer(delq);
9989}
9990
9991/*
9992 * dumpExtension
9993 * writes out to fout the queries to recreate an extension
9994 */
9995static void
9996dumpExtension(Archive *fout, ExtensionInfo *extinfo)
9997{
9998 DumpOptions *dopt = fout->dopt;
9999 PQExpBuffer q;
10000 PQExpBuffer delq;
10001 char *qextname;
10002
10003 /* Skip if not to be dumped */
10004 if (!extinfo->dobj.dump || dopt->dataOnly)
10005 return;
10006
10007 q = createPQExpBuffer();
10008 delq = createPQExpBuffer();
10009
10010 qextname = pg_strdup(fmtId(extinfo->dobj.name));
10011
10012 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10013
10014 if (!dopt->binary_upgrade)
10015 {
10016 /*
10017 * In a regular dump, we simply create the extension, intentionally
10018 * not specifying a version, so that the destination installation's
10019 * default version is used.
10020 *
10021 * Use of IF NOT EXISTS here is unlike our behavior for other object
10022 * types; but there are various scenarios in which it's convenient to
10023 * manually create the desired extension before restoring, so we
10024 * prefer to allow it to exist already.
10025 */
10026 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10027 qextname, fmtId(extinfo->namespace));
10028 }
10029 else
10030 {
10031 /*
10032 * In binary-upgrade mode, it's critical to reproduce the state of the
10033 * database exactly, so our procedure is to create an empty extension,
10034 * restore all the contained objects normally, and add them to the
10035 * extension one by one. This function performs just the first of
10036 * those steps. binary_upgrade_extension_member() takes care of
10037 * adding member objects as they're created.
10038 */
10039 int i;
10040 int n;
10041
10042 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10043
10044 /*
10045 * We unconditionally create the extension, so we must drop it if it
10046 * exists. This could happen if the user deleted 'plpgsql' and then
10047 * readded it, causing its oid to be greater than g_last_builtin_oid.
10048 */
10049 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10050
10051 appendPQExpBufferStr(q,
10052 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10053 appendStringLiteralAH(q, extinfo->dobj.name, fout);
10054 appendPQExpBufferStr(q, ", ");
10055 appendStringLiteralAH(q, extinfo->namespace, fout);
10056 appendPQExpBufferStr(q, ", ");
10057 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10058 appendStringLiteralAH(q, extinfo->extversion, fout);
10059 appendPQExpBufferStr(q, ", ");
10060
10061 /*
10062 * Note that we're pushing extconfig (an OID array) back into
10063 * pg_extension exactly as-is. This is OK because pg_class OIDs are
10064 * preserved in binary upgrade.
10065 */
10066 if (strlen(extinfo->extconfig) > 2)
10067 appendStringLiteralAH(q, extinfo->extconfig, fout);
10068 else
10069 appendPQExpBufferStr(q, "NULL");
10070 appendPQExpBufferStr(q, ", ");
10071 if (strlen(extinfo->extcondition) > 2)
10072 appendStringLiteralAH(q, extinfo->extcondition, fout);
10073 else
10074 appendPQExpBufferStr(q, "NULL");
10075 appendPQExpBufferStr(q, ", ");
10076 appendPQExpBufferStr(q, "ARRAY[");
10077 n = 0;
10078 for (i = 0; i < extinfo->dobj.nDeps; i++)
10079 {
10080 DumpableObject *extobj;
10081
10082 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10083 if (extobj && extobj->objType == DO_EXTENSION)
10084 {
10085 if (n++ > 0)
10086 appendPQExpBufferChar(q, ',');
10087 appendStringLiteralAH(q, extobj->name, fout);
10088 }
10089 }
10090 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10091 appendPQExpBufferStr(q, ");\n");
10092 }
10093
10094 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10095 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10096 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10097 .description = "EXTENSION",
10098 .section = SECTION_PRE_DATA,
10099 .createStmt = q->data,
10100 .dropStmt = delq->data));
10101
10102 /* Dump Extension Comments and Security Labels */
10103 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10104 dumpComment(fout, "EXTENSION", qextname,
10105 NULL, "",
10106 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10107
10108 if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10109 dumpSecLabel(fout, "EXTENSION", qextname,
10110 NULL, "",
10111 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10112
10113 free(qextname);
10114
10115 destroyPQExpBuffer(q);
10116 destroyPQExpBuffer(delq);
10117}
10118
10119/*
10120 * dumpType
10121 * writes out to fout the queries to recreate a user-defined type
10122 */
10123static void
10124dumpType(Archive *fout, TypeInfo *tyinfo)
10125{
10126 DumpOptions *dopt = fout->dopt;
10127
10128 /* Skip if not to be dumped */
10129 if (!tyinfo->dobj.dump || dopt->dataOnly)
10130 return;
10131
10132 /* Dump out in proper style */
10133 if (tyinfo->typtype == TYPTYPE_BASE)
10134 dumpBaseType(fout, tyinfo);
10135 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
10136 dumpDomain(fout, tyinfo);
10137 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
10138 dumpCompositeType(fout, tyinfo);
10139 else if (tyinfo->typtype == TYPTYPE_ENUM)
10140 dumpEnumType(fout, tyinfo);
10141 else if (tyinfo->typtype == TYPTYPE_RANGE)
10142 dumpRangeType(fout, tyinfo);
10143 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
10144 dumpUndefinedType(fout, tyinfo);
10145 else
10146 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
10147 tyinfo->dobj.name);
10148}
10149
10150/*
10151 * dumpEnumType
10152 * writes out to fout the queries to recreate a user-defined enum type
10153 */
10154static void
10155dumpEnumType(Archive *fout, TypeInfo *tyinfo)
10156{
10157 DumpOptions *dopt = fout->dopt;
10158 PQExpBuffer q = createPQExpBuffer();
10159 PQExpBuffer delq = createPQExpBuffer();
10160 PQExpBuffer query = createPQExpBuffer();
10161 PGresult *res;
10162 int num,
10163 i;
10164 Oid enum_oid;
10165 char *qtypname;
10166 char *qualtypname;
10167 char *label;
10168
10169 if (fout->remoteVersion >= 90100)
10170 appendPQExpBuffer(query, "SELECT oid, enumlabel "
10171 "FROM pg_catalog.pg_enum "
10172 "WHERE enumtypid = '%u'"
10173 "ORDER BY enumsortorder",
10174 tyinfo->dobj.catId.oid);
10175 else
10176 appendPQExpBuffer(query, "SELECT oid, enumlabel "
10177 "FROM pg_catalog.pg_enum "
10178 "WHERE enumtypid = '%u'"
10179 "ORDER BY oid",
10180 tyinfo->dobj.catId.oid);
10181
10182 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10183
10184 num = PQntuples(res);
10185
10186 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10187 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10188
10189 /*
10190 * CASCADE shouldn't be required here as for normal types since the I/O
10191 * functions are generic and do not get dropped.
10192 */
10193 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10194
10195 if (dopt->binary_upgrade)
10196 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10197 tyinfo->dobj.catId.oid,
10198 false);
10199
10200 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
10201 qualtypname);
10202
10203 if (!dopt->binary_upgrade)
10204 {
10205 /* Labels with server-assigned oids */
10206 for (i = 0; i < num; i++)
10207 {
10208 label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
10209 if (i > 0)
10210 appendPQExpBufferChar(q, ',');
10211 appendPQExpBufferStr(q, "\n ");
10212 appendStringLiteralAH(q, label, fout);
10213 }
10214 }
10215
10216 appendPQExpBufferStr(q, "\n);\n");
10217
10218 if (dopt->binary_upgrade)
10219 {
10220 /* Labels with dump-assigned (preserved) oids */
10221 for (i = 0; i < num; i++)
10222 {
10223 enum_oid = atooid(PQgetvalue(res, i, PQfnumber(res, "oid")));
10224 label = PQgetvalue(res, i, PQfnumber(res, "enumlabel"));
10225
10226 if (i == 0)
10227 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
10228 appendPQExpBuffer(q,
10229 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
10230 enum_oid);
10231 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
10232 appendStringLiteralAH(q, label, fout);
10233 appendPQExpBufferStr(q, ";\n\n");
10234 }
10235 }
10236
10237 if (dopt->binary_upgrade)
10238 binary_upgrade_extension_member(q, &tyinfo->dobj,
10239 "TYPE", qtypname,
10240 tyinfo->dobj.namespace->dobj.name);
10241
10242 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10243 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10244 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10245 .namespace = tyinfo->dobj.namespace->dobj.name,
10246 .owner = tyinfo->rolname,
10247 .description = "TYPE",
10248 .section = SECTION_PRE_DATA,
10249 .createStmt = q->data,
10250 .dropStmt = delq->data));
10251
10252 /* Dump Type Comments and Security Labels */
10253 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10254 dumpComment(fout, "TYPE", qtypname,
10255 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10256 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10257
10258 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10259 dumpSecLabel(fout, "TYPE", qtypname,
10260 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10261 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10262
10263 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10264 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10265 qtypname, NULL,
10266 tyinfo->dobj.namespace->dobj.name,
10267 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10268 tyinfo->inittypacl, tyinfo->initrtypacl);
10269
10270 PQclear(res);
10271 destroyPQExpBuffer(q);
10272 destroyPQExpBuffer(delq);
10273 destroyPQExpBuffer(query);
10274 free(qtypname);
10275 free(qualtypname);
10276}
10277
10278/*
10279 * dumpRangeType
10280 * writes out to fout the queries to recreate a user-defined range type
10281 */
10282static void
10283dumpRangeType(Archive *fout, TypeInfo *tyinfo)
10284{
10285 DumpOptions *dopt = fout->dopt;
10286 PQExpBuffer q = createPQExpBuffer();
10287 PQExpBuffer delq = createPQExpBuffer();
10288 PQExpBuffer query = createPQExpBuffer();
10289 PGresult *res;
10290 Oid collationOid;
10291 char *qtypname;
10292 char *qualtypname;
10293 char *procname;
10294
10295 appendPQExpBuffer(query,
10296 "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
10297 "opc.opcname AS opcname, "
10298 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
10299 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
10300 "opc.opcdefault, "
10301 "CASE WHEN rngcollation = st.typcollation THEN 0 "
10302 " ELSE rngcollation END AS collation, "
10303 "rngcanonical, rngsubdiff "
10304 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
10305 " pg_catalog.pg_opclass opc "
10306 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
10307 "rngtypid = '%u'",
10308 tyinfo->dobj.catId.oid);
10309
10310 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10311
10312 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10313 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10314
10315 /*
10316 * CASCADE shouldn't be required here as for normal types since the I/O
10317 * functions are generic and do not get dropped.
10318 */
10319 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10320
10321 if (dopt->binary_upgrade)
10322 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10323 tyinfo->dobj.catId.oid,
10324 false);
10325
10326 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
10327 qualtypname);
10328
10329 appendPQExpBuffer(q, "\n subtype = %s",
10330 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
10331
10332 /* print subtype_opclass only if not default for subtype */
10333 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
10334 {
10335 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
10336 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
10337
10338 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
10339 fmtId(nspname));
10340 appendPQExpBufferStr(q, fmtId(opcname));
10341 }
10342
10343 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
10344 if (OidIsValid(collationOid))
10345 {
10346 CollInfo *coll = findCollationByOid(collationOid);
10347
10348 if (coll)
10349 appendPQExpBuffer(q, ",\n collation = %s",
10350 fmtQualifiedDumpable(coll));
10351 }
10352
10353 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
10354 if (strcmp(procname, "-") != 0)
10355 appendPQExpBuffer(q, ",\n canonical = %s", procname);
10356
10357 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
10358 if (strcmp(procname, "-") != 0)
10359 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
10360
10361 appendPQExpBufferStr(q, "\n);\n");
10362
10363 if (dopt->binary_upgrade)
10364 binary_upgrade_extension_member(q, &tyinfo->dobj,
10365 "TYPE", qtypname,
10366 tyinfo->dobj.namespace->dobj.name);
10367
10368 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10369 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10370 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10371 .namespace = tyinfo->dobj.namespace->dobj.name,
10372 .owner = tyinfo->rolname,
10373 .description = "TYPE",
10374 .section = SECTION_PRE_DATA,
10375 .createStmt = q->data,
10376 .dropStmt = delq->data));
10377
10378 /* Dump Type Comments and Security Labels */
10379 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10380 dumpComment(fout, "TYPE", qtypname,
10381 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10382 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10383
10384 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10385 dumpSecLabel(fout, "TYPE", qtypname,
10386 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10387 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10388
10389 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10390 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10391 qtypname, NULL,
10392 tyinfo->dobj.namespace->dobj.name,
10393 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10394 tyinfo->inittypacl, tyinfo->initrtypacl);
10395
10396 PQclear(res);
10397 destroyPQExpBuffer(q);
10398 destroyPQExpBuffer(delq);
10399 destroyPQExpBuffer(query);
10400 free(qtypname);
10401 free(qualtypname);
10402}
10403
10404/*
10405 * dumpUndefinedType
10406 * writes out to fout the queries to recreate a !typisdefined type
10407 *
10408 * This is a shell type, but we use different terminology to distinguish
10409 * this case from where we have to emit a shell type definition to break
10410 * circular dependencies. An undefined type shouldn't ever have anything
10411 * depending on it.
10412 */
10413static void
10414dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
10415{
10416 DumpOptions *dopt = fout->dopt;
10417 PQExpBuffer q = createPQExpBuffer();
10418 PQExpBuffer delq = createPQExpBuffer();
10419 char *qtypname;
10420 char *qualtypname;
10421
10422 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10423 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10424
10425 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10426
10427 if (dopt->binary_upgrade)
10428 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10429 tyinfo->dobj.catId.oid,
10430 false);
10431
10432 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
10433 qualtypname);
10434
10435 if (dopt->binary_upgrade)
10436 binary_upgrade_extension_member(q, &tyinfo->dobj,
10437 "TYPE", qtypname,
10438 tyinfo->dobj.namespace->dobj.name);
10439
10440 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10441 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10442 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10443 .namespace = tyinfo->dobj.namespace->dobj.name,
10444 .owner = tyinfo->rolname,
10445 .description = "TYPE",
10446 .section = SECTION_PRE_DATA,
10447 .createStmt = q->data,
10448 .dropStmt = delq->data));
10449
10450 /* Dump Type Comments and Security Labels */
10451 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10452 dumpComment(fout, "TYPE", qtypname,
10453 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10454 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10455
10456 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10457 dumpSecLabel(fout, "TYPE", qtypname,
10458 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10459 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10460
10461 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10462 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10463 qtypname, NULL,
10464 tyinfo->dobj.namespace->dobj.name,
10465 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10466 tyinfo->inittypacl, tyinfo->initrtypacl);
10467
10468 destroyPQExpBuffer(q);
10469 destroyPQExpBuffer(delq);
10470 free(qtypname);
10471 free(qualtypname);
10472}
10473
10474/*
10475 * dumpBaseType
10476 * writes out to fout the queries to recreate a user-defined base type
10477 */
10478static void
10479dumpBaseType(Archive *fout, TypeInfo *tyinfo)
10480{
10481 DumpOptions *dopt = fout->dopt;
10482 PQExpBuffer q = createPQExpBuffer();
10483 PQExpBuffer delq = createPQExpBuffer();
10484 PQExpBuffer query = createPQExpBuffer();
10485 PGresult *res;
10486 char *qtypname;
10487 char *qualtypname;
10488 char *typlen;
10489 char *typinput;
10490 char *typoutput;
10491 char *typreceive;
10492 char *typsend;
10493 char *typmodin;
10494 char *typmodout;
10495 char *typanalyze;
10496 Oid typreceiveoid;
10497 Oid typsendoid;
10498 Oid typmodinoid;
10499 Oid typmodoutoid;
10500 Oid typanalyzeoid;
10501 char *typcategory;
10502 char *typispreferred;
10503 char *typdelim;
10504 char *typbyval;
10505 char *typalign;
10506 char *typstorage;
10507 char *typcollatable;
10508 char *typdefault;
10509 bool typdefault_is_literal = false;
10510
10511 /* Fetch type-specific details */
10512 if (fout->remoteVersion >= 90100)
10513 {
10514 appendPQExpBuffer(query, "SELECT typlen, "
10515 "typinput, typoutput, typreceive, typsend, "
10516 "typmodin, typmodout, typanalyze, "
10517 "typreceive::pg_catalog.oid AS typreceiveoid, "
10518 "typsend::pg_catalog.oid AS typsendoid, "
10519 "typmodin::pg_catalog.oid AS typmodinoid, "
10520 "typmodout::pg_catalog.oid AS typmodoutoid, "
10521 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10522 "typcategory, typispreferred, "
10523 "typdelim, typbyval, typalign, typstorage, "
10524 "(typcollation <> 0) AS typcollatable, "
10525 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "
10526 "FROM pg_catalog.pg_type "
10527 "WHERE oid = '%u'::pg_catalog.oid",
10528 tyinfo->dobj.catId.oid);
10529 }
10530 else if (fout->remoteVersion >= 80400)
10531 {
10532 appendPQExpBuffer(query, "SELECT typlen, "
10533 "typinput, typoutput, typreceive, typsend, "
10534 "typmodin, typmodout, typanalyze, "
10535 "typreceive::pg_catalog.oid AS typreceiveoid, "
10536 "typsend::pg_catalog.oid AS typsendoid, "
10537 "typmodin::pg_catalog.oid AS typmodinoid, "
10538 "typmodout::pg_catalog.oid AS typmodoutoid, "
10539 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10540 "typcategory, typispreferred, "
10541 "typdelim, typbyval, typalign, typstorage, "
10542 "false AS typcollatable, "
10543 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault "
10544 "FROM pg_catalog.pg_type "
10545 "WHERE oid = '%u'::pg_catalog.oid",
10546 tyinfo->dobj.catId.oid);
10547 }
10548 else if (fout->remoteVersion >= 80300)
10549 {
10550 /* Before 8.4, pg_get_expr does not allow 0 for its second arg */
10551 appendPQExpBuffer(query, "SELECT typlen, "
10552 "typinput, typoutput, typreceive, typsend, "
10553 "typmodin, typmodout, typanalyze, "
10554 "typreceive::pg_catalog.oid AS typreceiveoid, "
10555 "typsend::pg_catalog.oid AS typsendoid, "
10556 "typmodin::pg_catalog.oid AS typmodinoid, "
10557 "typmodout::pg_catalog.oid AS typmodoutoid, "
10558 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10559 "'U' AS typcategory, false AS typispreferred, "
10560 "typdelim, typbyval, typalign, typstorage, "
10561 "false AS typcollatable, "
10562 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "
10563 "FROM pg_catalog.pg_type "
10564 "WHERE oid = '%u'::pg_catalog.oid",
10565 tyinfo->dobj.catId.oid);
10566 }
10567 else
10568 {
10569 appendPQExpBuffer(query, "SELECT typlen, "
10570 "typinput, typoutput, typreceive, typsend, "
10571 "'-' AS typmodin, '-' AS typmodout, "
10572 "typanalyze, "
10573 "typreceive::pg_catalog.oid AS typreceiveoid, "
10574 "typsend::pg_catalog.oid AS typsendoid, "
10575 "0 AS typmodinoid, 0 AS typmodoutoid, "
10576 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
10577 "'U' AS typcategory, false AS typispreferred, "
10578 "typdelim, typbyval, typalign, typstorage, "
10579 "false AS typcollatable, "
10580 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "
10581 "FROM pg_catalog.pg_type "
10582 "WHERE oid = '%u'::pg_catalog.oid",
10583 tyinfo->dobj.catId.oid);
10584 }
10585
10586 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10587
10588 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
10589 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
10590 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
10591 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
10592 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
10593 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
10594 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
10595 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
10596 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
10597 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
10598 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
10599 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
10600 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
10601 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
10602 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
10603 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
10604 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
10605 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
10606 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
10607 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
10608 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
10609 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
10610 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
10611 {
10612 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
10613 typdefault_is_literal = true; /* it needs quotes */
10614 }
10615 else
10616 typdefault = NULL;
10617
10618 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10619 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10620
10621 /*
10622 * The reason we include CASCADE is that the circular dependency between
10623 * the type and its I/O functions makes it impossible to drop the type any
10624 * other way.
10625 */
10626 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
10627
10628 /*
10629 * We might already have a shell type, but setting pg_type_oid is
10630 * harmless, and in any case we'd better set the array type OID.
10631 */
10632 if (dopt->binary_upgrade)
10633 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10634 tyinfo->dobj.catId.oid,
10635 false);
10636
10637 appendPQExpBuffer(q,
10638 "CREATE TYPE %s (\n"
10639 " INTERNALLENGTH = %s",
10640 qualtypname,
10641 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
10642
10643 /* regproc result is sufficiently quoted already */
10644 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
10645 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
10646 if (OidIsValid(typreceiveoid))
10647 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
10648 if (OidIsValid(typsendoid))
10649 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
10650 if (OidIsValid(typmodinoid))
10651 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
10652 if (OidIsValid(typmodoutoid))
10653 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
10654 if (OidIsValid(typanalyzeoid))
10655 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
10656
10657 if (strcmp(typcollatable, "t") == 0)
10658 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
10659
10660 if (typdefault != NULL)
10661 {
10662 appendPQExpBufferStr(q, ",\n DEFAULT = ");
10663 if (typdefault_is_literal)
10664 appendStringLiteralAH(q, typdefault, fout);
10665 else
10666 appendPQExpBufferStr(q, typdefault);
10667 }
10668
10669 if (OidIsValid(tyinfo->typelem))
10670 {
10671 char *elemType;
10672
10673 elemType = getFormattedTypeName(fout, tyinfo->typelem, zeroAsOpaque);
10674 appendPQExpBuffer(q, ",\n ELEMENT = %s", elemType);
10675 free(elemType);
10676 }
10677
10678 if (strcmp(typcategory, "U") != 0)
10679 {
10680 appendPQExpBufferStr(q, ",\n CATEGORY = ");
10681 appendStringLiteralAH(q, typcategory, fout);
10682 }
10683
10684 if (strcmp(typispreferred, "t") == 0)
10685 appendPQExpBufferStr(q, ",\n PREFERRED = true");
10686
10687 if (typdelim && strcmp(typdelim, ",") != 0)
10688 {
10689 appendPQExpBufferStr(q, ",\n DELIMITER = ");
10690 appendStringLiteralAH(q, typdelim, fout);
10691 }
10692
10693 if (strcmp(typalign, "c") == 0)
10694 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
10695 else if (strcmp(typalign, "s") == 0)
10696 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
10697 else if (strcmp(typalign, "i") == 0)
10698 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
10699 else if (strcmp(typalign, "d") == 0)
10700 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
10701
10702 if (strcmp(typstorage, "p") == 0)
10703 appendPQExpBufferStr(q, ",\n STORAGE = plain");
10704 else if (strcmp(typstorage, "e") == 0)
10705 appendPQExpBufferStr(q, ",\n STORAGE = external");
10706 else if (strcmp(typstorage, "x") == 0)
10707 appendPQExpBufferStr(q, ",\n STORAGE = extended");
10708 else if (strcmp(typstorage, "m") == 0)
10709 appendPQExpBufferStr(q, ",\n STORAGE = main");
10710
10711 if (strcmp(typbyval, "t") == 0)
10712 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
10713
10714 appendPQExpBufferStr(q, "\n);\n");
10715
10716 if (dopt->binary_upgrade)
10717 binary_upgrade_extension_member(q, &tyinfo->dobj,
10718 "TYPE", qtypname,
10719 tyinfo->dobj.namespace->dobj.name);
10720
10721 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10722 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10723 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10724 .namespace = tyinfo->dobj.namespace->dobj.name,
10725 .owner = tyinfo->rolname,
10726 .description = "TYPE",
10727 .section = SECTION_PRE_DATA,
10728 .createStmt = q->data,
10729 .dropStmt = delq->data));
10730
10731 /* Dump Type Comments and Security Labels */
10732 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10733 dumpComment(fout, "TYPE", qtypname,
10734 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10735 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10736
10737 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10738 dumpSecLabel(fout, "TYPE", qtypname,
10739 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10740 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10741
10742 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10743 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10744 qtypname, NULL,
10745 tyinfo->dobj.namespace->dobj.name,
10746 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10747 tyinfo->inittypacl, tyinfo->initrtypacl);
10748
10749 PQclear(res);
10750 destroyPQExpBuffer(q);
10751 destroyPQExpBuffer(delq);
10752 destroyPQExpBuffer(query);
10753 free(qtypname);
10754 free(qualtypname);
10755}
10756
10757/*
10758 * dumpDomain
10759 * writes out to fout the queries to recreate a user-defined domain
10760 */
10761static void
10762dumpDomain(Archive *fout, TypeInfo *tyinfo)
10763{
10764 DumpOptions *dopt = fout->dopt;
10765 PQExpBuffer q = createPQExpBuffer();
10766 PQExpBuffer delq = createPQExpBuffer();
10767 PQExpBuffer query = createPQExpBuffer();
10768 PGresult *res;
10769 int i;
10770 char *qtypname;
10771 char *qualtypname;
10772 char *typnotnull;
10773 char *typdefn;
10774 char *typdefault;
10775 Oid typcollation;
10776 bool typdefault_is_literal = false;
10777
10778 /* Fetch domain specific details */
10779 if (fout->remoteVersion >= 90100)
10780 {
10781 /* typcollation is new in 9.1 */
10782 appendPQExpBuffer(query, "SELECT t.typnotnull, "
10783 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
10784 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
10785 "t.typdefault, "
10786 "CASE WHEN t.typcollation <> u.typcollation "
10787 "THEN t.typcollation ELSE 0 END AS typcollation "
10788 "FROM pg_catalog.pg_type t "
10789 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
10790 "WHERE t.oid = '%u'::pg_catalog.oid",
10791 tyinfo->dobj.catId.oid);
10792 }
10793 else
10794 {
10795 appendPQExpBuffer(query, "SELECT typnotnull, "
10796 "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
10797 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
10798 "typdefault, 0 AS typcollation "
10799 "FROM pg_catalog.pg_type "
10800 "WHERE oid = '%u'::pg_catalog.oid",
10801 tyinfo->dobj.catId.oid);
10802 }
10803
10804 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10805
10806 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
10807 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
10808 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
10809 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
10810 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
10811 {
10812 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
10813 typdefault_is_literal = true; /* it needs quotes */
10814 }
10815 else
10816 typdefault = NULL;
10817 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
10818
10819 if (dopt->binary_upgrade)
10820 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10821 tyinfo->dobj.catId.oid,
10822 true); /* force array type */
10823
10824 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10825 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10826
10827 appendPQExpBuffer(q,
10828 "CREATE DOMAIN %s AS %s",
10829 qualtypname,
10830 typdefn);
10831
10832 /* Print collation only if different from base type's collation */
10833 if (OidIsValid(typcollation))
10834 {
10835 CollInfo *coll;
10836
10837 coll = findCollationByOid(typcollation);
10838 if (coll)
10839 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
10840 }
10841
10842 if (typnotnull[0] == 't')
10843 appendPQExpBufferStr(q, " NOT NULL");
10844
10845 if (typdefault != NULL)
10846 {
10847 appendPQExpBufferStr(q, " DEFAULT ");
10848 if (typdefault_is_literal)
10849 appendStringLiteralAH(q, typdefault, fout);
10850 else
10851 appendPQExpBufferStr(q, typdefault);
10852 }
10853
10854 PQclear(res);
10855
10856 /*
10857 * Add any CHECK constraints for the domain
10858 */
10859 for (i = 0; i < tyinfo->nDomChecks; i++)
10860 {
10861 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
10862
10863 if (!domcheck->separate)
10864 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
10865 fmtId(domcheck->dobj.name), domcheck->condef);
10866 }
10867
10868 appendPQExpBufferStr(q, ";\n");
10869
10870 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
10871
10872 if (dopt->binary_upgrade)
10873 binary_upgrade_extension_member(q, &tyinfo->dobj,
10874 "DOMAIN", qtypname,
10875 tyinfo->dobj.namespace->dobj.name);
10876
10877 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10878 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10879 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10880 .namespace = tyinfo->dobj.namespace->dobj.name,
10881 .owner = tyinfo->rolname,
10882 .description = "DOMAIN",
10883 .section = SECTION_PRE_DATA,
10884 .createStmt = q->data,
10885 .dropStmt = delq->data));
10886
10887 /* Dump Domain Comments and Security Labels */
10888 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10889 dumpComment(fout, "DOMAIN", qtypname,
10890 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10891 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10892
10893 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10894 dumpSecLabel(fout, "DOMAIN", qtypname,
10895 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10896 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10897
10898 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10899 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10900 qtypname, NULL,
10901 tyinfo->dobj.namespace->dobj.name,
10902 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
10903 tyinfo->inittypacl, tyinfo->initrtypacl);
10904
10905 /* Dump any per-constraint comments */
10906 for (i = 0; i < tyinfo->nDomChecks; i++)
10907 {
10908 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
10909 PQExpBuffer conprefix = createPQExpBuffer();
10910
10911 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
10912 fmtId(domcheck->dobj.name));
10913
10914 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10915 dumpComment(fout, conprefix->data, qtypname,
10916 tyinfo->dobj.namespace->dobj.name,
10917 tyinfo->rolname,
10918 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
10919
10920 destroyPQExpBuffer(conprefix);
10921 }
10922
10923 destroyPQExpBuffer(q);
10924 destroyPQExpBuffer(delq);
10925 destroyPQExpBuffer(query);
10926 free(qtypname);
10927 free(qualtypname);
10928}
10929
10930/*
10931 * dumpCompositeType
10932 * writes out to fout the queries to recreate a user-defined stand-alone
10933 * composite type
10934 */
10935static void
10936dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
10937{
10938 DumpOptions *dopt = fout->dopt;
10939 PQExpBuffer q = createPQExpBuffer();
10940 PQExpBuffer dropped = createPQExpBuffer();
10941 PQExpBuffer delq = createPQExpBuffer();
10942 PQExpBuffer query = createPQExpBuffer();
10943 PGresult *res;
10944 char *qtypname;
10945 char *qualtypname;
10946 int ntups;
10947 int i_attname;
10948 int i_atttypdefn;
10949 int i_attlen;
10950 int i_attalign;
10951 int i_attisdropped;
10952 int i_attcollation;
10953 int i;
10954 int actual_atts;
10955
10956 /* Fetch type specific details */
10957 if (fout->remoteVersion >= 90100)
10958 {
10959 /*
10960 * attcollation is new in 9.1. Since we only want to dump COLLATE
10961 * clauses for attributes whose collation is different from their
10962 * type's default, we use a CASE here to suppress uninteresting
10963 * attcollations cheaply. atttypid will be 0 for dropped columns;
10964 * collation does not matter for those.
10965 */
10966 appendPQExpBuffer(query, "SELECT a.attname, "
10967 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
10968 "a.attlen, a.attalign, a.attisdropped, "
10969 "CASE WHEN a.attcollation <> at.typcollation "
10970 "THEN a.attcollation ELSE 0 END AS attcollation "
10971 "FROM pg_catalog.pg_type ct "
10972 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
10973 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
10974 "WHERE ct.oid = '%u'::pg_catalog.oid "
10975 "ORDER BY a.attnum ",
10976 tyinfo->dobj.catId.oid);
10977 }
10978 else
10979 {
10980 /*
10981 * Since ALTER TYPE could not drop columns until 9.1, attisdropped
10982 * should always be false.
10983 */
10984 appendPQExpBuffer(query, "SELECT a.attname, "
10985 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
10986 "a.attlen, a.attalign, a.attisdropped, "
10987 "0 AS attcollation "
10988 "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a "
10989 "WHERE ct.oid = '%u'::pg_catalog.oid "
10990 "AND a.attrelid = ct.typrelid "
10991 "ORDER BY a.attnum ",
10992 tyinfo->dobj.catId.oid);
10993 }
10994
10995 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10996
10997 ntups = PQntuples(res);
10998
10999 i_attname = PQfnumber(res, "attname");
11000 i_atttypdefn = PQfnumber(res, "atttypdefn");
11001 i_attlen = PQfnumber(res, "attlen");
11002 i_attalign = PQfnumber(res, "attalign");
11003 i_attisdropped = PQfnumber(res, "attisdropped");
11004 i_attcollation = PQfnumber(res, "attcollation");
11005
11006 if (dopt->binary_upgrade)
11007 {
11008 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11009 tyinfo->dobj.catId.oid,
11010 false);
11011 binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
11012 }
11013
11014 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11015 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11016
11017 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11018 qualtypname);
11019
11020 actual_atts = 0;
11021 for (i = 0; i < ntups; i++)
11022 {
11023 char *attname;
11024 char *atttypdefn;
11025 char *attlen;
11026 char *attalign;
11027 bool attisdropped;
11028 Oid attcollation;
11029
11030 attname = PQgetvalue(res, i, i_attname);
11031 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11032 attlen = PQgetvalue(res, i, i_attlen);
11033 attalign = PQgetvalue(res, i, i_attalign);
11034 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11035 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11036
11037 if (attisdropped && !dopt->binary_upgrade)
11038 continue;
11039
11040 /* Format properly if not first attr */
11041 if (actual_atts++ > 0)
11042 appendPQExpBufferChar(q, ',');
11043 appendPQExpBufferStr(q, "\n\t");
11044
11045 if (!attisdropped)
11046 {
11047 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11048
11049 /* Add collation if not default for the column type */
11050 if (OidIsValid(attcollation))
11051 {
11052 CollInfo *coll;
11053
11054 coll = findCollationByOid(attcollation);
11055 if (coll)
11056 appendPQExpBuffer(q, " COLLATE %s",
11057 fmtQualifiedDumpable(coll));
11058 }
11059 }
11060 else
11061 {
11062 /*
11063 * This is a dropped attribute and we're in binary_upgrade mode.
11064 * Insert a placeholder for it in the CREATE TYPE command, and set
11065 * length and alignment with direct UPDATE to the catalogs
11066 * afterwards. See similar code in dumpTableSchema().
11067 */
11068 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11069
11070 /* stash separately for insertion after the CREATE TYPE */
11071 appendPQExpBufferStr(dropped,
11072 "\n-- For binary upgrade, recreate dropped column.\n");
11073 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11074 "SET attlen = %s, "
11075 "attalign = '%s', attbyval = false\n"
11076 "WHERE attname = ", attlen, attalign);
11077 appendStringLiteralAH(dropped, attname, fout);
11078 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11079 appendStringLiteralAH(dropped, qualtypname, fout);
11080 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11081
11082 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11083 qualtypname);
11084 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11085 fmtId(attname));
11086 }
11087 }
11088 appendPQExpBufferStr(q, "\n);\n");
11089 appendPQExpBufferStr(q, dropped->data);
11090
11091 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11092
11093 if (dopt->binary_upgrade)
11094 binary_upgrade_extension_member(q, &tyinfo->dobj,
11095 "TYPE", qtypname,
11096 tyinfo->dobj.namespace->dobj.name);
11097
11098 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11099 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11100 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11101 .namespace = tyinfo->dobj.namespace->dobj.name,
11102 .owner = tyinfo->rolname,
11103 .description = "TYPE",
11104 .section = SECTION_PRE_DATA,
11105 .createStmt = q->data,
11106 .dropStmt = delq->data));
11107
11108
11109 /* Dump Type Comments and Security Labels */
11110 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11111 dumpComment(fout, "TYPE", qtypname,
11112 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11113 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11114
11115 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11116 dumpSecLabel(fout, "TYPE", qtypname,
11117 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11118 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11119
11120 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11121 dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
11122 qtypname, NULL,
11123 tyinfo->dobj.namespace->dobj.name,
11124 tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
11125 tyinfo->inittypacl, tyinfo->initrtypacl);
11126
11127 PQclear(res);
11128 destroyPQExpBuffer(q);
11129 destroyPQExpBuffer(dropped);
11130 destroyPQExpBuffer(delq);
11131 destroyPQExpBuffer(query);
11132 free(qtypname);
11133 free(qualtypname);
11134
11135 /* Dump any per-column comments */
11136 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11137 dumpCompositeTypeColComments(fout, tyinfo);
11138}
11139
11140/*
11141 * dumpCompositeTypeColComments
11142 * writes out to fout the queries to recreate comments on the columns of
11143 * a user-defined stand-alone composite type
11144 */
11145static void
11146dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
11147{
11148 CommentItem *comments;
11149 int ncomments;
11150 PGresult *res;
11151 PQExpBuffer query;
11152 PQExpBuffer target;
11153 Oid pgClassOid;
11154 int i;
11155 int ntups;
11156 int i_attname;
11157 int i_attnum;
11158
11159 /* do nothing, if --no-comments is supplied */
11160 if (fout->dopt->no_comments)
11161 return;
11162
11163 query = createPQExpBuffer();
11164
11165 appendPQExpBuffer(query,
11166 "SELECT c.tableoid, a.attname, a.attnum "
11167 "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a "
11168 "WHERE c.oid = '%u' AND c.oid = a.attrelid "
11169 " AND NOT a.attisdropped "
11170 "ORDER BY a.attnum ",
11171 tyinfo->typrelid);
11172
11173 /* Fetch column attnames */
11174 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11175
11176 ntups = PQntuples(res);
11177 if (ntups < 1)
11178 {
11179 PQclear(res);
11180 destroyPQExpBuffer(query);
11181 return;
11182 }
11183
11184 pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid")));
11185
11186 /* Search for comments associated with type's pg_class OID */
11187 ncomments = findComments(fout,
11188 pgClassOid,
11189 tyinfo->typrelid,
11190 &comments);
11191
11192 /* If no comments exist, we're done */
11193 if (ncomments <= 0)
11194 {
11195 PQclear(res);
11196 destroyPQExpBuffer(query);
11197 return;
11198 }
11199
11200 /* Build COMMENT ON statements */
11201 target = createPQExpBuffer();
11202
11203 i_attnum = PQfnumber(res, "attnum");
11204 i_attname = PQfnumber(res, "attname");
11205 while (ncomments > 0)
11206 {
11207 const char *attname;
11208
11209 attname = NULL;
11210 for (i = 0; i < ntups; i++)
11211 {
11212 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid)
11213 {
11214 attname = PQgetvalue(res, i, i_attname);
11215 break;
11216 }
11217 }
11218 if (attname) /* just in case we don't find it */
11219 {
11220 const char *descr = comments->descr;
11221
11222 resetPQExpBuffer(target);
11223 appendPQExpBuffer(target, "COLUMN %s.",
11224 fmtId(tyinfo->dobj.name));
11225 appendPQExpBufferStr(target, fmtId(attname));
11226
11227 resetPQExpBuffer(query);
11228 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11229 fmtQualifiedDumpable(tyinfo));
11230 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
11231 appendStringLiteralAH(query, descr, fout);
11232 appendPQExpBufferStr(query, ";\n");
11233
11234 ArchiveEntry(fout, nilCatalogId, createDumpId(),
11235 ARCHIVE_OPTS(.tag = target->data,
11236 .namespace = tyinfo->dobj.namespace->dobj.name,
11237 .owner = tyinfo->rolname,
11238 .description = "COMMENT",
11239 .section = SECTION_NONE,
11240 .createStmt = query->data,
11241 .deps = &(tyinfo->dobj.dumpId),
11242 .nDeps = 1));
11243 }
11244
11245 comments++;
11246 ncomments--;
11247 }
11248
11249 PQclear(res);
11250 destroyPQExpBuffer(query);
11251 destroyPQExpBuffer(target);
11252}
11253
11254/*
11255 * dumpShellType
11256 * writes out to fout the queries to create a shell type
11257 *
11258 * We dump a shell definition in advance of the I/O functions for the type.
11259 */
11260static void
11261dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
11262{
11263 DumpOptions *dopt = fout->dopt;
11264 PQExpBuffer q;
11265
11266 /* Skip if not to be dumped */
11267 if (!stinfo->dobj.dump || dopt->dataOnly)
11268 return;
11269
11270 q = createPQExpBuffer();
11271
11272 /*
11273 * Note the lack of a DROP command for the shell type; any required DROP
11274 * is driven off the base type entry, instead. This interacts with
11275 * _printTocEntry()'s use of the presence of a DROP command to decide
11276 * whether an entry needs an ALTER OWNER command. We don't want to alter
11277 * the shell type's owner immediately on creation; that should happen only
11278 * after it's filled in, otherwise the backend complains.
11279 */
11280
11281 if (dopt->binary_upgrade)
11282 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11283 stinfo->baseType->dobj.catId.oid,
11284 false);
11285
11286 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11287 fmtQualifiedDumpable(stinfo));
11288
11289 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11290 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
11291 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
11292 .namespace = stinfo->dobj.namespace->dobj.name,
11293 .owner = stinfo->baseType->rolname,
11294 .description = "SHELL TYPE",
11295 .section = SECTION_PRE_DATA,
11296 .createStmt = q->data));
11297
11298 destroyPQExpBuffer(q);
11299}
11300
11301/*
11302 * dumpProcLang
11303 * writes out to fout the queries to recreate a user-defined
11304 * procedural language
11305 */
11306static void
11307dumpProcLang(Archive *fout, ProcLangInfo *plang)
11308{
11309 DumpOptions *dopt = fout->dopt;
11310 PQExpBuffer defqry;
11311 PQExpBuffer delqry;
11312 bool useParams;
11313 char *qlanname;
11314 FuncInfo *funcInfo;
11315 FuncInfo *inlineInfo = NULL;
11316 FuncInfo *validatorInfo = NULL;
11317
11318 /* Skip if not to be dumped */
11319 if (!plang->dobj.dump || dopt->dataOnly)
11320 return;
11321
11322 /*
11323 * Try to find the support function(s). It is not an error if we don't
11324 * find them --- if the functions are in the pg_catalog schema, as is
11325 * standard in 8.1 and up, then we won't have loaded them. (In this case
11326 * we will emit a parameterless CREATE LANGUAGE command, which will
11327 * require PL template knowledge in the backend to reload.)
11328 */
11329
11330 funcInfo = findFuncByOid(plang->lanplcallfoid);
11331 if (funcInfo != NULL && !funcInfo->dobj.dump)
11332 funcInfo = NULL; /* treat not-dumped same as not-found */
11333
11334 if (OidIsValid(plang->laninline))
11335 {
11336 inlineInfo = findFuncByOid(plang->laninline);
11337 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
11338 inlineInfo = NULL;
11339 }
11340
11341 if (OidIsValid(plang->lanvalidator))
11342 {
11343 validatorInfo = findFuncByOid(plang->lanvalidator);
11344 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
11345 validatorInfo = NULL;
11346 }
11347
11348 /*
11349 * If the functions are dumpable then emit a traditional CREATE LANGUAGE
11350 * with parameters. Otherwise, we'll write a parameterless command, which
11351 * will rely on data from pg_pltemplate.
11352 */
11353 useParams = (funcInfo != NULL &&
11354 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
11355 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
11356
11357 defqry = createPQExpBuffer();
11358 delqry = createPQExpBuffer();
11359
11360 qlanname = pg_strdup(fmtId(plang->dobj.name));
11361
11362 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
11363 qlanname);
11364
11365 if (useParams)
11366 {
11367 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
11368 plang->lanpltrusted ? "TRUSTED " : "",
11369 qlanname);
11370 appendPQExpBuffer(defqry, " HANDLER %s",
11371 fmtQualifiedDumpable(funcInfo));
11372 if (OidIsValid(plang->laninline))
11373 appendPQExpBuffer(defqry, " INLINE %s",
11374 fmtQualifiedDumpable(inlineInfo));
11375 if (OidIsValid(plang->lanvalidator))
11376 appendPQExpBuffer(defqry, " VALIDATOR %s",
11377 fmtQualifiedDumpable(validatorInfo));
11378 }
11379 else
11380 {
11381 /*
11382 * If not dumping parameters, then use CREATE OR REPLACE so that the
11383 * command will not fail if the language is preinstalled in the target
11384 * database. We restrict the use of REPLACE to this case so as to
11385 * eliminate the risk of replacing a language with incompatible
11386 * parameter settings: this command will only succeed at all if there
11387 * is a pg_pltemplate entry, and if there is one, the existing entry
11388 * must match it too.
11389 */
11390 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
11391 qlanname);
11392 }
11393 appendPQExpBufferStr(defqry, ";\n");
11394
11395 if (dopt->binary_upgrade)
11396 binary_upgrade_extension_member(defqry, &plang->dobj,
11397 "LANGUAGE", qlanname, NULL);
11398
11399 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
11400 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
11401 ARCHIVE_OPTS(.tag = plang->dobj.name,
11402 .owner = plang->lanowner,
11403 .description = "PROCEDURAL LANGUAGE",
11404 .section = SECTION_PRE_DATA,
11405 .createStmt = defqry->data,
11406 .dropStmt = delqry->data,
11407 ));
11408
11409 /* Dump Proc Lang Comments and Security Labels */
11410 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
11411 dumpComment(fout, "LANGUAGE", qlanname,
11412 NULL, plang->lanowner,
11413 plang->dobj.catId, 0, plang->dobj.dumpId);
11414
11415 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
11416 dumpSecLabel(fout, "LANGUAGE", qlanname,
11417 NULL, plang->lanowner,
11418 plang->dobj.catId, 0, plang->dobj.dumpId);
11419
11420 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
11421 dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
11422 qlanname, NULL, NULL,
11423 plang->lanowner, plang->lanacl, plang->rlanacl,
11424 plang->initlanacl, plang->initrlanacl);
11425
11426 free(qlanname);
11427
11428 destroyPQExpBuffer(defqry);
11429 destroyPQExpBuffer(delqry);
11430}
11431
11432/*
11433 * format_function_arguments: generate function name and argument list
11434 *
11435 * This is used when we can rely on pg_get_function_arguments to format
11436 * the argument list. Note, however, that pg_get_function_arguments
11437 * does not special-case zero-argument aggregates.
11438 */
11439static char *
11440format_function_arguments(FuncInfo *finfo, char *funcargs, bool is_agg)
11441{
11442 PQExpBufferData fn;
11443
11444 initPQExpBuffer(&fn);
11445 appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
11446 if (is_agg && finfo->nargs == 0)
11447 appendPQExpBufferStr(&fn, "(*)");
11448 else
11449 appendPQExpBuffer(&fn, "(%s)", funcargs);
11450 return fn.data;
11451}
11452
11453/*
11454 * format_function_arguments_old: generate function name and argument list
11455 *
11456 * The argument type names are qualified if needed. The function name
11457 * is never qualified.
11458 *
11459 * This is used only with pre-8.4 servers, so we aren't expecting to see
11460 * VARIADIC or TABLE arguments, nor are there any defaults for arguments.
11461 *
11462 * Any or all of allargtypes, argmodes, argnames may be NULL.
11463 */
11464static char *
11465format_function_arguments_old(Archive *fout,
11466 FuncInfo *finfo, int nallargs,
11467 char **allargtypes,
11468 char **argmodes,
11469 char **argnames)
11470{
11471 PQExpBufferData fn;
11472 int j;
11473
11474 initPQExpBuffer(&fn);
11475 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
11476 for (j = 0; j < nallargs; j++)
11477 {
11478 Oid typid;
11479 char *typname;
11480 const char *argmode;
11481 const char *argname;
11482
11483 typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
11484 typname = getFormattedTypeName(fout, typid, zeroAsOpaque);
11485
11486 if (argmodes)
11487 {
11488 switch (argmodes[j][0])
11489 {
11490 case PROARGMODE_IN:
11491 argmode = "";
11492 break;
11493 case PROARGMODE_OUT:
11494 argmode = "OUT ";
11495 break;
11496 case PROARGMODE_INOUT:
11497 argmode = "INOUT ";
11498 break;
11499 default:
11500 pg_log_warning("bogus value in proargmodes array");
11501 argmode = "";
11502 break;
11503 }
11504 }
11505 else
11506 argmode = "";
11507
11508 argname = argnames ? argnames[j] : (char *) NULL;
11509 if (argname && argname[0] == '\0')
11510 argname = NULL;
11511
11512 appendPQExpBuffer(&fn, "%s%s%s%s%s",
11513 (j > 0) ? ", " : "",
11514 argmode,
11515 argname ? fmtId(argname) : "",
11516 argname ? " " : "",
11517 typname);
11518 free(typname);
11519 }
11520 appendPQExpBufferChar(&fn, ')');
11521 return fn.data;
11522}
11523
11524/*
11525 * format_function_signature: generate function name and argument list
11526 *
11527 * This is like format_function_arguments_old except that only a minimal
11528 * list of input argument types is generated; this is sufficient to
11529 * reference the function, but not to define it.
11530 *
11531 * If honor_quotes is false then the function name is never quoted.
11532 * This is appropriate for use in TOC tags, but not in SQL commands.
11533 */
11534static char *
11535format_function_signature(Archive *fout, FuncInfo *finfo, bool honor_quotes)
11536{
11537 PQExpBufferData fn;
11538 int j;
11539
11540 initPQExpBuffer(&fn);
11541 if (honor_quotes)
11542 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
11543 else
11544 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
11545 for (j = 0; j < finfo->nargs; j++)
11546 {
11547 char *typname;
11548
11549 if (j > 0)
11550 appendPQExpBufferStr(&fn, ", ");
11551
11552 typname = getFormattedTypeName(fout, finfo->argtypes[j],
11553 zeroAsOpaque);
11554 appendPQExpBufferStr(&fn, typname);
11555 free(typname);
11556 }
11557 appendPQExpBufferChar(&fn, ')');
11558 return fn.data;
11559}
11560
11561
11562/*
11563 * dumpFunc:
11564 * dump out one function
11565 */
11566static void
11567dumpFunc(Archive *fout, FuncInfo *finfo)
11568{
11569 DumpOptions *dopt = fout->dopt;
11570 PQExpBuffer query;
11571 PQExpBuffer q;
11572 PQExpBuffer delqry;
11573 PQExpBuffer asPart;
11574 PGresult *res;
11575 char *funcsig; /* identity signature */
11576 char *funcfullsig = NULL; /* full signature */
11577 char *funcsig_tag;
11578 char *proretset;
11579 char *prosrc;
11580 char *probin;
11581 char *funcargs;
11582 char *funciargs;
11583 char *funcresult;
11584 char *proallargtypes;
11585 char *proargmodes;
11586 char *proargnames;
11587 char *protrftypes;
11588 char *prokind;
11589 char *provolatile;
11590 char *proisstrict;
11591 char *prosecdef;
11592 char *proleakproof;
11593 char *proconfig;
11594 char *procost;
11595 char *prorows;
11596 char *prosupport;
11597 char *proparallel;
11598 char *lanname;
11599 char *rettypename;
11600 int nallargs;
11601 char **allargtypes = NULL;
11602 char **argmodes = NULL;
11603 char **argnames = NULL;
11604 char **configitems = NULL;
11605 int nconfigitems = 0;
11606 const char *keyword;
11607 int i;
11608
11609 /* Skip if not to be dumped */
11610 if (!finfo->dobj.dump || dopt->dataOnly)
11611 return;
11612
11613 query = createPQExpBuffer();
11614 q = createPQExpBuffer();
11615 delqry = createPQExpBuffer();
11616 asPart = createPQExpBuffer();
11617
11618 /* Fetch function-specific details */
11619 if (fout->remoteVersion >= 120000)
11620 {
11621 /*
11622 * prosupport was added in 12
11623 */
11624 appendPQExpBuffer(query,
11625 "SELECT proretset, prosrc, probin, "
11626 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11627 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11628 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11629 "array_to_string(protrftypes, ' ') AS protrftypes, "
11630 "prokind, provolatile, proisstrict, prosecdef, "
11631 "proleakproof, proconfig, procost, prorows, "
11632 "prosupport, proparallel, "
11633 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11634 "FROM pg_catalog.pg_proc "
11635 "WHERE oid = '%u'::pg_catalog.oid",
11636 finfo->dobj.catId.oid);
11637 }
11638 else if (fout->remoteVersion >= 110000)
11639 {
11640 /*
11641 * prokind was added in 11
11642 */
11643 appendPQExpBuffer(query,
11644 "SELECT proretset, prosrc, probin, "
11645 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11646 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11647 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11648 "array_to_string(protrftypes, ' ') AS protrftypes, "
11649 "prokind, provolatile, proisstrict, prosecdef, "
11650 "proleakproof, proconfig, procost, prorows, "
11651 "'-' AS prosupport, proparallel, "
11652 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11653 "FROM pg_catalog.pg_proc "
11654 "WHERE oid = '%u'::pg_catalog.oid",
11655 finfo->dobj.catId.oid);
11656 }
11657 else if (fout->remoteVersion >= 90600)
11658 {
11659 /*
11660 * proparallel was added in 9.6
11661 */
11662 appendPQExpBuffer(query,
11663 "SELECT proretset, prosrc, probin, "
11664 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11665 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11666 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11667 "array_to_string(protrftypes, ' ') AS protrftypes, "
11668 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
11669 "provolatile, proisstrict, prosecdef, "
11670 "proleakproof, proconfig, procost, prorows, "
11671 "'-' AS prosupport, proparallel, "
11672 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11673 "FROM pg_catalog.pg_proc "
11674 "WHERE oid = '%u'::pg_catalog.oid",
11675 finfo->dobj.catId.oid);
11676 }
11677 else if (fout->remoteVersion >= 90500)
11678 {
11679 /*
11680 * protrftypes was added in 9.5
11681 */
11682 appendPQExpBuffer(query,
11683 "SELECT proretset, prosrc, probin, "
11684 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11685 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11686 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11687 "array_to_string(protrftypes, ' ') AS protrftypes, "
11688 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
11689 "provolatile, proisstrict, prosecdef, "
11690 "proleakproof, proconfig, procost, prorows, "
11691 "'-' AS prosupport, "
11692 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11693 "FROM pg_catalog.pg_proc "
11694 "WHERE oid = '%u'::pg_catalog.oid",
11695 finfo->dobj.catId.oid);
11696 }
11697 else if (fout->remoteVersion >= 90200)
11698 {
11699 /*
11700 * proleakproof was added in 9.2
11701 */
11702 appendPQExpBuffer(query,
11703 "SELECT proretset, prosrc, probin, "
11704 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11705 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11706 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11707 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
11708 "provolatile, proisstrict, prosecdef, "
11709 "proleakproof, proconfig, procost, prorows, "
11710 "'-' AS prosupport, "
11711 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11712 "FROM pg_catalog.pg_proc "
11713 "WHERE oid = '%u'::pg_catalog.oid",
11714 finfo->dobj.catId.oid);
11715 }
11716 else if (fout->remoteVersion >= 80400)
11717 {
11718 /*
11719 * In 8.4 and up we rely on pg_get_function_arguments and
11720 * pg_get_function_result instead of examining proallargtypes etc.
11721 */
11722 appendPQExpBuffer(query,
11723 "SELECT proretset, prosrc, probin, "
11724 "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
11725 "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
11726 "pg_catalog.pg_get_function_result(oid) AS funcresult, "
11727 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
11728 "provolatile, proisstrict, prosecdef, "
11729 "false AS proleakproof, "
11730 " proconfig, procost, prorows, "
11731 "'-' AS prosupport, "
11732 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11733 "FROM pg_catalog.pg_proc "
11734 "WHERE oid = '%u'::pg_catalog.oid",
11735 finfo->dobj.catId.oid);
11736 }
11737 else if (fout->remoteVersion >= 80300)
11738 {
11739 appendPQExpBuffer(query,
11740 "SELECT proretset, prosrc, probin, "
11741 "proallargtypes, proargmodes, proargnames, "
11742 "'f' AS prokind, "
11743 "provolatile, proisstrict, prosecdef, "
11744 "false AS proleakproof, "
11745 "proconfig, procost, prorows, "
11746 "'-' AS prosupport, "
11747 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11748 "FROM pg_catalog.pg_proc "
11749 "WHERE oid = '%u'::pg_catalog.oid",
11750 finfo->dobj.catId.oid);
11751 }
11752 else if (fout->remoteVersion >= 80100)
11753 {
11754 appendPQExpBuffer(query,
11755 "SELECT proretset, prosrc, probin, "
11756 "proallargtypes, proargmodes, proargnames, "
11757 "'f' AS prokind, "
11758 "provolatile, proisstrict, prosecdef, "
11759 "false AS proleakproof, "
11760 "null AS proconfig, 0 AS procost, 0 AS prorows, "
11761 "'-' AS prosupport, "
11762 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11763 "FROM pg_catalog.pg_proc "
11764 "WHERE oid = '%u'::pg_catalog.oid",
11765 finfo->dobj.catId.oid);
11766 }
11767 else
11768 {
11769 appendPQExpBuffer(query,
11770 "SELECT proretset, prosrc, probin, "
11771 "null AS proallargtypes, "
11772 "null AS proargmodes, "
11773 "proargnames, "
11774 "'f' AS prokind, "
11775 "provolatile, proisstrict, prosecdef, "
11776 "false AS proleakproof, "
11777 "null AS proconfig, 0 AS procost, 0 AS prorows, "
11778 "'-' AS prosupport, "
11779 "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
11780 "FROM pg_catalog.pg_proc "
11781 "WHERE oid = '%u'::pg_catalog.oid",
11782 finfo->dobj.catId.oid);
11783 }
11784
11785 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11786
11787 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
11788 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
11789 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
11790 if (fout->remoteVersion >= 80400)
11791 {
11792 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
11793 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
11794 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
11795 proallargtypes = proargmodes = proargnames = NULL;
11796 }
11797 else
11798 {
11799 proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
11800 proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
11801 proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
11802 funcargs = funciargs = funcresult = NULL;
11803 }
11804 if (PQfnumber(res, "protrftypes") != -1)
11805 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
11806 else
11807 protrftypes = NULL;
11808 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
11809 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
11810 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
11811 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
11812 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
11813 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
11814 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
11815 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
11816 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
11817
11818 if (PQfnumber(res, "proparallel") != -1)
11819 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
11820 else
11821 proparallel = NULL;
11822
11823 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
11824
11825 /*
11826 * See backend/commands/functioncmds.c for details of how the 'AS' clause
11827 * is used. In 8.4 and up, an unused probin is NULL (here ""); previous
11828 * versions would set it to "-". There are no known cases in which prosrc
11829 * is unused, so the tests below for "-" are probably useless.
11830 */
11831 if (probin[0] != '\0' && strcmp(probin, "-") != 0)
11832 {
11833 appendPQExpBufferStr(asPart, "AS ");
11834 appendStringLiteralAH(asPart, probin, fout);
11835 if (strcmp(prosrc, "-") != 0)
11836 {
11837 appendPQExpBufferStr(asPart, ", ");
11838
11839 /*
11840 * where we have bin, use dollar quoting if allowed and src
11841 * contains quote or backslash; else use regular quoting.
11842 */
11843 if (dopt->disable_dollar_quoting ||
11844 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
11845 appendStringLiteralAH(asPart, prosrc, fout);
11846 else
11847 appendStringLiteralDQ(asPart, prosrc, NULL);
11848 }
11849 }
11850 else
11851 {
11852 if (strcmp(prosrc, "-") != 0)
11853 {
11854 appendPQExpBufferStr(asPart, "AS ");
11855 /* with no bin, dollar quote src unconditionally if allowed */
11856 if (dopt->disable_dollar_quoting)
11857 appendStringLiteralAH(asPart, prosrc, fout);
11858 else
11859 appendStringLiteralDQ(asPart, prosrc, NULL);
11860 }
11861 }
11862
11863 nallargs = finfo->nargs; /* unless we learn different from allargs */
11864
11865 if (proallargtypes && *proallargtypes)
11866 {
11867 int nitems = 0;
11868
11869 if (!parsePGArray(proallargtypes, &allargtypes, &nitems) ||
11870 nitems < finfo->nargs)
11871 {
11872 pg_log_warning("could not parse proallargtypes array");
11873 if (allargtypes)
11874 free(allargtypes);
11875 allargtypes = NULL;
11876 }
11877 else
11878 nallargs = nitems;
11879 }
11880
11881 if (proargmodes && *proargmodes)
11882 {
11883 int nitems = 0;
11884
11885 if (!parsePGArray(proargmodes, &argmodes, &nitems) ||
11886 nitems != nallargs)
11887 {
11888 pg_log_warning("could not parse proargmodes array");
11889 if (argmodes)
11890 free(argmodes);
11891 argmodes = NULL;
11892 }
11893 }
11894
11895 if (proargnames && *proargnames)
11896 {
11897 int nitems = 0;
11898
11899 if (!parsePGArray(proargnames, &argnames, &nitems) ||
11900 nitems != nallargs)
11901 {
11902 pg_log_warning("could not parse proargnames array");
11903 if (argnames)
11904 free(argnames);
11905 argnames = NULL;
11906 }
11907 }
11908
11909 if (proconfig && *proconfig)
11910 {
11911 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
11912 {
11913 pg_log_warning("could not parse proconfig array");
11914 if (configitems)
11915 free(configitems);
11916 configitems = NULL;
11917 nconfigitems = 0;
11918 }
11919 }
11920
11921 if (funcargs)
11922 {
11923 /* 8.4 or later; we rely on server-side code for most of the work */
11924 funcfullsig = format_function_arguments(finfo, funcargs, false);
11925 funcsig = format_function_arguments(finfo, funciargs, false);
11926 }
11927 else
11928 /* pre-8.4, do it ourselves */
11929 funcsig = format_function_arguments_old(fout,
11930 finfo, nallargs, allargtypes,
11931 argmodes, argnames);
11932
11933 funcsig_tag = format_function_signature(fout, finfo, false);
11934
11935 if (prokind[0] == PROKIND_PROCEDURE)
11936 keyword = "PROCEDURE";
11937 else
11938 keyword = "FUNCTION"; /* works for window functions too */
11939
11940 appendPQExpBuffer(delqry, "DROP %s %s.%s;\n",
11941 keyword,
11942 fmtId(finfo->dobj.namespace->dobj.name),
11943 funcsig);
11944
11945 appendPQExpBuffer(q, "CREATE %s %s.%s",
11946 keyword,
11947 fmtId(finfo->dobj.namespace->dobj.name),
11948 funcfullsig ? funcfullsig :
11949 funcsig);
11950
11951 if (prokind[0] == PROKIND_PROCEDURE)
11952 /* no result type to output */ ;
11953 else if (funcresult)
11954 appendPQExpBuffer(q, " RETURNS %s", funcresult);
11955 else
11956 {
11957 rettypename = getFormattedTypeName(fout, finfo->prorettype,
11958 zeroAsOpaque);
11959 appendPQExpBuffer(q, " RETURNS %s%s",
11960 (proretset[0] == 't') ? "SETOF " : "",
11961 rettypename);
11962 free(rettypename);
11963 }
11964
11965 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
11966
11967 if (protrftypes != NULL && strcmp(protrftypes, "") != 0)
11968 {
11969 Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
11970 int i;
11971
11972 appendPQExpBufferStr(q, " TRANSFORM ");
11973 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
11974 for (i = 0; typeids[i]; i++)
11975 {
11976 if (i != 0)
11977 appendPQExpBufferStr(q, ", ");
11978 appendPQExpBuffer(q, "FOR TYPE %s",
11979 getFormattedTypeName(fout, typeids[i], zeroAsNone));
11980 }
11981 }
11982
11983 if (prokind[0] == PROKIND_WINDOW)
11984 appendPQExpBufferStr(q, " WINDOW");
11985
11986 if (provolatile[0] != PROVOLATILE_VOLATILE)
11987 {
11988 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
11989 appendPQExpBufferStr(q, " IMMUTABLE");
11990 else if (provolatile[0] == PROVOLATILE_STABLE)
11991 appendPQExpBufferStr(q, " STABLE");
11992 else if (provolatile[0] != PROVOLATILE_VOLATILE)
11993 fatal("unrecognized provolatile value for function \"%s\"",
11994 finfo->dobj.name);
11995 }
11996
11997 if (proisstrict[0] == 't')
11998 appendPQExpBufferStr(q, " STRICT");
11999
12000 if (prosecdef[0] == 't')
12001 appendPQExpBufferStr(q, " SECURITY DEFINER");
12002
12003 if (proleakproof[0] == 't')
12004 appendPQExpBufferStr(q, " LEAKPROOF");
12005
12006 /*
12007 * COST and ROWS are emitted only if present and not default, so as not to
12008 * break backwards-compatibility of the dump without need. Keep this code
12009 * in sync with the defaults in functioncmds.c.
12010 */
12011 if (strcmp(procost, "0") != 0)
12012 {
12013 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12014 {
12015 /* default cost is 1 */
12016 if (strcmp(procost, "1") != 0)
12017 appendPQExpBuffer(q, " COST %s", procost);
12018 }
12019 else
12020 {
12021 /* default cost is 100 */
12022 if (strcmp(procost, "100") != 0)
12023 appendPQExpBuffer(q, " COST %s", procost);
12024 }
12025 }
12026 if (proretset[0] == 't' &&
12027 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12028 appendPQExpBuffer(q, " ROWS %s", prorows);
12029
12030 if (strcmp(prosupport, "-") != 0)
12031 {
12032 /* We rely on regprocout to provide quoting and qualification */
12033 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12034 }
12035
12036 if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE)
12037 {
12038 if (proparallel[0] == PROPARALLEL_SAFE)
12039 appendPQExpBufferStr(q, " PARALLEL SAFE");
12040 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12041 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12042 else if (proparallel[0] != PROPARALLEL_UNSAFE)
12043 fatal("unrecognized proparallel value for function \"%s\"",
12044 finfo->dobj.name);
12045 }
12046
12047 for (i = 0; i < nconfigitems; i++)
12048 {
12049 /* we feel free to scribble on configitems[] here */
12050 char *configitem = configitems[i];
12051 char *pos;
12052
12053 pos = strchr(configitem, '=');
12054 if (pos == NULL)
12055 continue;
12056 *pos++ = '\0';
12057 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12058
12059 /*
12060 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12061 * by flatten_set_variable_args() before they were put into the
12062 * proconfig array. However, because the quoting rules used there
12063 * aren't exactly like SQL's, we have to break the list value apart
12064 * and then quote the elements as string literals. (The elements may
12065 * be double-quoted as-is, but we can't just feed them to the SQL
12066 * parser; it would do the wrong thing with elements that are
12067 * zero-length or longer than NAMEDATALEN.)
12068 *
12069 * Variables that are not so marked should just be emitted as simple
12070 * string literals. If the variable is not known to
12071 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12072 * to use GUC_LIST_QUOTE for extension variables.
12073 */
12074 if (variable_is_guc_list_quote(configitem))
12075 {
12076 char **namelist;
12077 char **nameptr;
12078
12079 /* Parse string into list of identifiers */
12080 /* this shouldn't fail really */
12081 if (SplitGUCList(pos, ',', &namelist))
12082 {
12083 for (nameptr = namelist; *nameptr; nameptr++)
12084 {
12085 if (nameptr != namelist)
12086 appendPQExpBufferStr(q, ", ");
12087 appendStringLiteralAH(q, *nameptr, fout);
12088 }
12089 }
12090 pg_free(namelist);
12091 }
12092 else
12093 appendStringLiteralAH(q, pos, fout);
12094 }
12095
12096 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12097
12098 if (dopt->binary_upgrade)
12099 binary_upgrade_extension_member(q, &finfo->dobj,
12100 keyword, funcsig,
12101 finfo->dobj.namespace->dobj.name);
12102
12103 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12104 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12105 ARCHIVE_OPTS(.tag = funcsig_tag,
12106 .namespace = finfo->dobj.namespace->dobj.name,
12107 .owner = finfo->rolname,
12108 .description = keyword,
12109 .section = SECTION_PRE_DATA,
12110 .createStmt = q->data,
12111 .dropStmt = delqry->data));
12112
12113 /* Dump Function Comments and Security Labels */
12114 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12115 dumpComment(fout, keyword, funcsig,
12116 finfo->dobj.namespace->dobj.name, finfo->rolname,
12117 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12118
12119 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12120 dumpSecLabel(fout, keyword, funcsig,
12121 finfo->dobj.namespace->dobj.name, finfo->rolname,
12122 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12123
12124 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12125 dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, keyword,
12126 funcsig, NULL,
12127 finfo->dobj.namespace->dobj.name,
12128 finfo->rolname, finfo->proacl, finfo->rproacl,
12129 finfo->initproacl, finfo->initrproacl);
12130
12131 PQclear(res);
12132
12133 destroyPQExpBuffer(query);
12134 destroyPQExpBuffer(q);
12135 destroyPQExpBuffer(delqry);
12136 destroyPQExpBuffer(asPart);
12137 free(funcsig);
12138 if (funcfullsig)
12139 free(funcfullsig);
12140 free(funcsig_tag);
12141 if (allargtypes)
12142 free(allargtypes);
12143 if (argmodes)
12144 free(argmodes);
12145 if (argnames)
12146 free(argnames);
12147 if (configitems)
12148 free(configitems);
12149}
12150
12151
12152/*
12153 * Dump a user-defined cast
12154 */
12155static void
12156dumpCast(Archive *fout, CastInfo *cast)
12157{
12158 DumpOptions *dopt = fout->dopt;
12159 PQExpBuffer defqry;
12160 PQExpBuffer delqry;
12161 PQExpBuffer labelq;
12162 PQExpBuffer castargs;
12163 FuncInfo *funcInfo = NULL;
12164 char *sourceType;
12165 char *targetType;
12166
12167 /* Skip if not to be dumped */
12168 if (!cast->dobj.dump || dopt->dataOnly)
12169 return;
12170
12171 /* Cannot dump if we don't have the cast function's info */
12172 if (OidIsValid(cast->castfunc))
12173 {
12174 funcInfo = findFuncByOid(cast->castfunc);
12175 if (funcInfo == NULL)
12176 fatal("could not find function definition for function with OID %u",
12177 cast->castfunc);
12178 }
12179
12180 defqry = createPQExpBuffer();
12181 delqry = createPQExpBuffer();
12182 labelq = createPQExpBuffer();
12183 castargs = createPQExpBuffer();
12184
12185 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12186 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12187 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12188 sourceType, targetType);
12189
12190 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12191 sourceType, targetType);
12192
12193 switch (cast->castmethod)
12194 {
12195 case COERCION_METHOD_BINARY:
12196 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12197 break;
12198 case COERCION_METHOD_INOUT:
12199 appendPQExpBufferStr(defqry, "WITH INOUT");
12200 break;
12201 case COERCION_METHOD_FUNCTION:
12202 if (funcInfo)
12203 {
12204 char *fsig = format_function_signature(fout, funcInfo, true);
12205
12206 /*
12207 * Always qualify the function name (format_function_signature
12208 * won't qualify it).
12209 */
12210 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12211 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12212 free(fsig);
12213 }
12214 else
12215 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12216 break;
12217 default:
12218 pg_log_warning("bogus value in pg_cast.castmethod field");
12219 }
12220
12221 if (cast->castcontext == 'a')
12222 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12223 else if (cast->castcontext == 'i')
12224 appendPQExpBufferStr(defqry, " AS IMPLICIT");
12225 appendPQExpBufferStr(defqry, ";\n");
12226
12227 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12228 sourceType, targetType);
12229
12230 appendPQExpBuffer(castargs, "(%s AS %s)",
12231 sourceType, targetType);
12232
12233 if (dopt->binary_upgrade)
12234 binary_upgrade_extension_member(defqry, &cast->dobj,
12235 "CAST", castargs->data, NULL);
12236
12237 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12238 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12239 ARCHIVE_OPTS(.tag = labelq->data,
12240 .description = "CAST",
12241 .section = SECTION_PRE_DATA,
12242 .createStmt = defqry->data,
12243 .dropStmt = delqry->data));
12244
12245 /* Dump Cast Comments */
12246 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12247 dumpComment(fout, "CAST", castargs->data,
12248 NULL, "",
12249 cast->dobj.catId, 0, cast->dobj.dumpId);
12250
12251 free(sourceType);
12252 free(targetType);
12253
12254 destroyPQExpBuffer(defqry);
12255 destroyPQExpBuffer(delqry);
12256 destroyPQExpBuffer(labelq);
12257 destroyPQExpBuffer(castargs);
12258}
12259
12260/*
12261 * Dump a transform
12262 */
12263static void
12264dumpTransform(Archive *fout, TransformInfo *transform)
12265{
12266 DumpOptions *dopt = fout->dopt;
12267 PQExpBuffer defqry;
12268 PQExpBuffer delqry;
12269 PQExpBuffer labelq;
12270 PQExpBuffer transformargs;
12271 FuncInfo *fromsqlFuncInfo = NULL;
12272 FuncInfo *tosqlFuncInfo = NULL;
12273 char *lanname;
12274 char *transformType;
12275
12276 /* Skip if not to be dumped */
12277 if (!transform->dobj.dump || dopt->dataOnly)
12278 return;
12279
12280 /* Cannot dump if we don't have the transform functions' info */
12281 if (OidIsValid(transform->trffromsql))
12282 {
12283 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12284 if (fromsqlFuncInfo == NULL)
12285 fatal("could not find function definition for function with OID %u",
12286 transform->trffromsql);
12287 }
12288 if (OidIsValid(transform->trftosql))
12289 {
12290 tosqlFuncInfo = findFuncByOid(transform->trftosql);
12291 if (tosqlFuncInfo == NULL)
12292 fatal("could not find function definition for function with OID %u",
12293 transform->trftosql);
12294 }
12295
12296 defqry = createPQExpBuffer();
12297 delqry = createPQExpBuffer();
12298 labelq = createPQExpBuffer();
12299 transformargs = createPQExpBuffer();
12300
12301 lanname = get_language_name(fout, transform->trflang);
12302 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12303
12304 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12305 transformType, lanname);
12306
12307 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12308 transformType, lanname);
12309
12310 if (!transform->trffromsql && !transform->trftosql)
12311 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12312
12313 if (transform->trffromsql)
12314 {
12315 if (fromsqlFuncInfo)
12316 {
12317 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12318
12319 /*
12320 * Always qualify the function name (format_function_signature
12321 * won't qualify it).
12322 */
12323 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12324 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12325 free(fsig);
12326 }
12327 else
12328 pg_log_warning("bogus value in pg_transform.trffromsql field");
12329 }
12330
12331 if (transform->trftosql)
12332 {
12333 if (transform->trffromsql)
12334 appendPQExpBuffer(defqry, ", ");
12335
12336 if (tosqlFuncInfo)
12337 {
12338 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12339
12340 /*
12341 * Always qualify the function name (format_function_signature
12342 * won't qualify it).
12343 */
12344 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12345 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12346 free(fsig);
12347 }
12348 else
12349 pg_log_warning("bogus value in pg_transform.trftosql field");
12350 }
12351
12352 appendPQExpBuffer(defqry, ");\n");
12353
12354 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12355 transformType, lanname);
12356
12357 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12358 transformType, lanname);
12359
12360 if (dopt->binary_upgrade)
12361 binary_upgrade_extension_member(defqry, &transform->dobj,
12362 "TRANSFORM", transformargs->data, NULL);
12363
12364 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12365 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12366 ARCHIVE_OPTS(.tag = labelq->data,
12367 .description = "TRANSFORM",
12368 .section = SECTION_PRE_DATA,
12369 .createStmt = defqry->data,
12370 .dropStmt = delqry->data,
12371 .deps = transform->dobj.dependencies,
12372 .nDeps = transform->dobj.nDeps));
12373
12374 /* Dump Transform Comments */
12375 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12376 dumpComment(fout, "TRANSFORM", transformargs->data,
12377 NULL, "",
12378 transform->dobj.catId, 0, transform->dobj.dumpId);
12379
12380 free(lanname);
12381 free(transformType);
12382 destroyPQExpBuffer(defqry);
12383 destroyPQExpBuffer(delqry);
12384 destroyPQExpBuffer(labelq);
12385 destroyPQExpBuffer(transformargs);
12386}
12387
12388
12389/*
12390 * dumpOpr
12391 * write out a single operator definition
12392 */
12393static void
12394dumpOpr(Archive *fout, OprInfo *oprinfo)
12395{
12396 DumpOptions *dopt = fout->dopt;
12397 PQExpBuffer query;
12398 PQExpBuffer q;
12399 PQExpBuffer delq;
12400 PQExpBuffer oprid;
12401 PQExpBuffer details;
12402 PGresult *res;
12403 int i_oprkind;
12404 int i_oprcode;
12405 int i_oprleft;
12406 int i_oprright;
12407 int i_oprcom;
12408 int i_oprnegate;
12409 int i_oprrest;
12410 int i_oprjoin;
12411 int i_oprcanmerge;
12412 int i_oprcanhash;
12413 char *oprkind;
12414 char *oprcode;
12415 char *oprleft;
12416 char *oprright;
12417 char *oprcom;
12418 char *oprnegate;
12419 char *oprrest;
12420 char *oprjoin;
12421 char *oprcanmerge;
12422 char *oprcanhash;
12423 char *oprregproc;
12424 char *oprref;
12425
12426 /* Skip if not to be dumped */
12427 if (!oprinfo->dobj.dump || dopt->dataOnly)
12428 return;
12429
12430 /*
12431 * some operators are invalid because they were the result of user
12432 * defining operators before commutators exist
12433 */
12434 if (!OidIsValid(oprinfo->oprcode))
12435 return;
12436
12437 query = createPQExpBuffer();
12438 q = createPQExpBuffer();
12439 delq = createPQExpBuffer();
12440 oprid = createPQExpBuffer();
12441 details = createPQExpBuffer();
12442
12443 if (fout->remoteVersion >= 80300)
12444 {
12445 appendPQExpBuffer(query, "SELECT oprkind, "
12446 "oprcode::pg_catalog.regprocedure, "
12447 "oprleft::pg_catalog.regtype, "
12448 "oprright::pg_catalog.regtype, "
12449 "oprcom, "
12450 "oprnegate, "
12451 "oprrest::pg_catalog.regprocedure, "
12452 "oprjoin::pg_catalog.regprocedure, "
12453 "oprcanmerge, oprcanhash "
12454 "FROM pg_catalog.pg_operator "
12455 "WHERE oid = '%u'::pg_catalog.oid",
12456 oprinfo->dobj.catId.oid);
12457 }
12458 else
12459 {
12460 appendPQExpBuffer(query, "SELECT oprkind, "
12461 "oprcode::pg_catalog.regprocedure, "
12462 "oprleft::pg_catalog.regtype, "
12463 "oprright::pg_catalog.regtype, "
12464 "oprcom, "
12465 "oprnegate, "
12466 "oprrest::pg_catalog.regprocedure, "
12467 "oprjoin::pg_catalog.regprocedure, "
12468 "(oprlsortop != 0) AS oprcanmerge, "
12469 "oprcanhash "
12470 "FROM pg_catalog.pg_operator "
12471 "WHERE oid = '%u'::pg_catalog.oid",
12472 oprinfo->dobj.catId.oid);
12473 }
12474
12475 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12476
12477 i_oprkind = PQfnumber(res, "oprkind");
12478 i_oprcode = PQfnumber(res, "oprcode");
12479 i_oprleft = PQfnumber(res, "oprleft");
12480 i_oprright = PQfnumber(res, "oprright");
12481 i_oprcom = PQfnumber(res, "oprcom");
12482 i_oprnegate = PQfnumber(res, "oprnegate");
12483 i_oprrest = PQfnumber(res, "oprrest");
12484 i_oprjoin = PQfnumber(res, "oprjoin");
12485 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
12486 i_oprcanhash = PQfnumber(res, "oprcanhash");
12487
12488 oprkind = PQgetvalue(res, 0, i_oprkind);
12489 oprcode = PQgetvalue(res, 0, i_oprcode);
12490 oprleft = PQgetvalue(res, 0, i_oprleft);
12491 oprright = PQgetvalue(res, 0, i_oprright);
12492 oprcom = PQgetvalue(res, 0, i_oprcom);
12493 oprnegate = PQgetvalue(res, 0, i_oprnegate);
12494 oprrest = PQgetvalue(res, 0, i_oprrest);
12495 oprjoin = PQgetvalue(res, 0, i_oprjoin);
12496 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
12497 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
12498
12499 oprregproc = convertRegProcReference(fout, oprcode);
12500 if (oprregproc)
12501 {
12502 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
12503 free(oprregproc);
12504 }
12505
12506 appendPQExpBuffer(oprid, "%s (",
12507 oprinfo->dobj.name);
12508
12509 /*
12510 * right unary means there's a left arg and left unary means there's a
12511 * right arg
12512 */
12513 if (strcmp(oprkind, "r") == 0 ||
12514 strcmp(oprkind, "b") == 0)
12515 {
12516 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
12517 appendPQExpBufferStr(oprid, oprleft);
12518 }
12519 else
12520 appendPQExpBufferStr(oprid, "NONE");
12521
12522 if (strcmp(oprkind, "l") == 0 ||
12523 strcmp(oprkind, "b") == 0)
12524 {
12525 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
12526 appendPQExpBuffer(oprid, ", %s)", oprright);
12527 }
12528 else
12529 appendPQExpBufferStr(oprid, ", NONE)");
12530
12531 oprref = getFormattedOperatorName(fout, oprcom);
12532 if (oprref)
12533 {
12534 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
12535 free(oprref);
12536 }
12537
12538 oprref = getFormattedOperatorName(fout, oprnegate);
12539 if (oprref)
12540 {
12541 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
12542 free(oprref);
12543 }
12544
12545 if (strcmp(oprcanmerge, "t") == 0)
12546 appendPQExpBufferStr(details, ",\n MERGES");
12547
12548 if (strcmp(oprcanhash, "t") == 0)
12549 appendPQExpBufferStr(details, ",\n HASHES");
12550
12551 oprregproc = convertRegProcReference(fout, oprrest);
12552 if (oprregproc)
12553 {
12554 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
12555 free(oprregproc);
12556 }
12557
12558 oprregproc = convertRegProcReference(fout, oprjoin);
12559 if (oprregproc)
12560 {
12561 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
12562 free(oprregproc);
12563 }
12564
12565 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
12566 fmtId(oprinfo->dobj.namespace->dobj.name),
12567 oprid->data);
12568
12569 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
12570 fmtId(oprinfo->dobj.namespace->dobj.name),
12571 oprinfo->dobj.name, details->data);
12572
12573 if (dopt->binary_upgrade)
12574 binary_upgrade_extension_member(q, &oprinfo->dobj,
12575 "OPERATOR", oprid->data,
12576 oprinfo->dobj.namespace->dobj.name);
12577
12578 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12579 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
12580 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
12581 .namespace = oprinfo->dobj.namespace->dobj.name,
12582 .owner = oprinfo->rolname,
12583 .description = "OPERATOR",
12584 .section = SECTION_PRE_DATA,
12585 .createStmt = q->data,
12586 .dropStmt = delq->data));
12587
12588 /* Dump Operator Comments */
12589 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12590 dumpComment(fout, "OPERATOR", oprid->data,
12591 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
12592 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
12593
12594 PQclear(res);
12595
12596 destroyPQExpBuffer(query);
12597 destroyPQExpBuffer(q);
12598 destroyPQExpBuffer(delq);
12599 destroyPQExpBuffer(oprid);
12600 destroyPQExpBuffer(details);
12601}
12602
12603/*
12604 * Convert a function reference obtained from pg_operator
12605 *
12606 * Returns allocated string of what to print, or NULL if function references
12607 * is InvalidOid. Returned string is expected to be free'd by the caller.
12608 *
12609 * The input is a REGPROCEDURE display; we have to strip the argument-types
12610 * part.
12611 */
12612static char *
12613convertRegProcReference(Archive *fout, const char *proc)
12614{
12615 char *name;
12616 char *paren;
12617 bool inquote;
12618
12619 /* In all cases "-" means a null reference */
12620 if (strcmp(proc, "-") == 0)
12621 return NULL;
12622
12623 name = pg_strdup(proc);
12624 /* find non-double-quoted left paren */
12625 inquote = false;
12626 for (paren = name; *paren; paren++)
12627 {
12628 if (*paren == '(' && !inquote)
12629 {
12630 *paren = '\0';
12631 break;
12632 }
12633 if (*paren == '"')
12634 inquote = !inquote;
12635 }
12636 return name;
12637}
12638
12639/*
12640 * getFormattedOperatorName - retrieve the operator name for the
12641 * given operator OID (presented in string form).
12642 *
12643 * Returns an allocated string, or NULL if the given OID is invalid.
12644 * Caller is responsible for free'ing result string.
12645 *
12646 * What we produce has the format "OPERATOR(schema.oprname)". This is only
12647 * useful in commands where the operator's argument types can be inferred from
12648 * context. We always schema-qualify the name, though. The predecessor to
12649 * this code tried to skip the schema qualification if possible, but that led
12650 * to wrong results in corner cases, such as if an operator and its negator
12651 * are in different schemas.
12652 */
12653static char *
12654getFormattedOperatorName(Archive *fout, const char *oproid)
12655{
12656 OprInfo *oprInfo;
12657
12658 /* In all cases "0" means a null reference */
12659 if (strcmp(oproid, "0") == 0)
12660 return NULL;
12661
12662 oprInfo = findOprByOid(atooid(oproid));
12663 if (oprInfo == NULL)
12664 {
12665 pg_log_warning("could not find operator with OID %s",
12666 oproid);
12667 return NULL;
12668 }
12669
12670 return psprintf("OPERATOR(%s.%s)",
12671 fmtId(oprInfo->dobj.namespace->dobj.name),
12672 oprInfo->dobj.name);
12673}
12674
12675/*
12676 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
12677 *
12678 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
12679 * argument lists of these functions are predetermined. Note that the
12680 * caller should ensure we are in the proper schema, because the results
12681 * are search path dependent!
12682 */
12683static char *
12684convertTSFunction(Archive *fout, Oid funcOid)
12685{
12686 char *result;
12687 char query[128];
12688 PGresult *res;
12689
12690 snprintf(query, sizeof(query),
12691 "SELECT '%u'::pg_catalog.regproc", funcOid);
12692 res = ExecuteSqlQueryForSingleRow(fout, query);
12693
12694 result = pg_strdup(PQgetvalue(res, 0, 0));
12695
12696 PQclear(res);
12697
12698 return result;
12699}
12700
12701/*
12702 * dumpAccessMethod
12703 * write out a single access method definition
12704 */
12705static void
12706dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
12707{
12708 DumpOptions *dopt = fout->dopt;
12709 PQExpBuffer q;
12710 PQExpBuffer delq;
12711 char *qamname;
12712
12713 /* Skip if not to be dumped */
12714 if (!aminfo->dobj.dump || dopt->dataOnly)
12715 return;
12716
12717 q = createPQExpBuffer();
12718 delq = createPQExpBuffer();
12719
12720 qamname = pg_strdup(fmtId(aminfo->dobj.name));
12721
12722 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
12723
12724 switch (aminfo->amtype)
12725 {
12726 case AMTYPE_INDEX:
12727 appendPQExpBuffer(q, "TYPE INDEX ");
12728 break;
12729 case AMTYPE_TABLE:
12730 appendPQExpBuffer(q, "TYPE TABLE ");
12731 break;
12732 default:
12733 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
12734 aminfo->amtype, qamname);
12735 destroyPQExpBuffer(q);
12736 destroyPQExpBuffer(delq);
12737 free(qamname);
12738 return;
12739 }
12740
12741 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
12742
12743 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
12744 qamname);
12745
12746 if (dopt->binary_upgrade)
12747 binary_upgrade_extension_member(q, &aminfo->dobj,
12748 "ACCESS METHOD", qamname, NULL);
12749
12750 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12751 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
12752 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
12753 .description = "ACCESS METHOD",
12754 .section = SECTION_PRE_DATA,
12755 .createStmt = q->data,
12756 .dropStmt = delq->data));
12757
12758 /* Dump Access Method Comments */
12759 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12760 dumpComment(fout, "ACCESS METHOD", qamname,
12761 NULL, "",
12762 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
12763
12764 destroyPQExpBuffer(q);
12765 destroyPQExpBuffer(delq);
12766 free(qamname);
12767}
12768
12769/*
12770 * dumpOpclass
12771 * write out a single operator class definition
12772 */
12773static void
12774dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
12775{
12776 DumpOptions *dopt = fout->dopt;
12777 PQExpBuffer query;
12778 PQExpBuffer q;
12779 PQExpBuffer delq;
12780 PQExpBuffer nameusing;
12781 PGresult *res;
12782 int ntups;
12783 int i_opcintype;
12784 int i_opckeytype;
12785 int i_opcdefault;
12786 int i_opcfamily;
12787 int i_opcfamilyname;
12788 int i_opcfamilynsp;
12789 int i_amname;
12790 int i_amopstrategy;
12791 int i_amopreqcheck;
12792 int i_amopopr;
12793 int i_sortfamily;
12794 int i_sortfamilynsp;
12795 int i_amprocnum;
12796 int i_amproc;
12797 int i_amproclefttype;
12798 int i_amprocrighttype;
12799 char *opcintype;
12800 char *opckeytype;
12801 char *opcdefault;
12802 char *opcfamily;
12803 char *opcfamilyname;
12804 char *opcfamilynsp;
12805 char *amname;
12806 char *amopstrategy;
12807 char *amopreqcheck;
12808 char *amopopr;
12809 char *sortfamily;
12810 char *sortfamilynsp;
12811 char *amprocnum;
12812 char *amproc;
12813 char *amproclefttype;
12814 char *amprocrighttype;
12815 bool needComma;
12816 int i;
12817
12818 /* Skip if not to be dumped */
12819 if (!opcinfo->dobj.dump || dopt->dataOnly)
12820 return;
12821
12822 query = createPQExpBuffer();
12823 q = createPQExpBuffer();
12824 delq = createPQExpBuffer();
12825 nameusing = createPQExpBuffer();
12826
12827 /* Get additional fields from the pg_opclass row */
12828 if (fout->remoteVersion >= 80300)
12829 {
12830 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
12831 "opckeytype::pg_catalog.regtype, "
12832 "opcdefault, opcfamily, "
12833 "opfname AS opcfamilyname, "
12834 "nspname AS opcfamilynsp, "
12835 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
12836 "FROM pg_catalog.pg_opclass c "
12837 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
12838 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
12839 "WHERE c.oid = '%u'::pg_catalog.oid",
12840 opcinfo->dobj.catId.oid);
12841 }
12842 else
12843 {
12844 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
12845 "opckeytype::pg_catalog.regtype, "
12846 "opcdefault, NULL AS opcfamily, "
12847 "NULL AS opcfamilyname, "
12848 "NULL AS opcfamilynsp, "
12849 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
12850 "FROM pg_catalog.pg_opclass "
12851 "WHERE oid = '%u'::pg_catalog.oid",
12852 opcinfo->dobj.catId.oid);
12853 }
12854
12855 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12856
12857 i_opcintype = PQfnumber(res, "opcintype");
12858 i_opckeytype = PQfnumber(res, "opckeytype");
12859 i_opcdefault = PQfnumber(res, "opcdefault");
12860 i_opcfamily = PQfnumber(res, "opcfamily");
12861 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
12862 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
12863 i_amname = PQfnumber(res, "amname");
12864
12865 /* opcintype may still be needed after we PQclear res */
12866 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
12867 opckeytype = PQgetvalue(res, 0, i_opckeytype);
12868 opcdefault = PQgetvalue(res, 0, i_opcdefault);
12869 /* opcfamily will still be needed after we PQclear res */
12870 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
12871 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
12872 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
12873 /* amname will still be needed after we PQclear res */
12874 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
12875
12876 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
12877 fmtQualifiedDumpable(opcinfo));
12878 appendPQExpBuffer(delq, " USING %s;\n",
12879 fmtId(amname));
12880
12881 /* Build the fixed portion of the CREATE command */
12882 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
12883 fmtQualifiedDumpable(opcinfo));
12884 if (strcmp(opcdefault, "t") == 0)
12885 appendPQExpBufferStr(q, "DEFAULT ");
12886 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
12887 opcintype,
12888 fmtId(amname));
12889 if (strlen(opcfamilyname) > 0)
12890 {
12891 appendPQExpBufferStr(q, " FAMILY ");
12892 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
12893 appendPQExpBufferStr(q, fmtId(opcfamilyname));
12894 }
12895 appendPQExpBufferStr(q, " AS\n ");
12896
12897 needComma = false;
12898
12899 if (strcmp(opckeytype, "-") != 0)
12900 {
12901 appendPQExpBuffer(q, "STORAGE %s",
12902 opckeytype);
12903 needComma = true;
12904 }
12905
12906 PQclear(res);
12907
12908 /*
12909 * Now fetch and print the OPERATOR entries (pg_amop rows).
12910 *
12911 * Print only those opfamily members that are tied to the opclass by
12912 * pg_depend entries.
12913 *
12914 * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an
12915 * older server's opclass in which it is used. This is to avoid
12916 * hard-to-detect breakage if a newer pg_dump is used to dump from an
12917 * older server and then reload into that old version. This can go away
12918 * once 8.3 is so old as to not be of interest to anyone.
12919 */
12920 resetPQExpBuffer(query);
12921
12922 if (fout->remoteVersion >= 90100)
12923 {
12924 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12925 "amopopr::pg_catalog.regoperator, "
12926 "opfname AS sortfamily, "
12927 "nspname AS sortfamilynsp "
12928 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
12929 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
12930 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
12931 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
12932 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12933 "AND refobjid = '%u'::pg_catalog.oid "
12934 "AND amopfamily = '%s'::pg_catalog.oid "
12935 "ORDER BY amopstrategy",
12936 opcinfo->dobj.catId.oid,
12937 opcfamily);
12938 }
12939 else if (fout->remoteVersion >= 80400)
12940 {
12941 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
12942 "amopopr::pg_catalog.regoperator, "
12943 "NULL AS sortfamily, "
12944 "NULL AS sortfamilynsp "
12945 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12946 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12947 "AND refobjid = '%u'::pg_catalog.oid "
12948 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12949 "AND objid = ao.oid "
12950 "ORDER BY amopstrategy",
12951 opcinfo->dobj.catId.oid);
12952 }
12953 else if (fout->remoteVersion >= 80300)
12954 {
12955 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
12956 "amopopr::pg_catalog.regoperator, "
12957 "NULL AS sortfamily, "
12958 "NULL AS sortfamilynsp "
12959 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
12960 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
12961 "AND refobjid = '%u'::pg_catalog.oid "
12962 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
12963 "AND objid = ao.oid "
12964 "ORDER BY amopstrategy",
12965 opcinfo->dobj.catId.oid);
12966 }
12967 else
12968 {
12969 /*
12970 * Here, we print all entries since there are no opfamilies and hence
12971 * no loose operators to worry about.
12972 */
12973 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
12974 "amopopr::pg_catalog.regoperator, "
12975 "NULL AS sortfamily, "
12976 "NULL AS sortfamilynsp "
12977 "FROM pg_catalog.pg_amop "
12978 "WHERE amopclaid = '%u'::pg_catalog.oid "
12979 "ORDER BY amopstrategy",
12980 opcinfo->dobj.catId.oid);
12981 }
12982
12983 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12984
12985 ntups = PQntuples(res);
12986
12987 i_amopstrategy = PQfnumber(res, "amopstrategy");
12988 i_amopreqcheck = PQfnumber(res, "amopreqcheck");
12989 i_amopopr = PQfnumber(res, "amopopr");
12990 i_sortfamily = PQfnumber(res, "sortfamily");
12991 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
12992
12993 for (i = 0; i < ntups; i++)
12994 {
12995 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
12996 amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
12997 amopopr = PQgetvalue(res, i, i_amopopr);
12998 sortfamily = PQgetvalue(res, i, i_sortfamily);
12999 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13000
13001 if (needComma)
13002 appendPQExpBufferStr(q, " ,\n ");
13003
13004 appendPQExpBuffer(q, "OPERATOR %s %s",
13005 amopstrategy, amopopr);
13006
13007 if (strlen(sortfamily) > 0)
13008 {
13009 appendPQExpBufferStr(q, " FOR ORDER BY ");
13010 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13011 appendPQExpBufferStr(q, fmtId(sortfamily));
13012 }
13013
13014 if (strcmp(amopreqcheck, "t") == 0)
13015 appendPQExpBufferStr(q, " RECHECK");
13016
13017 needComma = true;
13018 }
13019
13020 PQclear(res);
13021
13022 /*
13023 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13024 *
13025 * Print only those opfamily members that are tied to the opclass by
13026 * pg_depend entries.
13027 *
13028 * We print the amproclefttype/amprocrighttype even though in most cases
13029 * the backend could deduce the right values, because of the corner case
13030 * of a btree sort support function for a cross-type comparison. That's
13031 * only allowed in 9.2 and later, but for simplicity print them in all
13032 * versions that have the columns.
13033 */
13034 resetPQExpBuffer(query);
13035
13036 if (fout->remoteVersion >= 80300)
13037 {
13038 appendPQExpBuffer(query, "SELECT amprocnum, "
13039 "amproc::pg_catalog.regprocedure, "
13040 "amproclefttype::pg_catalog.regtype, "
13041 "amprocrighttype::pg_catalog.regtype "
13042 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13043 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13044 "AND refobjid = '%u'::pg_catalog.oid "
13045 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13046 "AND objid = ap.oid "
13047 "ORDER BY amprocnum",
13048 opcinfo->dobj.catId.oid);
13049 }
13050 else
13051 {
13052 appendPQExpBuffer(query, "SELECT amprocnum, "
13053 "amproc::pg_catalog.regprocedure, "
13054 "'' AS amproclefttype, "
13055 "'' AS amprocrighttype "
13056 "FROM pg_catalog.pg_amproc "
13057 "WHERE amopclaid = '%u'::pg_catalog.oid "
13058 "ORDER BY amprocnum",
13059 opcinfo->dobj.catId.oid);
13060 }
13061
13062 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13063
13064 ntups = PQntuples(res);
13065
13066 i_amprocnum = PQfnumber(res, "amprocnum");
13067 i_amproc = PQfnumber(res, "amproc");
13068 i_amproclefttype = PQfnumber(res, "amproclefttype");
13069 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13070
13071 for (i = 0; i < ntups; i++)
13072 {
13073 amprocnum = PQgetvalue(res, i, i_amprocnum);
13074 amproc = PQgetvalue(res, i, i_amproc);
13075 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13076 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13077
13078 if (needComma)
13079 appendPQExpBufferStr(q, " ,\n ");
13080
13081 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13082
13083 if (*amproclefttype && *amprocrighttype)
13084 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13085
13086 appendPQExpBuffer(q, " %s", amproc);
13087
13088 needComma = true;
13089 }
13090
13091 PQclear(res);
13092
13093 /*
13094 * If needComma is still false it means we haven't added anything after
13095 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13096 * clause with the same datatype. This isn't sanctioned by the
13097 * documentation, but actually DefineOpClass will treat it as a no-op.
13098 */
13099 if (!needComma)
13100 appendPQExpBuffer(q, "STORAGE %s", opcintype);
13101
13102 appendPQExpBufferStr(q, ";\n");
13103
13104 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13105 appendPQExpBuffer(nameusing, " USING %s",
13106 fmtId(amname));
13107
13108 if (dopt->binary_upgrade)
13109 binary_upgrade_extension_member(q, &opcinfo->dobj,
13110 "OPERATOR CLASS", nameusing->data,
13111 opcinfo->dobj.namespace->dobj.name);
13112
13113 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13114 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13115 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13116 .namespace = opcinfo->dobj.namespace->dobj.name,
13117 .owner = opcinfo->rolname,
13118 .description = "OPERATOR CLASS",
13119 .section = SECTION_PRE_DATA,
13120 .createStmt = q->data,
13121 .dropStmt = delq->data));
13122
13123 /* Dump Operator Class Comments */
13124 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13125 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13126 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13127 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13128
13129 free(opcintype);
13130 free(opcfamily);
13131 free(amname);
13132 destroyPQExpBuffer(query);
13133 destroyPQExpBuffer(q);
13134 destroyPQExpBuffer(delq);
13135 destroyPQExpBuffer(nameusing);
13136}
13137
13138/*
13139 * dumpOpfamily
13140 * write out a single operator family definition
13141 *
13142 * Note: this also dumps any "loose" operator members that aren't bound to a
13143 * specific opclass within the opfamily.
13144 */
13145static void
13146dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
13147{
13148 DumpOptions *dopt = fout->dopt;
13149 PQExpBuffer query;
13150 PQExpBuffer q;
13151 PQExpBuffer delq;
13152 PQExpBuffer nameusing;
13153 PGresult *res;
13154 PGresult *res_ops;
13155 PGresult *res_procs;
13156 int ntups;
13157 int i_amname;
13158 int i_amopstrategy;
13159 int i_amopreqcheck;
13160 int i_amopopr;
13161 int i_sortfamily;
13162 int i_sortfamilynsp;
13163 int i_amprocnum;
13164 int i_amproc;
13165 int i_amproclefttype;
13166 int i_amprocrighttype;
13167 char *amname;
13168 char *amopstrategy;
13169 char *amopreqcheck;
13170 char *amopopr;
13171 char *sortfamily;
13172 char *sortfamilynsp;
13173 char *amprocnum;
13174 char *amproc;
13175 char *amproclefttype;
13176 char *amprocrighttype;
13177 bool needComma;
13178 int i;
13179
13180 /* Skip if not to be dumped */
13181 if (!opfinfo->dobj.dump || dopt->dataOnly)
13182 return;
13183
13184 query = createPQExpBuffer();
13185 q = createPQExpBuffer();
13186 delq = createPQExpBuffer();
13187 nameusing = createPQExpBuffer();
13188
13189 /*
13190 * Fetch only those opfamily members that are tied directly to the
13191 * opfamily by pg_depend entries.
13192 *
13193 * XXX RECHECK is gone as of 8.4, but we'll still print it if dumping an
13194 * older server's opclass in which it is used. This is to avoid
13195 * hard-to-detect breakage if a newer pg_dump is used to dump from an
13196 * older server and then reload into that old version. This can go away
13197 * once 8.3 is so old as to not be of interest to anyone.
13198 */
13199 if (fout->remoteVersion >= 90100)
13200 {
13201 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
13202 "amopopr::pg_catalog.regoperator, "
13203 "opfname AS sortfamily, "
13204 "nspname AS sortfamilynsp "
13205 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13206 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13207 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13208 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13209 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13210 "AND refobjid = '%u'::pg_catalog.oid "
13211 "AND amopfamily = '%u'::pg_catalog.oid "
13212 "ORDER BY amopstrategy",
13213 opfinfo->dobj.catId.oid,
13214 opfinfo->dobj.catId.oid);
13215 }
13216 else if (fout->remoteVersion >= 80400)
13217 {
13218 appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
13219 "amopopr::pg_catalog.regoperator, "
13220 "NULL AS sortfamily, "
13221 "NULL AS sortfamilynsp "
13222 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
13223 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13224 "AND refobjid = '%u'::pg_catalog.oid "
13225 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
13226 "AND objid = ao.oid "
13227 "ORDER BY amopstrategy",
13228 opfinfo->dobj.catId.oid);
13229 }
13230 else
13231 {
13232 appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
13233 "amopopr::pg_catalog.regoperator, "
13234 "NULL AS sortfamily, "
13235 "NULL AS sortfamilynsp "
13236 "FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
13237 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13238 "AND refobjid = '%u'::pg_catalog.oid "
13239 "AND classid = 'pg_catalog.pg_amop'::pg_catalog.regclass "
13240 "AND objid = ao.oid "
13241 "ORDER BY amopstrategy",
13242 opfinfo->dobj.catId.oid);
13243 }
13244
13245 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13246
13247 resetPQExpBuffer(query);
13248
13249 appendPQExpBuffer(query, "SELECT amprocnum, "
13250 "amproc::pg_catalog.regprocedure, "
13251 "amproclefttype::pg_catalog.regtype, "
13252 "amprocrighttype::pg_catalog.regtype "
13253 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13254 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13255 "AND refobjid = '%u'::pg_catalog.oid "
13256 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13257 "AND objid = ap.oid "
13258 "ORDER BY amprocnum",
13259 opfinfo->dobj.catId.oid);
13260
13261 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13262
13263 /* Get additional fields from the pg_opfamily row */
13264 resetPQExpBuffer(query);
13265
13266 appendPQExpBuffer(query, "SELECT "
13267 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13268 "FROM pg_catalog.pg_opfamily "
13269 "WHERE oid = '%u'::pg_catalog.oid",
13270 opfinfo->dobj.catId.oid);
13271
13272 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13273
13274 i_amname = PQfnumber(res, "amname");
13275
13276 /* amname will still be needed after we PQclear res */
13277 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13278
13279 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13280 fmtQualifiedDumpable(opfinfo));
13281 appendPQExpBuffer(delq, " USING %s;\n",
13282 fmtId(amname));
13283
13284 /* Build the fixed portion of the CREATE command */
13285 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13286 fmtQualifiedDumpable(opfinfo));
13287 appendPQExpBuffer(q, " USING %s;\n",
13288 fmtId(amname));
13289
13290 PQclear(res);
13291
13292 /* Do we need an ALTER to add loose members? */
13293 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13294 {
13295 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13296 fmtQualifiedDumpable(opfinfo));
13297 appendPQExpBuffer(q, " USING %s ADD\n ",
13298 fmtId(amname));
13299
13300 needComma = false;
13301
13302 /*
13303 * Now fetch and print the OPERATOR entries (pg_amop rows).
13304 */
13305 ntups = PQntuples(res_ops);
13306
13307 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13308 i_amopreqcheck = PQfnumber(res_ops, "amopreqcheck");
13309 i_amopopr = PQfnumber(res_ops, "amopopr");
13310 i_sortfamily = PQfnumber(res_ops, "sortfamily");
13311 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13312
13313 for (i = 0; i < ntups; i++)
13314 {
13315 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13316 amopreqcheck = PQgetvalue(res_ops, i, i_amopreqcheck);
13317 amopopr = PQgetvalue(res_ops, i, i_amopopr);
13318 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13319 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13320
13321 if (needComma)
13322 appendPQExpBufferStr(q, " ,\n ");
13323
13324 appendPQExpBuffer(q, "OPERATOR %s %s",
13325 amopstrategy, amopopr);
13326
13327 if (strlen(sortfamily) > 0)
13328 {
13329 appendPQExpBufferStr(q, " FOR ORDER BY ");
13330 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13331 appendPQExpBufferStr(q, fmtId(sortfamily));
13332 }
13333
13334 if (strcmp(amopreqcheck, "t") == 0)
13335 appendPQExpBufferStr(q, " RECHECK");
13336
13337 needComma = true;
13338 }
13339
13340 /*
13341 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13342 */
13343 ntups = PQntuples(res_procs);
13344
13345 i_amprocnum = PQfnumber(res_procs, "amprocnum");
13346 i_amproc = PQfnumber(res_procs, "amproc");
13347 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13348 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13349
13350 for (i = 0; i < ntups; i++)
13351 {
13352 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13353 amproc = PQgetvalue(res_procs, i, i_amproc);
13354 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13355 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13356
13357 if (needComma)
13358 appendPQExpBufferStr(q, " ,\n ");
13359
13360 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13361 amprocnum, amproclefttype, amprocrighttype,
13362 amproc);
13363
13364 needComma = true;
13365 }
13366
13367 appendPQExpBufferStr(q, ";\n");
13368 }
13369
13370 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13371 appendPQExpBuffer(nameusing, " USING %s",
13372 fmtId(amname));
13373
13374 if (dopt->binary_upgrade)
13375 binary_upgrade_extension_member(q, &opfinfo->dobj,
13376 "OPERATOR FAMILY", nameusing->data,
13377 opfinfo->dobj.namespace->dobj.name);
13378
13379 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13380 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13381 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13382 .namespace = opfinfo->dobj.namespace->dobj.name,
13383 .owner = opfinfo->rolname,
13384 .description = "OPERATOR FAMILY",
13385 .section = SECTION_PRE_DATA,
13386 .createStmt = q->data,
13387 .dropStmt = delq->data));
13388
13389 /* Dump Operator Family Comments */
13390 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13391 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13392 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13393 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13394
13395 free(amname);
13396 PQclear(res_ops);
13397 PQclear(res_procs);
13398 destroyPQExpBuffer(query);
13399 destroyPQExpBuffer(q);
13400 destroyPQExpBuffer(delq);
13401 destroyPQExpBuffer(nameusing);
13402}
13403
13404/*
13405 * dumpCollation
13406 * write out a single collation definition
13407 */
13408static void
13409dumpCollation(Archive *fout, CollInfo *collinfo)
13410{
13411 DumpOptions *dopt = fout->dopt;
13412 PQExpBuffer query;
13413 PQExpBuffer q;
13414 PQExpBuffer delq;
13415 char *qcollname;
13416 PGresult *res;
13417 int i_collprovider;
13418 int i_collisdeterministic;
13419 int i_collcollate;
13420 int i_collctype;
13421 const char *collprovider;
13422 const char *collcollate;
13423 const char *collctype;
13424
13425 /* Skip if not to be dumped */
13426 if (!collinfo->dobj.dump || dopt->dataOnly)
13427 return;
13428
13429 query = createPQExpBuffer();
13430 q = createPQExpBuffer();
13431 delq = createPQExpBuffer();
13432
13433 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13434
13435 /* Get collation-specific details */
13436 appendPQExpBuffer(query, "SELECT ");
13437
13438 if (fout->remoteVersion >= 100000)
13439 appendPQExpBuffer(query,
13440 "collprovider, "
13441 "collversion, ");
13442 else
13443 appendPQExpBuffer(query,
13444 "'c' AS collprovider, "
13445 "NULL AS collversion, ");
13446
13447 if (fout->remoteVersion >= 120000)
13448 appendPQExpBuffer(query,
13449 "collisdeterministic, ");
13450 else
13451 appendPQExpBuffer(query,
13452 "true AS collisdeterministic, ");
13453
13454 appendPQExpBuffer(query,
13455 "collcollate, "
13456 "collctype "
13457 "FROM pg_catalog.pg_collation c "
13458 "WHERE c.oid = '%u'::pg_catalog.oid",
13459 collinfo->dobj.catId.oid);
13460
13461 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13462
13463 i_collprovider = PQfnumber(res, "collprovider");
13464 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13465 i_collcollate = PQfnumber(res, "collcollate");
13466 i_collctype = PQfnumber(res, "collctype");
13467
13468 collprovider = PQgetvalue(res, 0, i_collprovider);
13469 collcollate = PQgetvalue(res, 0, i_collcollate);
13470 collctype = PQgetvalue(res, 0, i_collctype);
13471
13472 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13473 fmtQualifiedDumpable(collinfo));
13474
13475 appendPQExpBuffer(q, "CREATE COLLATION %s (",
13476 fmtQualifiedDumpable(collinfo));
13477
13478 appendPQExpBufferStr(q, "provider = ");
13479 if (collprovider[0] == 'c')
13480 appendPQExpBufferStr(q, "libc");
13481 else if (collprovider[0] == 'i')
13482 appendPQExpBufferStr(q, "icu");
13483 else if (collprovider[0] == 'd')
13484 /* to allow dumping pg_catalog; not accepted on input */
13485 appendPQExpBufferStr(q, "default");
13486 else
13487 fatal("unrecognized collation provider: %s",
13488 collprovider);
13489
13490 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
13491 appendPQExpBufferStr(q, ", deterministic = false");
13492
13493 if (strcmp(collcollate, collctype) == 0)
13494 {
13495 appendPQExpBufferStr(q, ", locale = ");
13496 appendStringLiteralAH(q, collcollate, fout);
13497 }
13498 else
13499 {
13500 appendPQExpBufferStr(q, ", lc_collate = ");
13501 appendStringLiteralAH(q, collcollate, fout);
13502 appendPQExpBufferStr(q, ", lc_ctype = ");
13503 appendStringLiteralAH(q, collctype, fout);
13504 }
13505
13506 /*
13507 * For binary upgrade, carry over the collation version. For normal
13508 * dump/restore, omit the version, so that it is computed upon restore.
13509 */
13510 if (dopt->binary_upgrade)
13511 {
13512 int i_collversion;
13513
13514 i_collversion = PQfnumber(res, "collversion");
13515 if (!PQgetisnull(res, 0, i_collversion))
13516 {
13517 appendPQExpBufferStr(q, ", version = ");
13518 appendStringLiteralAH(q,
13519 PQgetvalue(res, 0, i_collversion),
13520 fout);
13521 }
13522 }
13523
13524 appendPQExpBufferStr(q, ");\n");
13525
13526 if (dopt->binary_upgrade)
13527 binary_upgrade_extension_member(q, &collinfo->dobj,
13528 "COLLATION", qcollname,
13529 collinfo->dobj.namespace->dobj.name);
13530
13531 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13532 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
13533 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
13534 .namespace = collinfo->dobj.namespace->dobj.name,
13535 .owner = collinfo->rolname,
13536 .description = "COLLATION",
13537 .section = SECTION_PRE_DATA,
13538 .createStmt = q->data,
13539 .dropStmt = delq->data));
13540
13541 /* Dump Collation Comments */
13542 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13543 dumpComment(fout, "COLLATION", qcollname,
13544 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
13545 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
13546
13547 PQclear(res);
13548
13549 destroyPQExpBuffer(query);
13550 destroyPQExpBuffer(q);
13551 destroyPQExpBuffer(delq);
13552 free(qcollname);
13553}
13554
13555/*
13556 * dumpConversion
13557 * write out a single conversion definition
13558 */
13559static void
13560dumpConversion(Archive *fout, ConvInfo *convinfo)
13561{
13562 DumpOptions *dopt = fout->dopt;
13563 PQExpBuffer query;
13564 PQExpBuffer q;
13565 PQExpBuffer delq;
13566 char *qconvname;
13567 PGresult *res;
13568 int i_conforencoding;
13569 int i_contoencoding;
13570 int i_conproc;
13571 int i_condefault;
13572 const char *conforencoding;
13573 const char *contoencoding;
13574 const char *conproc;
13575 bool condefault;
13576
13577 /* Skip if not to be dumped */
13578 if (!convinfo->dobj.dump || dopt->dataOnly)
13579 return;
13580
13581 query = createPQExpBuffer();
13582 q = createPQExpBuffer();
13583 delq = createPQExpBuffer();
13584
13585 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
13586
13587 /* Get conversion-specific details */
13588 appendPQExpBuffer(query, "SELECT "
13589 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
13590 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
13591 "conproc, condefault "
13592 "FROM pg_catalog.pg_conversion c "
13593 "WHERE c.oid = '%u'::pg_catalog.oid",
13594 convinfo->dobj.catId.oid);
13595
13596 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13597
13598 i_conforencoding = PQfnumber(res, "conforencoding");
13599 i_contoencoding = PQfnumber(res, "contoencoding");
13600 i_conproc = PQfnumber(res, "conproc");
13601 i_condefault = PQfnumber(res, "condefault");
13602
13603 conforencoding = PQgetvalue(res, 0, i_conforencoding);
13604 contoencoding = PQgetvalue(res, 0, i_contoencoding);
13605 conproc = PQgetvalue(res, 0, i_conproc);
13606 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
13607
13608 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
13609 fmtQualifiedDumpable(convinfo));
13610
13611 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
13612 (condefault) ? "DEFAULT " : "",
13613 fmtQualifiedDumpable(convinfo));
13614 appendStringLiteralAH(q, conforencoding, fout);
13615 appendPQExpBufferStr(q, " TO ");
13616 appendStringLiteralAH(q, contoencoding, fout);
13617 /* regproc output is already sufficiently quoted */
13618 appendPQExpBuffer(q, " FROM %s;\n", conproc);
13619
13620 if (dopt->binary_upgrade)
13621 binary_upgrade_extension_member(q, &convinfo->dobj,
13622 "CONVERSION", qconvname,
13623 convinfo->dobj.namespace->dobj.name);
13624
13625 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13626 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
13627 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
13628 .namespace = convinfo->dobj.namespace->dobj.name,
13629 .owner = convinfo->rolname,
13630 .description = "CONVERSION",
13631 .section = SECTION_PRE_DATA,
13632 .createStmt = q->data,
13633 .dropStmt = delq->data));
13634
13635 /* Dump Conversion Comments */
13636 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13637 dumpComment(fout, "CONVERSION", qconvname,
13638 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
13639 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
13640
13641 PQclear(res);
13642
13643 destroyPQExpBuffer(query);
13644 destroyPQExpBuffer(q);
13645 destroyPQExpBuffer(delq);
13646 free(qconvname);
13647}
13648
13649/*
13650 * format_aggregate_signature: generate aggregate name and argument list
13651 *
13652 * The argument type names are qualified if needed. The aggregate name
13653 * is never qualified.
13654 */
13655static char *
13656format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
13657{
13658 PQExpBufferData buf;
13659 int j;
13660
13661 initPQExpBuffer(&buf);
13662 if (honor_quotes)
13663 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
13664 else
13665 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
13666
13667 if (agginfo->aggfn.nargs == 0)
13668 appendPQExpBuffer(&buf, "(*)");
13669 else
13670 {
13671 appendPQExpBufferChar(&buf, '(');
13672 for (j = 0; j < agginfo->aggfn.nargs; j++)
13673 {
13674 char *typname;
13675
13676 typname = getFormattedTypeName(fout, agginfo->aggfn.argtypes[j],
13677 zeroAsOpaque);
13678
13679 appendPQExpBuffer(&buf, "%s%s",
13680 (j > 0) ? ", " : "",
13681 typname);
13682 free(typname);
13683 }
13684 appendPQExpBufferChar(&buf, ')');
13685 }
13686 return buf.data;
13687}
13688
13689/*
13690 * dumpAgg
13691 * write out a single aggregate definition
13692 */
13693static void
13694dumpAgg(Archive *fout, AggInfo *agginfo)
13695{
13696 DumpOptions *dopt = fout->dopt;
13697 PQExpBuffer query;
13698 PQExpBuffer q;
13699 PQExpBuffer delq;
13700 PQExpBuffer details;
13701 char *aggsig; /* identity signature */
13702 char *aggfullsig = NULL; /* full signature */
13703 char *aggsig_tag;
13704 PGresult *res;
13705 int i_aggtransfn;
13706 int i_aggfinalfn;
13707 int i_aggcombinefn;
13708 int i_aggserialfn;
13709 int i_aggdeserialfn;
13710 int i_aggmtransfn;
13711 int i_aggminvtransfn;
13712 int i_aggmfinalfn;
13713 int i_aggfinalextra;
13714 int i_aggmfinalextra;
13715 int i_aggfinalmodify;
13716 int i_aggmfinalmodify;
13717 int i_aggsortop;
13718 int i_aggkind;
13719 int i_aggtranstype;
13720 int i_aggtransspace;
13721 int i_aggmtranstype;
13722 int i_aggmtransspace;
13723 int i_agginitval;
13724 int i_aggminitval;
13725 int i_convertok;
13726 int i_proparallel;
13727 const char *aggtransfn;
13728 const char *aggfinalfn;
13729 const char *aggcombinefn;
13730 const char *aggserialfn;
13731 const char *aggdeserialfn;
13732 const char *aggmtransfn;
13733 const char *aggminvtransfn;
13734 const char *aggmfinalfn;
13735 bool aggfinalextra;
13736 bool aggmfinalextra;
13737 char aggfinalmodify;
13738 char aggmfinalmodify;
13739 const char *aggsortop;
13740 char *aggsortconvop;
13741 char aggkind;
13742 const char *aggtranstype;
13743 const char *aggtransspace;
13744 const char *aggmtranstype;
13745 const char *aggmtransspace;
13746 const char *agginitval;
13747 const char *aggminitval;
13748 bool convertok;
13749 const char *proparallel;
13750 char defaultfinalmodify;
13751
13752 /* Skip if not to be dumped */
13753 if (!agginfo->aggfn.dobj.dump || dopt->dataOnly)
13754 return;
13755
13756 query = createPQExpBuffer();
13757 q = createPQExpBuffer();
13758 delq = createPQExpBuffer();
13759 details = createPQExpBuffer();
13760
13761 /* Get aggregate-specific details */
13762 if (fout->remoteVersion >= 110000)
13763 {
13764 appendPQExpBuffer(query, "SELECT aggtransfn, "
13765 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13766 "aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, "
13767 "aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
13768 "aggfinalextra, aggmfinalextra, "
13769 "aggfinalmodify, aggmfinalmodify, "
13770 "aggsortop, "
13771 "aggkind, "
13772 "aggtransspace, agginitval, "
13773 "aggmtransspace, aggminitval, "
13774 "true AS convertok, "
13775 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13776 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs, "
13777 "p.proparallel "
13778 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13779 "WHERE a.aggfnoid = p.oid "
13780 "AND p.oid = '%u'::pg_catalog.oid",
13781 agginfo->aggfn.dobj.catId.oid);
13782 }
13783 else if (fout->remoteVersion >= 90600)
13784 {
13785 appendPQExpBuffer(query, "SELECT aggtransfn, "
13786 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13787 "aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, "
13788 "aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
13789 "aggfinalextra, aggmfinalextra, "
13790 "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, "
13791 "aggsortop, "
13792 "aggkind, "
13793 "aggtransspace, agginitval, "
13794 "aggmtransspace, aggminitval, "
13795 "true AS convertok, "
13796 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13797 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs, "
13798 "p.proparallel "
13799 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13800 "WHERE a.aggfnoid = p.oid "
13801 "AND p.oid = '%u'::pg_catalog.oid",
13802 agginfo->aggfn.dobj.catId.oid);
13803 }
13804 else if (fout->remoteVersion >= 90400)
13805 {
13806 appendPQExpBuffer(query, "SELECT aggtransfn, "
13807 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13808 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13809 "'-' AS aggdeserialfn, aggmtransfn, aggminvtransfn, "
13810 "aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
13811 "aggfinalextra, aggmfinalextra, "
13812 "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, "
13813 "aggsortop, "
13814 "aggkind, "
13815 "aggtransspace, agginitval, "
13816 "aggmtransspace, aggminitval, "
13817 "true AS convertok, "
13818 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13819 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
13820 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13821 "WHERE a.aggfnoid = p.oid "
13822 "AND p.oid = '%u'::pg_catalog.oid",
13823 agginfo->aggfn.dobj.catId.oid);
13824 }
13825 else if (fout->remoteVersion >= 80400)
13826 {
13827 appendPQExpBuffer(query, "SELECT aggtransfn, "
13828 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13829 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13830 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13831 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13832 "0 AS aggmtranstype, false AS aggfinalextra, "
13833 "false AS aggmfinalextra, "
13834 "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, "
13835 "aggsortop, "
13836 "'n' AS aggkind, "
13837 "0 AS aggtransspace, agginitval, "
13838 "0 AS aggmtransspace, NULL AS aggminitval, "
13839 "true AS convertok, "
13840 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
13841 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
13842 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13843 "WHERE a.aggfnoid = p.oid "
13844 "AND p.oid = '%u'::pg_catalog.oid",
13845 agginfo->aggfn.dobj.catId.oid);
13846 }
13847 else if (fout->remoteVersion >= 80100)
13848 {
13849 appendPQExpBuffer(query, "SELECT aggtransfn, "
13850 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13851 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13852 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13853 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13854 "0 AS aggmtranstype, false AS aggfinalextra, "
13855 "false AS aggmfinalextra, "
13856 "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, "
13857 "aggsortop, "
13858 "'n' AS aggkind, "
13859 "0 AS aggtransspace, agginitval, "
13860 "0 AS aggmtransspace, NULL AS aggminitval, "
13861 "true AS convertok "
13862 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13863 "WHERE a.aggfnoid = p.oid "
13864 "AND p.oid = '%u'::pg_catalog.oid",
13865 agginfo->aggfn.dobj.catId.oid);
13866 }
13867 else
13868 {
13869 appendPQExpBuffer(query, "SELECT aggtransfn, "
13870 "aggfinalfn, aggtranstype::pg_catalog.regtype, "
13871 "'-' AS aggcombinefn, '-' AS aggserialfn, "
13872 "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
13873 "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
13874 "0 AS aggmtranstype, false AS aggfinalextra, "
13875 "false AS aggmfinalextra, "
13876 "'0' AS aggfinalmodify, '0' AS aggmfinalmodify, "
13877 "0 AS aggsortop, "
13878 "'n' AS aggkind, "
13879 "0 AS aggtransspace, agginitval, "
13880 "0 AS aggmtransspace, NULL AS aggminitval, "
13881 "true AS convertok "
13882 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
13883 "WHERE a.aggfnoid = p.oid "
13884 "AND p.oid = '%u'::pg_catalog.oid",
13885 agginfo->aggfn.dobj.catId.oid);
13886 }
13887
13888 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13889
13890 i_aggtransfn = PQfnumber(res, "aggtransfn");
13891 i_aggfinalfn = PQfnumber(res, "aggfinalfn");
13892 i_aggcombinefn = PQfnumber(res, "aggcombinefn");
13893 i_aggserialfn = PQfnumber(res, "aggserialfn");
13894 i_aggdeserialfn = PQfnumber(res, "aggdeserialfn");
13895 i_aggmtransfn = PQfnumber(res, "aggmtransfn");
13896 i_aggminvtransfn = PQfnumber(res, "aggminvtransfn");
13897 i_aggmfinalfn = PQfnumber(res, "aggmfinalfn");
13898 i_aggfinalextra = PQfnumber(res, "aggfinalextra");
13899 i_aggmfinalextra = PQfnumber(res, "aggmfinalextra");
13900 i_aggfinalmodify = PQfnumber(res, "aggfinalmodify");
13901 i_aggmfinalmodify = PQfnumber(res, "aggmfinalmodify");
13902 i_aggsortop = PQfnumber(res, "aggsortop");
13903 i_aggkind = PQfnumber(res, "aggkind");
13904 i_aggtranstype = PQfnumber(res, "aggtranstype");
13905 i_aggtransspace = PQfnumber(res, "aggtransspace");
13906 i_aggmtranstype = PQfnumber(res, "aggmtranstype");
13907 i_aggmtransspace = PQfnumber(res, "aggmtransspace");
13908 i_agginitval = PQfnumber(res, "agginitval");
13909 i_aggminitval = PQfnumber(res, "aggminitval");
13910 i_convertok = PQfnumber(res, "convertok");
13911 i_proparallel = PQfnumber(res, "proparallel");
13912
13913 aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
13914 aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
13915 aggcombinefn = PQgetvalue(res, 0, i_aggcombinefn);
13916 aggserialfn = PQgetvalue(res, 0, i_aggserialfn);
13917 aggdeserialfn = PQgetvalue(res, 0, i_aggdeserialfn);
13918 aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn);
13919 aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn);
13920 aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn);
13921 aggfinalextra = (PQgetvalue(res, 0, i_aggfinalextra)[0] == 't');
13922 aggmfinalextra = (PQgetvalue(res, 0, i_aggmfinalextra)[0] == 't');
13923 aggfinalmodify = PQgetvalue(res, 0, i_aggfinalmodify)[0];
13924 aggmfinalmodify = PQgetvalue(res, 0, i_aggmfinalmodify)[0];
13925 aggsortop = PQgetvalue(res, 0, i_aggsortop);
13926 aggkind = PQgetvalue(res, 0, i_aggkind)[0];
13927 aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
13928 aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
13929 aggmtranstype = PQgetvalue(res, 0, i_aggmtranstype);
13930 aggmtransspace = PQgetvalue(res, 0, i_aggmtransspace);
13931 agginitval = PQgetvalue(res, 0, i_agginitval);
13932 aggminitval = PQgetvalue(res, 0, i_aggminitval);
13933 convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
13934
13935 if (fout->remoteVersion >= 80400)
13936 {
13937 /* 8.4 or later; we rely on server-side code for most of the work */
13938 char *funcargs;
13939 char *funciargs;
13940
13941 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13942 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13943 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
13944 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
13945 }
13946 else
13947 /* pre-8.4, do it ourselves */
13948 aggsig = format_aggregate_signature(agginfo, fout, true);
13949
13950 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
13951
13952 if (i_proparallel != -1)
13953 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13954 else
13955 proparallel = NULL;
13956
13957 if (!convertok)
13958 {
13959 pg_log_warning("aggregate function %s could not be dumped correctly for this database version; ignored",
13960 aggsig);
13961
13962 if (aggfullsig)
13963 free(aggfullsig);
13964
13965 free(aggsig);
13966
13967 return;
13968 }
13969
13970 /* identify default modify flag for aggkind (must match DefineAggregate) */
13971 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
13972 /* replace omitted flags for old versions */
13973 if (aggfinalmodify == '0')
13974 aggfinalmodify = defaultfinalmodify;
13975 if (aggmfinalmodify == '0')
13976 aggmfinalmodify = defaultfinalmodify;
13977
13978 /* regproc and regtype output is already sufficiently quoted */
13979 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
13980 aggtransfn, aggtranstype);
13981
13982 if (strcmp(aggtransspace, "0") != 0)
13983 {
13984 appendPQExpBuffer(details, ",\n SSPACE = %s",
13985 aggtransspace);
13986 }
13987
13988 if (!PQgetisnull(res, 0, i_agginitval))
13989 {
13990 appendPQExpBufferStr(details, ",\n INITCOND = ");
13991 appendStringLiteralAH(details, agginitval, fout);
13992 }
13993
13994 if (strcmp(aggfinalfn, "-") != 0)
13995 {
13996 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
13997 aggfinalfn);
13998 if (aggfinalextra)
13999 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14000 if (aggfinalmodify != defaultfinalmodify)
14001 {
14002 switch (aggfinalmodify)
14003 {
14004 case AGGMODIFY_READ_ONLY:
14005 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14006 break;
14007 case AGGMODIFY_SHAREABLE:
14008 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14009 break;
14010 case AGGMODIFY_READ_WRITE:
14011 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14012 break;
14013 default:
14014 fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14015 agginfo->aggfn.dobj.name);
14016 break;
14017 }
14018 }
14019 }
14020
14021 if (strcmp(aggcombinefn, "-") != 0)
14022 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14023
14024 if (strcmp(aggserialfn, "-") != 0)
14025 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14026
14027 if (strcmp(aggdeserialfn, "-") != 0)
14028 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14029
14030 if (strcmp(aggmtransfn, "-") != 0)
14031 {
14032 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14033 aggmtransfn,
14034 aggminvtransfn,
14035 aggmtranstype);
14036 }
14037
14038 if (strcmp(aggmtransspace, "0") != 0)
14039 {
14040 appendPQExpBuffer(details, ",\n MSSPACE = %s",
14041 aggmtransspace);
14042 }
14043
14044 if (!PQgetisnull(res, 0, i_aggminitval))
14045 {
14046 appendPQExpBufferStr(details, ",\n MINITCOND = ");
14047 appendStringLiteralAH(details, aggminitval, fout);
14048 }
14049
14050 if (strcmp(aggmfinalfn, "-") != 0)
14051 {
14052 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14053 aggmfinalfn);
14054 if (aggmfinalextra)
14055 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14056 if (aggmfinalmodify != defaultfinalmodify)
14057 {
14058 switch (aggmfinalmodify)
14059 {
14060 case AGGMODIFY_READ_ONLY:
14061 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14062 break;
14063 case AGGMODIFY_SHAREABLE:
14064 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14065 break;
14066 case AGGMODIFY_READ_WRITE:
14067 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14068 break;
14069 default:
14070 fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14071 agginfo->aggfn.dobj.name);
14072 break;
14073 }
14074 }
14075 }
14076
14077 aggsortconvop = getFormattedOperatorName(fout, aggsortop);
14078 if (aggsortconvop)
14079 {
14080 appendPQExpBuffer(details, ",\n SORTOP = %s",
14081 aggsortconvop);
14082 free(aggsortconvop);
14083 }
14084
14085 if (aggkind == AGGKIND_HYPOTHETICAL)
14086 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14087
14088 if (proparallel != NULL && proparallel[0] != PROPARALLEL_UNSAFE)
14089 {
14090 if (proparallel[0] == PROPARALLEL_SAFE)
14091 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14092 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14093 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14094 else if (proparallel[0] != PROPARALLEL_UNSAFE)
14095 fatal("unrecognized proparallel value for function \"%s\"",
14096 agginfo->aggfn.dobj.name);
14097 }
14098
14099 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14100 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14101 aggsig);
14102
14103 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14104 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14105 aggfullsig ? aggfullsig : aggsig, details->data);
14106
14107 if (dopt->binary_upgrade)
14108 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14109 "AGGREGATE", aggsig,
14110 agginfo->aggfn.dobj.namespace->dobj.name);
14111
14112 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14113 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14114 agginfo->aggfn.dobj.dumpId,
14115 ARCHIVE_OPTS(.tag = aggsig_tag,
14116 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14117 .owner = agginfo->aggfn.rolname,
14118 .description = "AGGREGATE",
14119 .section = SECTION_PRE_DATA,
14120 .createStmt = q->data,
14121 .dropStmt = delq->data));
14122
14123 /* Dump Aggregate Comments */
14124 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14125 dumpComment(fout, "AGGREGATE", aggsig,
14126 agginfo->aggfn.dobj.namespace->dobj.name,
14127 agginfo->aggfn.rolname,
14128 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14129
14130 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14131 dumpSecLabel(fout, "AGGREGATE", aggsig,
14132 agginfo->aggfn.dobj.namespace->dobj.name,
14133 agginfo->aggfn.rolname,
14134 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14135
14136 /*
14137 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14138 * command look like a function's GRANT; in particular this affects the
14139 * syntax for zero-argument aggregates and ordered-set aggregates.
14140 */
14141 free(aggsig);
14142
14143 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14144
14145 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14146 dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
14147 "FUNCTION", aggsig, NULL,
14148 agginfo->aggfn.dobj.namespace->dobj.name,
14149 agginfo->aggfn.rolname, agginfo->aggfn.proacl,
14150 agginfo->aggfn.rproacl,
14151 agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl);
14152
14153 free(aggsig);
14154 if (aggfullsig)
14155 free(aggfullsig);
14156 free(aggsig_tag);
14157
14158 PQclear(res);
14159
14160 destroyPQExpBuffer(query);
14161 destroyPQExpBuffer(q);
14162 destroyPQExpBuffer(delq);
14163 destroyPQExpBuffer(details);
14164}
14165
14166/*
14167 * dumpTSParser
14168 * write out a single text search parser
14169 */
14170static void
14171dumpTSParser(Archive *fout, TSParserInfo *prsinfo)
14172{
14173 DumpOptions *dopt = fout->dopt;
14174 PQExpBuffer q;
14175 PQExpBuffer delq;
14176 char *qprsname;
14177
14178 /* Skip if not to be dumped */
14179 if (!prsinfo->dobj.dump || dopt->dataOnly)
14180 return;
14181
14182 q = createPQExpBuffer();
14183 delq = createPQExpBuffer();
14184
14185 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14186
14187 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14188 fmtQualifiedDumpable(prsinfo));
14189
14190 appendPQExpBuffer(q, " START = %s,\n",
14191 convertTSFunction(fout, prsinfo->prsstart));
14192 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14193 convertTSFunction(fout, prsinfo->prstoken));
14194 appendPQExpBuffer(q, " END = %s,\n",
14195 convertTSFunction(fout, prsinfo->prsend));
14196 if (prsinfo->prsheadline != InvalidOid)
14197 appendPQExpBuffer(q, " HEADLINE = %s,\n",
14198 convertTSFunction(fout, prsinfo->prsheadline));
14199 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14200 convertTSFunction(fout, prsinfo->prslextype));
14201
14202 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14203 fmtQualifiedDumpable(prsinfo));
14204
14205 if (dopt->binary_upgrade)
14206 binary_upgrade_extension_member(q, &prsinfo->dobj,
14207 "TEXT SEARCH PARSER", qprsname,
14208 prsinfo->dobj.namespace->dobj.name);
14209
14210 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14211 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14212 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14213 .namespace = prsinfo->dobj.namespace->dobj.name,
14214 .description = "TEXT SEARCH PARSER",
14215 .section = SECTION_PRE_DATA,
14216 .createStmt = q->data,
14217 .dropStmt = delq->data));
14218
14219 /* Dump Parser Comments */
14220 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14221 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14222 prsinfo->dobj.namespace->dobj.name, "",
14223 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14224
14225 destroyPQExpBuffer(q);
14226 destroyPQExpBuffer(delq);
14227 free(qprsname);
14228}
14229
14230/*
14231 * dumpTSDictionary
14232 * write out a single text search dictionary
14233 */
14234static void
14235dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo)
14236{
14237 DumpOptions *dopt = fout->dopt;
14238 PQExpBuffer q;
14239 PQExpBuffer delq;
14240 PQExpBuffer query;
14241 char *qdictname;
14242 PGresult *res;
14243 char *nspname;
14244 char *tmplname;
14245
14246 /* Skip if not to be dumped */
14247 if (!dictinfo->dobj.dump || dopt->dataOnly)
14248 return;
14249
14250 q = createPQExpBuffer();
14251 delq = createPQExpBuffer();
14252 query = createPQExpBuffer();
14253
14254 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14255
14256 /* Fetch name and namespace of the dictionary's template */
14257 appendPQExpBuffer(query, "SELECT nspname, tmplname "
14258 "FROM pg_ts_template p, pg_namespace n "
14259 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14260 dictinfo->dicttemplate);
14261 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14262 nspname = PQgetvalue(res, 0, 0);
14263 tmplname = PQgetvalue(res, 0, 1);
14264
14265 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14266 fmtQualifiedDumpable(dictinfo));
14267
14268 appendPQExpBufferStr(q, " TEMPLATE = ");
14269 appendPQExpBuffer(q, "%s.", fmtId(nspname));
14270 appendPQExpBufferStr(q, fmtId(tmplname));
14271
14272 PQclear(res);
14273
14274 /* the dictinitoption can be dumped straight into the command */
14275 if (dictinfo->dictinitoption)
14276 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14277
14278 appendPQExpBufferStr(q, " );\n");
14279
14280 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14281 fmtQualifiedDumpable(dictinfo));
14282
14283 if (dopt->binary_upgrade)
14284 binary_upgrade_extension_member(q, &dictinfo->dobj,
14285 "TEXT SEARCH DICTIONARY", qdictname,
14286 dictinfo->dobj.namespace->dobj.name);
14287
14288 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14289 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14290 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14291 .namespace = dictinfo->dobj.namespace->dobj.name,
14292 .owner = dictinfo->rolname,
14293 .description = "TEXT SEARCH DICTIONARY",
14294 .section = SECTION_PRE_DATA,
14295 .createStmt = q->data,
14296 .dropStmt = delq->data));
14297
14298 /* Dump Dictionary Comments */
14299 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14300 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14301 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14302 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14303
14304 destroyPQExpBuffer(q);
14305 destroyPQExpBuffer(delq);
14306 destroyPQExpBuffer(query);
14307 free(qdictname);
14308}
14309
14310/*
14311 * dumpTSTemplate
14312 * write out a single text search template
14313 */
14314static void
14315dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo)
14316{
14317 DumpOptions *dopt = fout->dopt;
14318 PQExpBuffer q;
14319 PQExpBuffer delq;
14320 char *qtmplname;
14321
14322 /* Skip if not to be dumped */
14323 if (!tmplinfo->dobj.dump || dopt->dataOnly)
14324 return;
14325
14326 q = createPQExpBuffer();
14327 delq = createPQExpBuffer();
14328
14329 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14330
14331 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14332 fmtQualifiedDumpable(tmplinfo));
14333
14334 if (tmplinfo->tmplinit != InvalidOid)
14335 appendPQExpBuffer(q, " INIT = %s,\n",
14336 convertTSFunction(fout, tmplinfo->tmplinit));
14337 appendPQExpBuffer(q, " LEXIZE = %s );\n",
14338 convertTSFunction(fout, tmplinfo->tmpllexize));
14339
14340 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14341 fmtQualifiedDumpable(tmplinfo));
14342
14343 if (dopt->binary_upgrade)
14344 binary_upgrade_extension_member(q, &tmplinfo->dobj,
14345 "TEXT SEARCH TEMPLATE", qtmplname,
14346 tmplinfo->dobj.namespace->dobj.name);
14347
14348 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14349 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14350 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14351 .namespace = tmplinfo->dobj.namespace->dobj.name,
14352 .description = "TEXT SEARCH TEMPLATE",
14353 .section = SECTION_PRE_DATA,
14354 .createStmt = q->data,
14355 .dropStmt = delq->data));
14356
14357 /* Dump Template Comments */
14358 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14359 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14360 tmplinfo->dobj.namespace->dobj.name, "",
14361 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14362
14363 destroyPQExpBuffer(q);
14364 destroyPQExpBuffer(delq);
14365 free(qtmplname);
14366}
14367
14368/*
14369 * dumpTSConfig
14370 * write out a single text search configuration
14371 */
14372static void
14373dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
14374{
14375 DumpOptions *dopt = fout->dopt;
14376 PQExpBuffer q;
14377 PQExpBuffer delq;
14378 PQExpBuffer query;
14379 char *qcfgname;
14380 PGresult *res;
14381 char *nspname;
14382 char *prsname;
14383 int ntups,
14384 i;
14385 int i_tokenname;
14386 int i_dictname;
14387
14388 /* Skip if not to be dumped */
14389 if (!cfginfo->dobj.dump || dopt->dataOnly)
14390 return;
14391
14392 q = createPQExpBuffer();
14393 delq = createPQExpBuffer();
14394 query = createPQExpBuffer();
14395
14396 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14397
14398 /* Fetch name and namespace of the config's parser */
14399 appendPQExpBuffer(query, "SELECT nspname, prsname "
14400 "FROM pg_ts_parser p, pg_namespace n "
14401 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14402 cfginfo->cfgparser);
14403 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14404 nspname = PQgetvalue(res, 0, 0);
14405 prsname = PQgetvalue(res, 0, 1);
14406
14407 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14408 fmtQualifiedDumpable(cfginfo));
14409
14410 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14411 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14412
14413 PQclear(res);
14414
14415 resetPQExpBuffer(query);
14416 appendPQExpBuffer(query,
14417 "SELECT\n"
14418 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14419 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14420 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14421 "FROM pg_catalog.pg_ts_config_map AS m\n"
14422 "WHERE m.mapcfg = '%u'\n"
14423 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14424 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14425
14426 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14427 ntups = PQntuples(res);
14428
14429 i_tokenname = PQfnumber(res, "tokenname");
14430 i_dictname = PQfnumber(res, "dictname");
14431
14432 for (i = 0; i < ntups; i++)
14433 {
14434 char *tokenname = PQgetvalue(res, i, i_tokenname);
14435 char *dictname = PQgetvalue(res, i, i_dictname);
14436
14437 if (i == 0 ||
14438 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14439 {
14440 /* starting a new token type, so start a new command */
14441 if (i > 0)
14442 appendPQExpBufferStr(q, ";\n");
14443 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14444 fmtQualifiedDumpable(cfginfo));
14445 /* tokenname needs quoting, dictname does NOT */
14446 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14447 fmtId(tokenname), dictname);
14448 }
14449 else
14450 appendPQExpBuffer(q, ", %s", dictname);
14451 }
14452
14453 if (ntups > 0)
14454 appendPQExpBufferStr(q, ";\n");
14455
14456 PQclear(res);
14457
14458 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14459 fmtQualifiedDumpable(cfginfo));
14460
14461 if (dopt->binary_upgrade)
14462 binary_upgrade_extension_member(q, &cfginfo->dobj,
14463 "TEXT SEARCH CONFIGURATION", qcfgname,
14464 cfginfo->dobj.namespace->dobj.name);
14465
14466 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14467 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14468 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14469 .namespace = cfginfo->dobj.namespace->dobj.name,
14470 .owner = cfginfo->rolname,
14471 .description = "TEXT SEARCH CONFIGURATION",
14472 .section = SECTION_PRE_DATA,
14473 .createStmt = q->data,
14474 .dropStmt = delq->data));
14475
14476 /* Dump Configuration Comments */
14477 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14478 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14479 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14480 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14481
14482 destroyPQExpBuffer(q);
14483 destroyPQExpBuffer(delq);
14484 destroyPQExpBuffer(query);
14485 free(qcfgname);
14486}
14487
14488/*
14489 * dumpForeignDataWrapper
14490 * write out a single foreign-data wrapper definition
14491 */
14492static void
14493dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
14494{
14495 DumpOptions *dopt = fout->dopt;
14496 PQExpBuffer q;
14497 PQExpBuffer delq;
14498 char *qfdwname;
14499
14500 /* Skip if not to be dumped */
14501 if (!fdwinfo->dobj.dump || dopt->dataOnly)
14502 return;
14503
14504 q = createPQExpBuffer();
14505 delq = createPQExpBuffer();
14506
14507 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14508
14509 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14510 qfdwname);
14511
14512 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14513 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14514
14515 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14516 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14517
14518 if (strlen(fdwinfo->fdwoptions) > 0)
14519 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14520
14521 appendPQExpBufferStr(q, ";\n");
14522
14523 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14524 qfdwname);
14525
14526 if (dopt->binary_upgrade)
14527 binary_upgrade_extension_member(q, &fdwinfo->dobj,
14528 "FOREIGN DATA WRAPPER", qfdwname,
14529 NULL);
14530
14531 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14532 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14533 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14534 .owner = fdwinfo->rolname,
14535 .description = "FOREIGN DATA WRAPPER",
14536 .section = SECTION_PRE_DATA,
14537 .createStmt = q->data,
14538 .dropStmt = delq->data));
14539
14540 /* Dump Foreign Data Wrapper Comments */
14541 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14542 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14543 NULL, fdwinfo->rolname,
14544 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14545
14546 /* Handle the ACL */
14547 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14548 dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14549 "FOREIGN DATA WRAPPER", qfdwname, NULL,
14550 NULL, fdwinfo->rolname,
14551 fdwinfo->fdwacl, fdwinfo->rfdwacl,
14552 fdwinfo->initfdwacl, fdwinfo->initrfdwacl);
14553
14554 free(qfdwname);
14555
14556 destroyPQExpBuffer(q);
14557 destroyPQExpBuffer(delq);
14558}
14559
14560/*
14561 * dumpForeignServer
14562 * write out a foreign server definition
14563 */
14564static void
14565dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
14566{
14567 DumpOptions *dopt = fout->dopt;
14568 PQExpBuffer q;
14569 PQExpBuffer delq;
14570 PQExpBuffer query;
14571 PGresult *res;
14572 char *qsrvname;
14573 char *fdwname;
14574
14575 /* Skip if not to be dumped */
14576 if (!srvinfo->dobj.dump || dopt->dataOnly)
14577 return;
14578
14579 q = createPQExpBuffer();
14580 delq = createPQExpBuffer();
14581 query = createPQExpBuffer();
14582
14583 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14584
14585 /* look up the foreign-data wrapper */
14586 appendPQExpBuffer(query, "SELECT fdwname "
14587 "FROM pg_foreign_data_wrapper w "
14588 "WHERE w.oid = '%u'",
14589 srvinfo->srvfdw);
14590 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14591 fdwname = PQgetvalue(res, 0, 0);
14592
14593 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
14594 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
14595 {
14596 appendPQExpBufferStr(q, " TYPE ");
14597 appendStringLiteralAH(q, srvinfo->srvtype, fout);
14598 }
14599 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
14600 {
14601 appendPQExpBufferStr(q, " VERSION ");
14602 appendStringLiteralAH(q, srvinfo->srvversion, fout);
14603 }
14604
14605 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
14606 appendPQExpBufferStr(q, fmtId(fdwname));
14607
14608 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
14609 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
14610
14611 appendPQExpBufferStr(q, ";\n");
14612
14613 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
14614 qsrvname);
14615
14616 if (dopt->binary_upgrade)
14617 binary_upgrade_extension_member(q, &srvinfo->dobj,
14618 "SERVER", qsrvname, NULL);
14619
14620 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14622 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
14623 .owner = srvinfo->rolname,
14624 .description = "SERVER",
14625 .section = SECTION_PRE_DATA,
14626 .createStmt = q->data,
14627 .dropStmt = delq->data));
14628
14629 /* Dump Foreign Server Comments */
14630 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14631 dumpComment(fout, "SERVER", qsrvname,
14632 NULL, srvinfo->rolname,
14633 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
14634
14635 /* Handle the ACL */
14636 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
14637 dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14638 "FOREIGN SERVER", qsrvname, NULL,
14639 NULL, srvinfo->rolname,
14640 srvinfo->srvacl, srvinfo->rsrvacl,
14641 srvinfo->initsrvacl, srvinfo->initrsrvacl);
14642
14643 /* Dump user mappings */
14644 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
14645 dumpUserMappings(fout,
14646 srvinfo->dobj.name, NULL,
14647 srvinfo->rolname,
14648 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
14649
14650 free(qsrvname);
14651
14652 destroyPQExpBuffer(q);
14653 destroyPQExpBuffer(delq);
14654 destroyPQExpBuffer(query);
14655}
14656
14657/*
14658 * dumpUserMappings
14659 *
14660 * This routine is used to dump any user mappings associated with the
14661 * server handed to this routine. Should be called after ArchiveEntry()
14662 * for the server.
14663 */
14664static void
14665dumpUserMappings(Archive *fout,
14666 const char *servername, const char *namespace,
14667 const char *owner,
14668 CatalogId catalogId, DumpId dumpId)
14669{
14670 PQExpBuffer q;
14671 PQExpBuffer delq;
14672 PQExpBuffer query;
14673 PQExpBuffer tag;
14674 PGresult *res;
14675 int ntups;
14676 int i_usename;
14677 int i_umoptions;
14678 int i;
14679
14680 q = createPQExpBuffer();
14681 tag = createPQExpBuffer();
14682 delq = createPQExpBuffer();
14683 query = createPQExpBuffer();
14684
14685 /*
14686 * We read from the publicly accessible view pg_user_mappings, so as not
14687 * to fail if run by a non-superuser. Note that the view will show
14688 * umoptions as null if the user hasn't got privileges for the associated
14689 * server; this means that pg_dump will dump such a mapping, but with no
14690 * OPTIONS clause. A possible alternative is to skip such mappings
14691 * altogether, but it's not clear that that's an improvement.
14692 */
14693 appendPQExpBuffer(query,
14694 "SELECT usename, "
14695 "array_to_string(ARRAY("
14696 "SELECT quote_ident(option_name) || ' ' || "
14697 "quote_literal(option_value) "
14698 "FROM pg_options_to_table(umoptions) "
14699 "ORDER BY option_name"
14700 "), E',\n ') AS umoptions "
14701 "FROM pg_user_mappings "
14702 "WHERE srvid = '%u' "
14703 "ORDER BY usename",
14704 catalogId.oid);
14705
14706 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14707
14708 ntups = PQntuples(res);
14709 i_usename = PQfnumber(res, "usename");
14710 i_umoptions = PQfnumber(res, "umoptions");
14711
14712 for (i = 0; i < ntups; i++)
14713 {
14714 char *usename;
14715 char *umoptions;
14716
14717 usename = PQgetvalue(res, i, i_usename);
14718 umoptions = PQgetvalue(res, i, i_umoptions);
14719
14720 resetPQExpBuffer(q);
14721 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
14722 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
14723
14724 if (umoptions && strlen(umoptions) > 0)
14725 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
14726
14727 appendPQExpBufferStr(q, ";\n");
14728
14729 resetPQExpBuffer(delq);
14730 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
14731 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
14732
14733 resetPQExpBuffer(tag);
14734 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
14735 usename, servername);
14736
14737 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14738 ARCHIVE_OPTS(.tag = tag->data,
14739 .namespace = namespace,
14740 .owner = owner,
14741 .description = "USER MAPPING",
14742 .section = SECTION_PRE_DATA,
14743 .createStmt = q->data,
14744 .dropStmt = delq->data));
14745 }
14746
14747 PQclear(res);
14748
14749 destroyPQExpBuffer(query);
14750 destroyPQExpBuffer(delq);
14751 destroyPQExpBuffer(tag);
14752 destroyPQExpBuffer(q);
14753}
14754
14755/*
14756 * Write out default privileges information
14757 */
14758static void
14759dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
14760{
14761 DumpOptions *dopt = fout->dopt;
14762 PQExpBuffer q;
14763 PQExpBuffer tag;
14764 const char *type;
14765
14766 /* Skip if not to be dumped */
14767 if (!daclinfo->dobj.dump || dopt->dataOnly || dopt->aclsSkip)
14768 return;
14769
14770 q = createPQExpBuffer();
14771 tag = createPQExpBuffer();
14772
14773 switch (daclinfo->defaclobjtype)
14774 {
14775 case DEFACLOBJ_RELATION:
14776 type = "TABLES";
14777 break;
14778 case DEFACLOBJ_SEQUENCE:
14779 type = "SEQUENCES";
14780 break;
14781 case DEFACLOBJ_FUNCTION:
14782 type = "FUNCTIONS";
14783 break;
14784 case DEFACLOBJ_TYPE:
14785 type = "TYPES";
14786 break;
14787 case DEFACLOBJ_NAMESPACE:
14788 type = "SCHEMAS";
14789 break;
14790 default:
14791 /* shouldn't get here */
14792 fatal("unrecognized object type in default privileges: %d",
14793 (int) daclinfo->defaclobjtype);
14794 type = ""; /* keep compiler quiet */
14795 }
14796
14797 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
14798
14799 /* build the actual command(s) for this tuple */
14800 if (!buildDefaultACLCommands(type,
14801 daclinfo->dobj.namespace != NULL ?
14802 daclinfo->dobj.namespace->dobj.name : NULL,
14803 daclinfo->defaclacl,
14804 daclinfo->rdefaclacl,
14805 daclinfo->initdefaclacl,
14806 daclinfo->initrdefaclacl,
14807 daclinfo->defaclrole,
14808 fout->remoteVersion,
14809 q))
14810 fatal("could not parse default ACL list (%s)",
14811 daclinfo->defaclacl);
14812
14813 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
14814 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
14815 ARCHIVE_OPTS(.tag = tag->data,
14816 .namespace = daclinfo->dobj.namespace ?
14817 daclinfo->dobj.namespace->dobj.name : NULL,
14818 .owner = daclinfo->defaclrole,
14819 .description = "DEFAULT ACL",
14820 .section = SECTION_POST_DATA,
14821 .createStmt = q->data));
14822
14823 destroyPQExpBuffer(tag);
14824 destroyPQExpBuffer(q);
14825}
14826
14827/*----------
14828 * Write out grant/revoke information
14829 *
14830 * 'objCatId' is the catalog ID of the underlying object.
14831 * 'objDumpId' is the dump ID of the underlying object.
14832 * 'type' must be one of
14833 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
14834 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
14835 * 'name' is the formatted name of the object. Must be quoted etc. already.
14836 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
14837 * (Currently we assume that subname is only provided for table columns.)
14838 * 'nspname' is the namespace the object is in (NULL if none).
14839 * 'owner' is the owner, NULL if there is no owner (for languages).
14840 * 'acls' contains the ACL string of the object from the appropriate system
14841 * catalog field; it will be passed to buildACLCommands for building the
14842 * appropriate GRANT commands.
14843 * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the
14844 * object; it will be passed to buildACLCommands for building the
14845 * appropriate REVOKE commands.
14846 * 'initacls' In binary-upgrade mode, ACL string of the object's initial
14847 * privileges, to be recorded into pg_init_privs
14848 * 'initracls' In binary-upgrade mode, ACL string of the object's
14849 * revoked-from-default privileges, to be recorded into pg_init_privs
14850 *
14851 * NB: initacls/initracls are needed because extensions can set privileges on
14852 * an object during the extension's script file and we record those into
14853 * pg_init_privs as that object's initial privileges.
14854 *----------
14855 */
14856static void
14857dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
14858 const char *type, const char *name, const char *subname,
14859 const char *nspname, const char *owner,
14860 const char *acls, const char *racls,
14861 const char *initacls, const char *initracls)
14862{
14863 DumpOptions *dopt = fout->dopt;
14864 PQExpBuffer sql;
14865
14866 /* Do nothing if ACL dump is not enabled */
14867 if (dopt->aclsSkip)
14868 return;
14869
14870 /* --data-only skips ACLs *except* BLOB ACLs */
14871 if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
14872 return;
14873
14874 sql = createPQExpBuffer();
14875
14876 /*
14877 * Check to see if this object has had any initial ACLs included for it.
14878 * If so, we are in binary upgrade mode and these are the ACLs to turn
14879 * into GRANT and REVOKE statements to set and record the initial
14880 * privileges for an extension object. Let the backend know that these
14881 * are to be recorded by calling binary_upgrade_set_record_init_privs()
14882 * before and after.
14883 */
14884 if (strlen(initacls) != 0 || strlen(initracls) != 0)
14885 {
14886 appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
14887 if (!buildACLCommands(name, subname, nspname, type,
14888 initacls, initracls, owner,
14889 "", fout->remoteVersion, sql))
14890 fatal("could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)",
14891 initacls, initracls, name, type);
14892 appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
14893 }
14894
14895 if (!buildACLCommands(name, subname, nspname, type,
14896 acls, racls, owner,
14897 "", fout->remoteVersion, sql))
14898 fatal("could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)",
14899 acls, racls, name, type);
14900
14901 if (sql->len > 0)
14902 {
14903 PQExpBuffer tag = createPQExpBuffer();
14904
14905 if (subname)
14906 appendPQExpBuffer(tag, "COLUMN %s.%s", name, subname);
14907 else
14908 appendPQExpBuffer(tag, "%s %s", type, name);
14909
14910 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14911 ARCHIVE_OPTS(.tag = tag->data,
14912 .namespace = nspname,
14913 .owner = owner,
14914 .description = "ACL",
14915 .section = SECTION_NONE,
14916 .createStmt = sql->data,
14917 .deps = &objDumpId,
14918 .nDeps = 1));
14919 destroyPQExpBuffer(tag);
14920 }
14921
14922 destroyPQExpBuffer(sql);
14923}
14924
14925/*
14926 * dumpSecLabel
14927 *
14928 * This routine is used to dump any security labels associated with the
14929 * object handed to this routine. The routine takes the object type
14930 * and object name (ready to print, except for schema decoration), plus
14931 * the namespace and owner of the object (for labeling the ArchiveEntry),
14932 * plus catalog ID and subid which are the lookup key for pg_seclabel,
14933 * plus the dump ID for the object (for setting a dependency).
14934 * If a matching pg_seclabel entry is found, it is dumped.
14935 *
14936 * Note: although this routine takes a dumpId for dependency purposes,
14937 * that purpose is just to mark the dependency in the emitted dump file
14938 * for possible future use by pg_restore. We do NOT use it for determining
14939 * ordering of the label in the dump file, because this routine is called
14940 * after dependency sorting occurs. This routine should be called just after
14941 * calling ArchiveEntry() for the specified object.
14942 */
14943static void
14944dumpSecLabel(Archive *fout, const char *type, const char *name,
14945 const char *namespace, const char *owner,
14946 CatalogId catalogId, int subid, DumpId dumpId)
14947{
14948 DumpOptions *dopt = fout->dopt;
14949 SecLabelItem *labels;
14950 int nlabels;
14951 int i;
14952 PQExpBuffer query;
14953
14954 /* do nothing, if --no-security-labels is supplied */
14955 if (dopt->no_security_labels)
14956 return;
14957
14958 /* Security labels are schema not data ... except blob labels are data */
14959 if (strcmp(type, "LARGE OBJECT") != 0)
14960 {
14961 if (dopt->dataOnly)
14962 return;
14963 }
14964 else
14965 {
14966 /* We do dump blob security labels in binary-upgrade mode */
14967 if (dopt->schemaOnly && !dopt->binary_upgrade)
14968 return;
14969 }
14970
14971 /* Search for security labels associated with catalogId, using table */
14972 nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels);
14973
14974 query = createPQExpBuffer();
14975
14976 for (i = 0; i < nlabels; i++)
14977 {
14978 /*
14979 * Ignore label entries for which the subid doesn't match.
14980 */
14981 if (labels[i].objsubid != subid)
14982 continue;
14983
14984 appendPQExpBuffer(query,
14985 "SECURITY LABEL FOR %s ON %s ",
14986 fmtId(labels[i].provider), type);
14987 if (namespace && *namespace)
14988 appendPQExpBuffer(query, "%s.", fmtId(namespace));
14989 appendPQExpBuffer(query, "%s IS ", name);
14990 appendStringLiteralAH(query, labels[i].label, fout);
14991 appendPQExpBufferStr(query, ";\n");
14992 }
14993
14994 if (query->len > 0)
14995 {
14996 PQExpBuffer tag = createPQExpBuffer();
14997
14998 appendPQExpBuffer(tag, "%s %s", type, name);
14999 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15000 ARCHIVE_OPTS(.tag = tag->data,
15001 .namespace = namespace,
15002 .owner = owner,
15003 .description = "SECURITY LABEL",
15004 .section = SECTION_NONE,
15005 .createStmt = query->data,
15006 .deps = &dumpId,
15007 .nDeps = 1));
15008 destroyPQExpBuffer(tag);
15009 }
15010
15011 destroyPQExpBuffer(query);
15012}
15013
15014/*
15015 * dumpTableSecLabel
15016 *
15017 * As above, but dump security label for both the specified table (or view)
15018 * and its columns.
15019 */
15020static void
15021dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename)
15022{
15023 DumpOptions *dopt = fout->dopt;
15024 SecLabelItem *labels;
15025 int nlabels;
15026 int i;
15027 PQExpBuffer query;
15028 PQExpBuffer target;
15029
15030 /* do nothing, if --no-security-labels is supplied */
15031 if (dopt->no_security_labels)
15032 return;
15033
15034 /* SecLabel are SCHEMA not data */
15035 if (dopt->dataOnly)
15036 return;
15037
15038 /* Search for comments associated with relation, using table */
15039 nlabels = findSecLabels(fout,
15040 tbinfo->dobj.catId.tableoid,
15041 tbinfo->dobj.catId.oid,
15042 &labels);
15043
15044 /* If security labels exist, build SECURITY LABEL statements */
15045 if (nlabels <= 0)
15046 return;
15047
15048 query = createPQExpBuffer();
15049 target = createPQExpBuffer();
15050
15051 for (i = 0; i < nlabels; i++)
15052 {
15053 const char *colname;
15054 const char *provider = labels[i].provider;
15055 const char *label = labels[i].label;
15056 int objsubid = labels[i].objsubid;
15057
15058 resetPQExpBuffer(target);
15059 if (objsubid == 0)
15060 {
15061 appendPQExpBuffer(target, "%s %s", reltypename,
15062 fmtQualifiedDumpable(tbinfo));
15063 }
15064 else
15065 {
15066 colname = getAttrName(objsubid, tbinfo);
15067 /* first fmtXXX result must be consumed before calling again */
15068 appendPQExpBuffer(target, "COLUMN %s",
15069 fmtQualifiedDumpable(tbinfo));
15070 appendPQExpBuffer(target, ".%s", fmtId(colname));
15071 }
15072 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15073 fmtId(provider), target->data);
15074 appendStringLiteralAH(query, label, fout);
15075 appendPQExpBufferStr(query, ";\n");
15076 }
15077 if (query->len > 0)
15078 {
15079 resetPQExpBuffer(target);
15080 appendPQExpBuffer(target, "%s %s", reltypename,
15081 fmtId(tbinfo->dobj.name));
15082 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15083 ARCHIVE_OPTS(.tag = target->data,
15084 .namespace = tbinfo->dobj.namespace->dobj.name,
15085 .owner = tbinfo->rolname,
15086 .description = "SECURITY LABEL",
15087 .section = SECTION_NONE,
15088 .createStmt = query->data,
15089 .deps = &(tbinfo->dobj.dumpId),
15090 .nDeps = 1));
15091 }
15092 destroyPQExpBuffer(query);
15093 destroyPQExpBuffer(target);
15094}
15095
15096/*
15097 * findSecLabels
15098 *
15099 * Find the security label(s), if any, associated with the given object.
15100 * All the objsubid values associated with the given classoid/objoid are
15101 * found with one search.
15102 */
15103static int
15104findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
15105{
15106 /* static storage for table of security labels */
15107 static SecLabelItem *labels = NULL;
15108 static int nlabels = -1;
15109
15110 SecLabelItem *middle = NULL;
15111 SecLabelItem *low;
15112 SecLabelItem *high;
15113 int nmatch;
15114
15115 /* Get security labels if we didn't already */
15116 if (nlabels < 0)
15117 nlabels = collectSecLabels(fout, &labels);
15118
15119 if (nlabels <= 0) /* no labels, so no match is possible */
15120 {
15121 *items = NULL;
15122 return 0;
15123 }
15124
15125 /*
15126 * Do binary search to find some item matching the object.
15127 */
15128 low = &labels[0];
15129 high = &labels[nlabels - 1];
15130 while (low <= high)
15131 {
15132 middle = low + (high - low) / 2;
15133
15134 if (classoid < middle->classoid)
15135 high = middle - 1;
15136 else if (classoid > middle->classoid)
15137 low = middle + 1;
15138 else if (objoid < middle->objoid)
15139 high = middle - 1;
15140 else if (objoid > middle->objoid)
15141 low = middle + 1;
15142 else
15143 break; /* found a match */
15144 }
15145
15146 if (low > high) /* no matches */
15147 {
15148 *items = NULL;
15149 return 0;
15150 }
15151
15152 /*
15153 * Now determine how many items match the object. The search loop
15154 * invariant still holds: only items between low and high inclusive could
15155 * match.
15156 */
15157 nmatch = 1;
15158 while (middle > low)
15159 {
15160 if (classoid != middle[-1].classoid ||
15161 objoid != middle[-1].objoid)
15162 break;
15163 middle--;
15164 nmatch++;
15165 }
15166
15167 *items = middle;
15168
15169 middle += nmatch;
15170 while (middle <= high)
15171 {
15172 if (classoid != middle->classoid ||
15173 objoid != middle->objoid)
15174 break;
15175 middle++;
15176 nmatch++;
15177 }
15178
15179 return nmatch;
15180}
15181
15182/*
15183 * collectSecLabels
15184 *
15185 * Construct a table of all security labels available for database objects.
15186 * It's much faster to pull them all at once.
15187 *
15188 * The table is sorted by classoid/objid/objsubid for speed in lookup.
15189 */
15190static int
15191collectSecLabels(Archive *fout, SecLabelItem **items)
15192{
15193 PGresult *res;
15194 PQExpBuffer query;
15195 int i_label;
15196 int i_provider;
15197 int i_classoid;
15198 int i_objoid;
15199 int i_objsubid;
15200 int ntups;
15201 int i;
15202 SecLabelItem *labels;
15203
15204 query = createPQExpBuffer();
15205
15206 appendPQExpBufferStr(query,
15207 "SELECT label, provider, classoid, objoid, objsubid "
15208 "FROM pg_catalog.pg_seclabel "
15209 "ORDER BY classoid, objoid, objsubid");
15210
15211 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15212
15213 /* Construct lookup table containing OIDs in numeric form */
15214 i_label = PQfnumber(res, "label");
15215 i_provider = PQfnumber(res, "provider");
15216 i_classoid = PQfnumber(res, "classoid");
15217 i_objoid = PQfnumber(res, "objoid");
15218 i_objsubid = PQfnumber(res, "objsubid");
15219
15220 ntups = PQntuples(res);
15221
15222 labels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15223
15224 for (i = 0; i < ntups; i++)
15225 {
15226 labels[i].label = PQgetvalue(res, i, i_label);
15227 labels[i].provider = PQgetvalue(res, i, i_provider);
15228 labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
15229 labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
15230 labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
15231 }
15232
15233 /* Do NOT free the PGresult since we are keeping pointers into it */
15234 destroyPQExpBuffer(query);
15235
15236 *items = labels;
15237 return ntups;
15238}
15239
15240/*
15241 * dumpTable
15242 * write out to fout the declarations (not data) of a user-defined table
15243 */
15244static void
15245dumpTable(Archive *fout, TableInfo *tbinfo)
15246{
15247 DumpOptions *dopt = fout->dopt;
15248 char *namecopy;
15249
15250 /*
15251 * noop if we are not dumping anything about this table, or if we are
15252 * doing a data-only dump
15253 */
15254 if (!tbinfo->dobj.dump || dopt->dataOnly)
15255 return;
15256
15257 if (tbinfo->relkind == RELKIND_SEQUENCE)
15258 dumpSequence(fout, tbinfo);
15259 else
15260 dumpTableSchema(fout, tbinfo);
15261
15262 /* Handle the ACL here */
15263 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15264 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15265 {
15266 const char *objtype =
15267 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15268
15269 dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
15270 objtype, namecopy, NULL,
15271 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15272 tbinfo->relacl, tbinfo->rrelacl,
15273 tbinfo->initrelacl, tbinfo->initrrelacl);
15274 }
15275
15276 /*
15277 * Handle column ACLs, if any. Note: we pull these with a separate query
15278 * rather than trying to fetch them during getTableAttrs, so that we won't
15279 * miss ACLs on system columns.
15280 */
15281 if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15282 {
15283 PQExpBuffer query = createPQExpBuffer();
15284 PGresult *res;
15285 int i;
15286
15287 if (fout->remoteVersion >= 90600)
15288 {
15289 PQExpBuffer acl_subquery = createPQExpBuffer();
15290 PQExpBuffer racl_subquery = createPQExpBuffer();
15291 PQExpBuffer initacl_subquery = createPQExpBuffer();
15292 PQExpBuffer initracl_subquery = createPQExpBuffer();
15293
15294 buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
15295 initracl_subquery, "at.attacl", "c.relowner", "'c'",
15296 dopt->binary_upgrade);
15297
15298 appendPQExpBuffer(query,
15299 "SELECT at.attname, "
15300 "%s AS attacl, "
15301 "%s AS rattacl, "
15302 "%s AS initattacl, "
15303 "%s AS initrattacl "
15304 "FROM pg_catalog.pg_attribute at "
15305 "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) "
15306 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15307 "(at.attrelid = pip.objoid "
15308 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15309 "AND at.attnum = pip.objsubid) "
15310 "WHERE at.attrelid = '%u'::pg_catalog.oid AND "
15311 "NOT at.attisdropped "
15312 "AND ("
15313 "%s IS NOT NULL OR "
15314 "%s IS NOT NULL OR "
15315 "%s IS NOT NULL OR "
15316 "%s IS NOT NULL)"
15317 "ORDER BY at.attnum",
15318 acl_subquery->data,
15319 racl_subquery->data,
15320 initacl_subquery->data,
15321 initracl_subquery->data,
15322 tbinfo->dobj.catId.oid,
15323 acl_subquery->data,
15324 racl_subquery->data,
15325 initacl_subquery->data,
15326 initracl_subquery->data);
15327
15328 destroyPQExpBuffer(acl_subquery);
15329 destroyPQExpBuffer(racl_subquery);
15330 destroyPQExpBuffer(initacl_subquery);
15331 destroyPQExpBuffer(initracl_subquery);
15332 }
15333 else
15334 {
15335 appendPQExpBuffer(query,
15336 "SELECT attname, attacl, NULL as rattacl, "
15337 "NULL AS initattacl, NULL AS initrattacl "
15338 "FROM pg_catalog.pg_attribute "
15339 "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped "
15340 "AND attacl IS NOT NULL "
15341 "ORDER BY attnum",
15342 tbinfo->dobj.catId.oid);
15343 }
15344
15345 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15346
15347 for (i = 0; i < PQntuples(res); i++)
15348 {
15349 char *attname = PQgetvalue(res, i, 0);
15350 char *attacl = PQgetvalue(res, i, 1);
15351 char *rattacl = PQgetvalue(res, i, 2);
15352 char *initattacl = PQgetvalue(res, i, 3);
15353 char *initrattacl = PQgetvalue(res, i, 4);
15354 char *attnamecopy;
15355
15356 attnamecopy = pg_strdup(fmtId(attname));
15357 /* Column's GRANT type is always TABLE */
15358 dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
15359 "TABLE", namecopy, attnamecopy,
15360 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15361 attacl, rattacl, initattacl, initrattacl);
15362 free(attnamecopy);
15363 }
15364 PQclear(res);
15365 destroyPQExpBuffer(query);
15366 }
15367
15368 free(namecopy);
15369
15370 return;
15371}
15372
15373/*
15374 * Create the AS clause for a view or materialized view. The semicolon is
15375 * stripped because a materialized view must add a WITH NO DATA clause.
15376 *
15377 * This returns a new buffer which must be freed by the caller.
15378 */
15379static PQExpBuffer
15380createViewAsClause(Archive *fout, TableInfo *tbinfo)
15381{
15382 PQExpBuffer query = createPQExpBuffer();
15383 PQExpBuffer result = createPQExpBuffer();
15384 PGresult *res;
15385 int len;
15386
15387 /* Fetch the view definition */
15388 appendPQExpBuffer(query,
15389 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15390 tbinfo->dobj.catId.oid);
15391
15392 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15393
15394 if (PQntuples(res) != 1)
15395 {
15396 if (PQntuples(res) < 1)
15397 fatal("query to obtain definition of view \"%s\" returned no data",
15398 tbinfo->dobj.name);
15399 else
15400 fatal("query to obtain definition of view \"%s\" returned more than one definition",
15401 tbinfo->dobj.name);
15402 }
15403
15404 len = PQgetlength(res, 0, 0);
15405
15406 if (len == 0)
15407 fatal("definition of view \"%s\" appears to be empty (length zero)",
15408 tbinfo->dobj.name);
15409
15410 /* Strip off the trailing semicolon so that other things may follow. */
15411 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15412 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15413
15414 PQclear(res);
15415 destroyPQExpBuffer(query);
15416
15417 return result;
15418}
15419
15420/*
15421 * Create a dummy AS clause for a view. This is used when the real view
15422 * definition has to be postponed because of circular dependencies.
15423 * We must duplicate the view's external properties -- column names and types
15424 * (including collation) -- so that it works for subsequent references.
15425 *
15426 * This returns a new buffer which must be freed by the caller.
15427 */
15428static PQExpBuffer
15429createDummyViewAsClause(Archive *fout, TableInfo *tbinfo)
15430{
15431 PQExpBuffer result = createPQExpBuffer();
15432 int j;
15433
15434 appendPQExpBufferStr(result, "SELECT");
15435
15436 for (j = 0; j < tbinfo->numatts; j++)
15437 {
15438 if (j > 0)
15439 appendPQExpBufferChar(result, ',');
15440 appendPQExpBufferStr(result, "\n ");
15441
15442 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15443
15444 /*
15445 * Must add collation if not default for the type, because CREATE OR
15446 * REPLACE VIEW won't change it
15447 */
15448 if (OidIsValid(tbinfo->attcollation[j]))
15449 {
15450 CollInfo *coll;
15451
15452 coll = findCollationByOid(tbinfo->attcollation[j]);
15453 if (coll)
15454 appendPQExpBuffer(result, " COLLATE %s",
15455 fmtQualifiedDumpable(coll));
15456 }
15457
15458 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15459 }
15460
15461 return result;
15462}
15463
15464/*
15465 * dumpTableSchema
15466 * write the declaration (not data) of one user-defined table or view
15467 */
15468static void
15469dumpTableSchema(Archive *fout, TableInfo *tbinfo)
15470{
15471 DumpOptions *dopt = fout->dopt;
15472 PQExpBuffer q = createPQExpBuffer();
15473 PQExpBuffer delq = createPQExpBuffer();
15474 char *qrelname;
15475 char *qualrelname;
15476 int numParents;
15477 TableInfo **parents;
15478 int actual_atts; /* number of attrs in this CREATE statement */
15479 const char *reltypename;
15480 char *storage;
15481 int j,
15482 k;
15483
15484 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
15485 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15486
15487
15488 if (tbinfo->hasoids)
15489 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
15490 qrelname);
15491
15492 if (dopt->binary_upgrade)
15493 binary_upgrade_set_type_oids_by_rel_oid(fout, q,
15494 tbinfo->dobj.catId.oid);
15495
15496 /* Is it a table or a view? */
15497 if (tbinfo->relkind == RELKIND_VIEW)
15498 {
15499 PQExpBuffer result;
15500
15501 /*
15502 * Note: keep this code in sync with the is_view case in dumpRule()
15503 */
15504
15505 reltypename = "VIEW";
15506
15507 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
15508
15509 if (dopt->binary_upgrade)
15510 binary_upgrade_set_pg_class_oids(fout, q,
15511 tbinfo->dobj.catId.oid, false);
15512
15513 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
15514
15515 if (tbinfo->dummy_view)
15516 result = createDummyViewAsClause(fout, tbinfo);
15517 else
15518 {
15519 if (nonemptyReloptions(tbinfo->reloptions))
15520 {
15521 appendPQExpBufferStr(q, " WITH (");
15522 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15523 appendPQExpBufferChar(q, ')');
15524 }
15525 result = createViewAsClause(fout, tbinfo);
15526 }
15527 appendPQExpBuffer(q, " AS\n%s", result->data);
15528 destroyPQExpBuffer(result);
15529
15530 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
15531 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
15532 appendPQExpBufferStr(q, ";\n");
15533 }
15534 else
15535 {
15536 char *ftoptions = NULL;
15537 char *srvname = NULL;
15538
15539 switch (tbinfo->relkind)
15540 {
15541 case RELKIND_FOREIGN_TABLE:
15542 {
15543 PQExpBuffer query = createPQExpBuffer();
15544 PGresult *res;
15545 int i_srvname;
15546 int i_ftoptions;
15547
15548 reltypename = "FOREIGN TABLE";
15549
15550 /* retrieve name of foreign server and generic options */
15551 appendPQExpBuffer(query,
15552 "SELECT fs.srvname, "
15553 "pg_catalog.array_to_string(ARRAY("
15554 "SELECT pg_catalog.quote_ident(option_name) || "
15555 "' ' || pg_catalog.quote_literal(option_value) "
15556 "FROM pg_catalog.pg_options_to_table(ftoptions) "
15557 "ORDER BY option_name"
15558 "), E',\n ') AS ftoptions "
15559 "FROM pg_catalog.pg_foreign_table ft "
15560 "JOIN pg_catalog.pg_foreign_server fs "
15561 "ON (fs.oid = ft.ftserver) "
15562 "WHERE ft.ftrelid = '%u'",
15563 tbinfo->dobj.catId.oid);
15564 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15565 i_srvname = PQfnumber(res, "srvname");
15566 i_ftoptions = PQfnumber(res, "ftoptions");
15567 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
15568 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
15569 PQclear(res);
15570 destroyPQExpBuffer(query);
15571 break;
15572 }
15573 case RELKIND_MATVIEW:
15574 reltypename = "MATERIALIZED VIEW";
15575 break;
15576 default:
15577 reltypename = "TABLE";
15578 }
15579
15580 numParents = tbinfo->numParents;
15581 parents = tbinfo->parents;
15582
15583 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
15584
15585 if (dopt->binary_upgrade)
15586 binary_upgrade_set_pg_class_oids(fout, q,
15587 tbinfo->dobj.catId.oid, false);
15588
15589 appendPQExpBuffer(q, "CREATE %s%s %s",
15590 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
15591 "UNLOGGED " : "",
15592 reltypename,
15593 qualrelname);
15594
15595 /*
15596 * Attach to type, if reloftype; except in case of a binary upgrade,
15597 * we dump the table normally and attach it to the type afterward.
15598 */
15599 if (tbinfo->reloftype && !dopt->binary_upgrade)
15600 appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
15601
15602 if (tbinfo->relkind != RELKIND_MATVIEW)
15603 {
15604 /* Dump the attributes */
15605 actual_atts = 0;
15606 for (j = 0; j < tbinfo->numatts; j++)
15607 {
15608 /*
15609 * Normally, dump if it's locally defined in this table, and
15610 * not dropped. But for binary upgrade, we'll dump all the
15611 * columns, and then fix up the dropped and nonlocal cases
15612 * below.
15613 */
15614 if (shouldPrintColumn(dopt, tbinfo, j))
15615 {
15616 bool print_default;
15617 bool print_notnull;
15618
15619 /*
15620 * Default value --- suppress if to be printed separately.
15621 */
15622 print_default = (tbinfo->attrdefs[j] != NULL &&
15623 !tbinfo->attrdefs[j]->separate);
15624
15625 /*
15626 * Not Null constraint --- suppress if inherited, except
15627 * if partition, or in binary-upgrade case where that
15628 * won't work.
15629 */
15630 print_notnull = (tbinfo->notnull[j] &&
15631 (!tbinfo->inhNotNull[j] ||
15632 tbinfo->ispartition || dopt->binary_upgrade));
15633
15634 /*
15635 * Skip column if fully defined by reloftype, except in
15636 * binary upgrade
15637 */
15638 if (tbinfo->reloftype && !print_default && !print_notnull &&
15639 !dopt->binary_upgrade)
15640 continue;
15641
15642 /* Format properly if not first attr */
15643 if (actual_atts == 0)
15644 appendPQExpBufferStr(q, " (");
15645 else
15646 appendPQExpBufferChar(q, ',');
15647 appendPQExpBufferStr(q, "\n ");
15648 actual_atts++;
15649
15650 /* Attribute name */
15651 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
15652
15653 if (tbinfo->attisdropped[j])
15654 {
15655 /*
15656 * ALTER TABLE DROP COLUMN clears
15657 * pg_attribute.atttypid, so we will not have gotten a
15658 * valid type name; insert INTEGER as a stopgap. We'll
15659 * clean things up later.
15660 */
15661 appendPQExpBufferStr(q, " INTEGER /* dummy */");
15662 /* and skip to the next column */
15663 continue;
15664 }
15665
15666 /*
15667 * Attribute type; print it except when creating a typed
15668 * table ('OF type_name'), but in binary-upgrade mode,
15669 * print it in that case too.
15670 */
15671 if (dopt->binary_upgrade || !tbinfo->reloftype)
15672 {
15673 appendPQExpBuffer(q, " %s",
15674 tbinfo->atttypnames[j]);
15675 }
15676
15677 if (print_default)
15678 {
15679 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
15680 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
15681 tbinfo->attrdefs[j]->adef_expr);
15682 else
15683 appendPQExpBuffer(q, " DEFAULT %s",
15684 tbinfo->attrdefs[j]->adef_expr);
15685 }
15686
15687
15688 if (print_notnull)
15689 appendPQExpBufferStr(q, " NOT NULL");
15690
15691 /* Add collation if not default for the type */
15692 if (OidIsValid(tbinfo->attcollation[j]))
15693 {
15694 CollInfo *coll;
15695
15696 coll = findCollationByOid(tbinfo->attcollation[j]);
15697 if (coll)
15698 appendPQExpBuffer(q, " COLLATE %s",
15699 fmtQualifiedDumpable(coll));
15700 }
15701 }
15702 }
15703
15704 /*
15705 * Add non-inherited CHECK constraints, if any.
15706 *
15707 * For partitions, we need to include check constraints even if
15708 * they're not defined locally, because the ALTER TABLE ATTACH
15709 * PARTITION that we'll emit later expects the constraint to be
15710 * there. (No need to fix conislocal: ATTACH PARTITION does that)
15711 */
15712 for (j = 0; j < tbinfo->ncheck; j++)
15713 {
15714 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
15715
15716 if (constr->separate ||
15717 (!constr->conislocal && !tbinfo->ispartition))
15718 continue;
15719
15720 if (actual_atts == 0)
15721 appendPQExpBufferStr(q, " (\n ");
15722 else
15723 appendPQExpBufferStr(q, ",\n ");
15724
15725 appendPQExpBuffer(q, "CONSTRAINT %s ",
15726 fmtId(constr->dobj.name));
15727 appendPQExpBufferStr(q, constr->condef);
15728
15729 actual_atts++;
15730 }
15731
15732 if (actual_atts)
15733 appendPQExpBufferStr(q, "\n)");
15734 else if (!(tbinfo->reloftype && !dopt->binary_upgrade))
15735 {
15736 /*
15737 * No attributes? we must have a parenthesized attribute list,
15738 * even though empty, when not using the OF TYPE syntax.
15739 */
15740 appendPQExpBufferStr(q, " (\n)");
15741 }
15742
15743 /*
15744 * Emit the INHERITS clause (not for partitions), except in
15745 * binary-upgrade mode.
15746 */
15747 if (numParents > 0 && !tbinfo->ispartition &&
15748 !dopt->binary_upgrade)
15749 {
15750 appendPQExpBufferStr(q, "\nINHERITS (");
15751 for (k = 0; k < numParents; k++)
15752 {
15753 TableInfo *parentRel = parents[k];
15754
15755 if (k > 0)
15756 appendPQExpBufferStr(q, ", ");
15757 appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
15758 }
15759 appendPQExpBufferChar(q, ')');
15760 }
15761
15762 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
15763 appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef);
15764
15765 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
15766 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
15767 }
15768
15769 if (nonemptyReloptions(tbinfo->reloptions) ||
15770 nonemptyReloptions(tbinfo->toast_reloptions))
15771 {
15772 bool addcomma = false;
15773
15774 appendPQExpBufferStr(q, "\nWITH (");
15775 if (nonemptyReloptions(tbinfo->reloptions))
15776 {
15777 addcomma = true;
15778 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15779 }
15780 if (nonemptyReloptions(tbinfo->toast_reloptions))
15781 {
15782 if (addcomma)
15783 appendPQExpBufferStr(q, ", ");
15784 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
15785 fout);
15786 }
15787 appendPQExpBufferChar(q, ')');
15788 }
15789
15790 /* Dump generic options if any */
15791 if (ftoptions && ftoptions[0])
15792 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
15793
15794 /*
15795 * For materialized views, create the AS clause just like a view. At
15796 * this point, we always mark the view as not populated.
15797 */
15798 if (tbinfo->relkind == RELKIND_MATVIEW)
15799 {
15800 PQExpBuffer result;
15801
15802 result = createViewAsClause(fout, tbinfo);
15803 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
15804 result->data);
15805 destroyPQExpBuffer(result);
15806 }
15807 else
15808 appendPQExpBufferStr(q, ";\n");
15809
15810 /*
15811 * in binary upgrade mode, update the catalog with any missing values
15812 * that might be present.
15813 */
15814 if (dopt->binary_upgrade)
15815 {
15816 for (j = 0; j < tbinfo->numatts; j++)
15817 {
15818 if (tbinfo->attmissingval[j][0] != '\0')
15819 {
15820 appendPQExpBufferStr(q, "\n-- set missing value.\n");
15821 appendPQExpBufferStr(q,
15822 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
15823 appendStringLiteralAH(q, qualrelname, fout);
15824 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
15825 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
15826 appendPQExpBufferStr(q, ",");
15827 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
15828 appendPQExpBufferStr(q, ");\n\n");
15829 }
15830 }
15831 }
15832
15833 /*
15834 * To create binary-compatible heap files, we have to ensure the same
15835 * physical column order, including dropped columns, as in the
15836 * original. Therefore, we create dropped columns above and drop them
15837 * here, also updating their attlen/attalign values so that the
15838 * dropped column can be skipped properly. (We do not bother with
15839 * restoring the original attbyval setting.) Also, inheritance
15840 * relationships are set up by doing ALTER TABLE INHERIT rather than
15841 * using an INHERITS clause --- the latter would possibly mess up the
15842 * column order. That also means we have to take care about setting
15843 * attislocal correctly, plus fix up any inherited CHECK constraints.
15844 * Analogously, we set up typed tables using ALTER TABLE / OF here.
15845 *
15846 * We process foreign and partitioned tables here, even though they
15847 * lack heap storage, because they can participate in inheritance
15848 * relationships and we want this stuff to be consistent across the
15849 * inheritance tree. We can exclude indexes, toast tables, sequences
15850 * and matviews, even though they have storage, because we don't
15851 * support altering or dropping columns in them, nor can they be part
15852 * of inheritance trees.
15853 */
15854 if (dopt->binary_upgrade &&
15855 (tbinfo->relkind == RELKIND_RELATION ||
15856 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
15857 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
15858 {
15859 for (j = 0; j < tbinfo->numatts; j++)
15860 {
15861 if (tbinfo->attisdropped[j])
15862 {
15863 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
15864 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
15865 "SET attlen = %d, "
15866 "attalign = '%c', attbyval = false\n"
15867 "WHERE attname = ",
15868 tbinfo->attlen[j],
15869 tbinfo->attalign[j]);
15870 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
15871 appendPQExpBufferStr(q, "\n AND attrelid = ");
15872 appendStringLiteralAH(q, qualrelname, fout);
15873 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15874
15875 if (tbinfo->relkind == RELKIND_RELATION ||
15876 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
15877 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15878 qualrelname);
15879 else
15880 appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
15881 qualrelname);
15882 appendPQExpBuffer(q, "DROP COLUMN %s;\n",
15883 fmtId(tbinfo->attnames[j]));
15884 }
15885 else if (!tbinfo->attislocal[j])
15886 {
15887 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
15888 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
15889 "SET attislocal = false\n"
15890 "WHERE attname = ");
15891 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
15892 appendPQExpBufferStr(q, "\n AND attrelid = ");
15893 appendStringLiteralAH(q, qualrelname, fout);
15894 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15895 }
15896 }
15897
15898 /*
15899 * Add inherited CHECK constraints, if any.
15900 *
15901 * For partitions, they were already dumped, and conislocal
15902 * doesn't need fixing.
15903 */
15904 for (k = 0; k < tbinfo->ncheck; k++)
15905 {
15906 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
15907
15908 if (constr->separate || constr->conislocal || tbinfo->ispartition)
15909 continue;
15910
15911 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
15912 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
15913 qualrelname);
15914 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
15915 fmtId(constr->dobj.name));
15916 appendPQExpBuffer(q, "%s;\n", constr->condef);
15917 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
15918 "SET conislocal = false\n"
15919 "WHERE contype = 'c' AND conname = ");
15920 appendStringLiteralAH(q, constr->dobj.name, fout);
15921 appendPQExpBufferStr(q, "\n AND conrelid = ");
15922 appendStringLiteralAH(q, qualrelname, fout);
15923 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15924 }
15925
15926 if (numParents > 0 && !tbinfo->ispartition)
15927 {
15928 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
15929 for (k = 0; k < numParents; k++)
15930 {
15931 TableInfo *parentRel = parents[k];
15932
15933 appendPQExpBuffer(q, "ALTER TABLE ONLY %s INHERIT %s;\n",
15934 qualrelname,
15935 fmtQualifiedDumpable(parentRel));
15936 }
15937 }
15938
15939 if (tbinfo->reloftype)
15940 {
15941 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
15942 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
15943 qualrelname,
15944 tbinfo->reloftype);
15945 }
15946 }
15947
15948 /*
15949 * For partitioned tables, emit the ATTACH PARTITION clause. Note
15950 * that we always want to create partitions this way instead of using
15951 * CREATE TABLE .. PARTITION OF, mainly to preserve a possible column
15952 * layout discrepancy with the parent, but also to ensure it gets the
15953 * correct tablespace setting if it differs from the parent's.
15954 */
15955 if (tbinfo->ispartition)
15956 {
15957 /* With partitions there can only be one parent */
15958 if (tbinfo->numParents != 1)
15959 fatal("invalid number of parents %d for table \"%s\"",
15960 tbinfo->numParents, tbinfo->dobj.name);
15961
15962 /* Perform ALTER TABLE on the parent */
15963 appendPQExpBuffer(q,
15964 "ALTER TABLE ONLY %s ATTACH PARTITION %s %s;\n",
15965 fmtQualifiedDumpable(parents[0]),
15966 qualrelname, tbinfo->partbound);
15967 }
15968
15969 /*
15970 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
15971 * relminmxid of all vacuumable relations. (While vacuum.c processes
15972 * TOAST tables semi-independently, here we see them only as children
15973 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
15974 * child toast table is handled below.)
15975 */
15976 if (dopt->binary_upgrade &&
15977 (tbinfo->relkind == RELKIND_RELATION ||
15978 tbinfo->relkind == RELKIND_MATVIEW))
15979 {
15980 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
15981 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
15982 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
15983 "WHERE oid = ",
15984 tbinfo->frozenxid, tbinfo->minmxid);
15985 appendStringLiteralAH(q, qualrelname, fout);
15986 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
15987
15988 if (tbinfo->toast_oid)
15989 {
15990 /*
15991 * The toast table will have the same OID at restore, so we
15992 * can safely target it by OID.
15993 */
15994 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
15995 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
15996 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
15997 "WHERE oid = '%u';\n",
15998 tbinfo->toast_frozenxid,
15999 tbinfo->toast_minmxid, tbinfo->toast_oid);
16000 }
16001 }
16002
16003 /*
16004 * In binary_upgrade mode, restore matviews' populated status by
16005 * poking pg_class directly. This is pretty ugly, but we can't use
16006 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16007 * matview is not populated even though this matview is; in any case,
16008 * we want to transfer the matview's heap storage, not run REFRESH.
16009 */
16010 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16011 tbinfo->relispopulated)
16012 {
16013 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16014 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16015 "SET relispopulated = 't'\n"
16016 "WHERE oid = ");
16017 appendStringLiteralAH(q, qualrelname, fout);
16018 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16019 }
16020
16021 /*
16022 * Dump additional per-column properties that we can't handle in the
16023 * main CREATE TABLE command.
16024 */
16025 for (j = 0; j < tbinfo->numatts; j++)
16026 {
16027 /* None of this applies to dropped columns */
16028 if (tbinfo->attisdropped[j])
16029 continue;
16030
16031 /*
16032 * If we didn't dump the column definition explicitly above, and
16033 * it is NOT NULL and did not inherit that property from a parent,
16034 * we have to mark it separately.
16035 */
16036 if (!shouldPrintColumn(dopt, tbinfo, j) &&
16037 tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
16038 {
16039 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16040 qualrelname);
16041 appendPQExpBuffer(q, "ALTER COLUMN %s SET NOT NULL;\n",
16042 fmtId(tbinfo->attnames[j]));
16043 }
16044
16045 /*
16046 * Dump per-column statistics information. We only issue an ALTER
16047 * TABLE statement if the attstattarget entry for this column is
16048 * non-negative (i.e. it's not the default value)
16049 */
16050 if (tbinfo->attstattarget[j] >= 0)
16051 {
16052 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16053 qualrelname);
16054 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16055 fmtId(tbinfo->attnames[j]));
16056 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
16057 tbinfo->attstattarget[j]);
16058 }
16059
16060 /*
16061 * Dump per-column storage information. The statement is only
16062 * dumped if the storage has been changed from the type's default.
16063 */
16064 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16065 {
16066 switch (tbinfo->attstorage[j])
16067 {
16068 case 'p':
16069 storage = "PLAIN";
16070 break;
16071 case 'e':
16072 storage = "EXTERNAL";
16073 break;
16074 case 'm':
16075 storage = "MAIN";
16076 break;
16077 case 'x':
16078 storage = "EXTENDED";
16079 break;
16080 default:
16081 storage = NULL;
16082 }
16083
16084 /*
16085 * Only dump the statement if it's a storage type we recognize
16086 */
16087 if (storage != NULL)
16088 {
16089 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16090 qualrelname);
16091 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16092 fmtId(tbinfo->attnames[j]));
16093 appendPQExpBuffer(q, "SET STORAGE %s;\n",
16094 storage);
16095 }
16096 }
16097
16098 /*
16099 * Dump per-column attributes.
16100 */
16101 if (tbinfo->attoptions[j][0] != '\0')
16102 {
16103 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16104 qualrelname);
16105 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16106 fmtId(tbinfo->attnames[j]));
16107 appendPQExpBuffer(q, "SET (%s);\n",
16108 tbinfo->attoptions[j]);
16109 }
16110
16111 /*
16112 * Dump per-column fdw options.
16113 */
16114 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16115 tbinfo->attfdwoptions[j][0] != '\0')
16116 {
16117 appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ",
16118 qualrelname);
16119 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16120 fmtId(tbinfo->attnames[j]));
16121 appendPQExpBuffer(q, "OPTIONS (\n %s\n);\n",
16122 tbinfo->attfdwoptions[j]);
16123 }
16124 }
16125
16126 if (ftoptions)
16127 free(ftoptions);
16128 if (srvname)
16129 free(srvname);
16130 }
16131
16132 /*
16133 * dump properties we only have ALTER TABLE syntax for
16134 */
16135 if ((tbinfo->relkind == RELKIND_RELATION ||
16136 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16137 tbinfo->relkind == RELKIND_MATVIEW) &&
16138 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16139 {
16140 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16141 {
16142 /* nothing to do, will be set when the index is dumped */
16143 }
16144 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16145 {
16146 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16147 qualrelname);
16148 }
16149 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16150 {
16151 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16152 qualrelname);
16153 }
16154 }
16155
16156 if (tbinfo->forcerowsec)
16157 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16158 qualrelname);
16159
16160 if (dopt->binary_upgrade)
16161 binary_upgrade_extension_member(q, &tbinfo->dobj,
16162 reltypename, qrelname,
16163 tbinfo->dobj.namespace->dobj.name);
16164
16165 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16166 {
16167 char *tableam = NULL;
16168
16169 if (tbinfo->relkind == RELKIND_RELATION ||
16170 tbinfo->relkind == RELKIND_MATVIEW)
16171 tableam = tbinfo->amname;
16172
16173 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16174 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16175 .namespace = tbinfo->dobj.namespace->dobj.name,
16176 .tablespace = (tbinfo->relkind == RELKIND_VIEW) ?
16177 NULL : tbinfo->reltablespace,
16178 .tableam = tableam,
16179 .owner = tbinfo->rolname,
16180 .description = reltypename,
16181 .section = tbinfo->postponed_def ?
16182 SECTION_POST_DATA : SECTION_PRE_DATA,
16183 .createStmt = q->data,
16184 .dropStmt = delq->data));
16185 }
16186
16187 /* Dump Table Comments */
16188 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16189 dumpTableComment(fout, tbinfo, reltypename);
16190
16191 /* Dump Table Security Labels */
16192 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16193 dumpTableSecLabel(fout, tbinfo, reltypename);
16194
16195 /* Dump comments on inlined table constraints */
16196 for (j = 0; j < tbinfo->ncheck; j++)
16197 {
16198 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16199
16200 if (constr->separate || !constr->conislocal)
16201 continue;
16202
16203 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16204 dumpTableConstraintComment(fout, constr);
16205 }
16206
16207 destroyPQExpBuffer(q);
16208 destroyPQExpBuffer(delq);
16209 free(qrelname);
16210 free(qualrelname);
16211}
16212
16213/*
16214 * dumpAttrDef --- dump an attribute's default-value declaration
16215 */
16216static void
16217dumpAttrDef(Archive *fout, AttrDefInfo *adinfo)
16218{
16219 DumpOptions *dopt = fout->dopt;
16220 TableInfo *tbinfo = adinfo->adtable;
16221 int adnum = adinfo->adnum;
16222 PQExpBuffer q;
16223 PQExpBuffer delq;
16224 char *qualrelname;
16225 char *tag;
16226
16227 /* Skip if table definition not to be dumped */
16228 if (!tbinfo->dobj.dump || dopt->dataOnly)
16229 return;
16230
16231 /* Skip if not "separate"; it was dumped in the table's definition */
16232 if (!adinfo->separate)
16233 return;
16234
16235 q = createPQExpBuffer();
16236 delq = createPQExpBuffer();
16237
16238 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16239
16240 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16241 qualrelname);
16242 appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
16243 fmtId(tbinfo->attnames[adnum - 1]),
16244 adinfo->adef_expr);
16245
16246 appendPQExpBuffer(delq, "ALTER TABLE %s ",
16247 qualrelname);
16248 appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
16249 fmtId(tbinfo->attnames[adnum - 1]));
16250
16251 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
16252
16253 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16254 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
16255 ARCHIVE_OPTS(.tag = tag,
16256 .namespace = tbinfo->dobj.namespace->dobj.name,
16257 .owner = tbinfo->rolname,
16258 .description = "DEFAULT",
16259 .section = SECTION_PRE_DATA,
16260 .createStmt = q->data,
16261 .dropStmt = delq->data));
16262
16263 free(tag);
16264 destroyPQExpBuffer(q);
16265 destroyPQExpBuffer(delq);
16266 free(qualrelname);
16267}
16268
16269/*
16270 * getAttrName: extract the correct name for an attribute
16271 *
16272 * The array tblInfo->attnames[] only provides names of user attributes;
16273 * if a system attribute number is supplied, we have to fake it.
16274 * We also do a little bit of bounds checking for safety's sake.
16275 */
16276static const char *
16277getAttrName(int attrnum, TableInfo *tblInfo)
16278{
16279 if (attrnum > 0 && attrnum <= tblInfo->numatts)
16280 return tblInfo->attnames[attrnum - 1];
16281 switch (attrnum)
16282 {
16283 case SelfItemPointerAttributeNumber:
16284 return "ctid";
16285 case MinTransactionIdAttributeNumber:
16286 return "xmin";
16287 case MinCommandIdAttributeNumber:
16288 return "cmin";
16289 case MaxTransactionIdAttributeNumber:
16290 return "xmax";
16291 case MaxCommandIdAttributeNumber:
16292 return "cmax";
16293 case TableOidAttributeNumber:
16294 return "tableoid";
16295 }
16296 fatal("invalid column number %d for table \"%s\"",
16297 attrnum, tblInfo->dobj.name);
16298 return NULL; /* keep compiler quiet */
16299}
16300
16301/*
16302 * dumpIndex
16303 * write out to fout a user-defined index
16304 */
16305static void
16306dumpIndex(Archive *fout, IndxInfo *indxinfo)
16307{
16308 DumpOptions *dopt = fout->dopt;
16309 TableInfo *tbinfo = indxinfo->indextable;
16310 bool is_constraint = (indxinfo->indexconstraint != 0);
16311 PQExpBuffer q;
16312 PQExpBuffer delq;
16313 char *qindxname;
16314
16315 if (dopt->dataOnly)
16316 return;
16317
16318 q = createPQExpBuffer();
16319 delq = createPQExpBuffer();
16320
16321 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
16322
16323 /*
16324 * If there's an associated constraint, don't dump the index per se, but
16325 * do dump any comment for it. (This is safe because dependency ordering
16326 * will have ensured the constraint is emitted first.) Note that the
16327 * emitted comment has to be shown as depending on the constraint, not the
16328 * index, in such cases.
16329 */
16330 if (!is_constraint)
16331 {
16332 char *indstatcols = indxinfo->indstatcols;
16333 char *indstatvals = indxinfo->indstatvals;
16334 char **indstatcolsarray = NULL;
16335 char **indstatvalsarray = NULL;
16336 int nstatcols;
16337 int nstatvals;
16338
16339 if (dopt->binary_upgrade)
16340 binary_upgrade_set_pg_class_oids(fout, q,
16341 indxinfo->dobj.catId.oid, true);
16342
16343 /* Plain secondary index */
16344 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
16345
16346 /*
16347 * Append ALTER TABLE commands as needed to set properties that we
16348 * only have ALTER TABLE syntax for. Keep this in sync with the
16349 * similar code in dumpConstraint!
16350 */
16351
16352 /* If the index is clustered, we need to record that. */
16353 if (indxinfo->indisclustered)
16354 {
16355 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
16356 fmtQualifiedDumpable(tbinfo));
16357 /* index name is not qualified in this syntax */
16358 appendPQExpBuffer(q, " ON %s;\n",
16359 qindxname);
16360 }
16361
16362 /*
16363 * If the index has any statistics on some of its columns, generate
16364 * the associated ALTER INDEX queries.
16365 */
16366 if (parsePGArray(indstatcols, &indstatcolsarray, &nstatcols) &&
16367 parsePGArray(indstatvals, &indstatvalsarray, &nstatvals) &&
16368 nstatcols == nstatvals)
16369 {
16370 int j;
16371
16372 for (j = 0; j < nstatcols; j++)
16373 {
16374 appendPQExpBuffer(q, "ALTER INDEX %s ",
16375 fmtQualifiedDumpable(indxinfo));
16376
16377 /*
16378 * Note that this is a column number, so no quotes should be
16379 * used.
16380 */
16381 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16382 indstatcolsarray[j]);
16383 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
16384 indstatvalsarray[j]);
16385 }
16386 }
16387
16388 /* If the index defines identity, we need to record that. */
16389 if (indxinfo->indisreplident)
16390 {
16391 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
16392 fmtQualifiedDumpable(tbinfo));
16393 /* index name is not qualified in this syntax */
16394 appendPQExpBuffer(q, " INDEX %s;\n",
16395 qindxname);
16396 }
16397
16398 appendPQExpBuffer(delq, "DROP INDEX %s;\n",
16399 fmtQualifiedDumpable(indxinfo));
16400
16401 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16402 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
16403 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
16404 .namespace = tbinfo->dobj.namespace->dobj.name,
16405 .tablespace = indxinfo->tablespace,
16406 .owner = tbinfo->rolname,
16407 .description = "INDEX",
16408 .section = SECTION_POST_DATA,
16409 .createStmt = q->data,
16410 .dropStmt = delq->data));
16411
16412 if (indstatcolsarray)
16413 free(indstatcolsarray);
16414 if (indstatvalsarray)
16415 free(indstatvalsarray);
16416 }
16417
16418 /* Dump Index Comments */
16419 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16420 dumpComment(fout, "INDEX", qindxname,
16421 tbinfo->dobj.namespace->dobj.name,
16422 tbinfo->rolname,
16423 indxinfo->dobj.catId, 0,
16424 is_constraint ? indxinfo->indexconstraint :
16425 indxinfo->dobj.dumpId);
16426
16427 destroyPQExpBuffer(q);
16428 destroyPQExpBuffer(delq);
16429 free(qindxname);
16430}
16431
16432/*
16433 * dumpIndexAttach
16434 * write out to fout a partitioned-index attachment clause
16435 */
16436static void
16437dumpIndexAttach(Archive *fout, IndexAttachInfo *attachinfo)
16438{
16439 if (fout->dopt->dataOnly)
16440 return;
16441
16442 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
16443 {
16444 PQExpBuffer q = createPQExpBuffer();
16445
16446 appendPQExpBuffer(q, "ALTER INDEX %s ",
16447 fmtQualifiedDumpable(attachinfo->parentIdx));
16448 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
16449 fmtQualifiedDumpable(attachinfo->partitionIdx));
16450
16451 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16452 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16453 .namespace = attachinfo->dobj.namespace->dobj.name,
16454 .description = "INDEX ATTACH",
16455 .section = SECTION_POST_DATA,
16456 .createStmt = q->data));
16457
16458 destroyPQExpBuffer(q);
16459 }
16460}
16461
16462/*
16463 * dumpStatisticsExt
16464 * write out to fout an extended statistics object
16465 */
16466static void
16467dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo)
16468{
16469 DumpOptions *dopt = fout->dopt;
16470 PQExpBuffer q;
16471 PQExpBuffer delq;
16472 PQExpBuffer query;
16473 char *qstatsextname;
16474 PGresult *res;
16475 char *stxdef;
16476
16477 /* Skip if not to be dumped */
16478 if (!statsextinfo->dobj.dump || dopt->dataOnly)
16479 return;
16480
16481 q = createPQExpBuffer();
16482 delq = createPQExpBuffer();
16483 query = createPQExpBuffer();
16484
16485 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
16486
16487 appendPQExpBuffer(query, "SELECT "
16488 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
16489 statsextinfo->dobj.catId.oid);
16490
16491 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16492
16493 stxdef = PQgetvalue(res, 0, 0);
16494
16495 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
16496 appendPQExpBuffer(q, "%s;\n", stxdef);
16497
16498 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
16499 fmtQualifiedDumpable(statsextinfo));
16500
16501 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16502 ArchiveEntry(fout, statsextinfo->dobj.catId,
16503 statsextinfo->dobj.dumpId,
16504 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
16505 .namespace = statsextinfo->dobj.namespace->dobj.name,
16506 .owner = statsextinfo->rolname,
16507 .description = "STATISTICS",
16508 .section = SECTION_POST_DATA,
16509 .createStmt = q->data,
16510 .dropStmt = delq->data));
16511
16512 /* Dump Statistics Comments */
16513 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16514 dumpComment(fout, "STATISTICS", qstatsextname,
16515 statsextinfo->dobj.namespace->dobj.name,
16516 statsextinfo->rolname,
16517 statsextinfo->dobj.catId, 0,
16518 statsextinfo->dobj.dumpId);
16519
16520 PQclear(res);
16521 destroyPQExpBuffer(q);
16522 destroyPQExpBuffer(delq);
16523 destroyPQExpBuffer(query);
16524 free(qstatsextname);
16525}
16526
16527/*
16528 * dumpConstraint
16529 * write out to fout a user-defined constraint
16530 */
16531static void
16532dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
16533{
16534 DumpOptions *dopt = fout->dopt;
16535 TableInfo *tbinfo = coninfo->contable;
16536 PQExpBuffer q;
16537 PQExpBuffer delq;
16538 char *tag = NULL;
16539
16540 /* Skip if not to be dumped */
16541 if (!coninfo->dobj.dump || dopt->dataOnly)
16542 return;
16543
16544 q = createPQExpBuffer();
16545 delq = createPQExpBuffer();
16546
16547 if (coninfo->contype == 'p' ||
16548 coninfo->contype == 'u' ||
16549 coninfo->contype == 'x')
16550 {
16551 /* Index-related constraint */
16552 IndxInfo *indxinfo;
16553 int k;
16554
16555 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
16556
16557 if (indxinfo == NULL)
16558 fatal("missing index for constraint \"%s\"",
16559 coninfo->dobj.name);
16560
16561 if (dopt->binary_upgrade)
16562 binary_upgrade_set_pg_class_oids(fout, q,
16563 indxinfo->dobj.catId.oid, true);
16564
16565 appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
16566 fmtQualifiedDumpable(tbinfo));
16567 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
16568 fmtId(coninfo->dobj.name));
16569
16570 if (coninfo->condef)
16571 {
16572 /* pg_get_constraintdef should have provided everything */
16573 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
16574 }
16575 else
16576 {
16577 appendPQExpBuffer(q, "%s (",
16578 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
16579 for (k = 0; k < indxinfo->indnkeyattrs; k++)
16580 {
16581 int indkey = (int) indxinfo->indkeys[k];
16582 const char *attname;
16583
16584 if (indkey == InvalidAttrNumber)
16585 break;
16586 attname = getAttrName(indkey, tbinfo);
16587
16588 appendPQExpBuffer(q, "%s%s",
16589 (k == 0) ? "" : ", ",
16590 fmtId(attname));
16591 }
16592
16593 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
16594 appendPQExpBuffer(q, ") INCLUDE (");
16595
16596 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
16597 {
16598 int indkey = (int) indxinfo->indkeys[k];
16599 const char *attname;
16600
16601 if (indkey == InvalidAttrNumber)
16602 break;
16603 attname = getAttrName(indkey, tbinfo);
16604
16605 appendPQExpBuffer(q, "%s%s",
16606 (k == indxinfo->indnkeyattrs) ? "" : ", ",
16607 fmtId(attname));
16608 }
16609
16610 appendPQExpBufferChar(q, ')');
16611
16612 if (nonemptyReloptions(indxinfo->indreloptions))
16613 {
16614 appendPQExpBufferStr(q, " WITH (");
16615 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
16616 appendPQExpBufferChar(q, ')');
16617 }
16618
16619 if (coninfo->condeferrable)
16620 {
16621 appendPQExpBufferStr(q, " DEFERRABLE");
16622 if (coninfo->condeferred)
16623 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
16624 }
16625
16626 appendPQExpBufferStr(q, ";\n");
16627 }
16628
16629 /*
16630 * Append ALTER TABLE commands as needed to set properties that we
16631 * only have ALTER TABLE syntax for. Keep this in sync with the
16632 * similar code in dumpIndex!
16633 */
16634
16635 /* If the index is clustered, we need to record that. */
16636 if (indxinfo->indisclustered)
16637 {
16638 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
16639 fmtQualifiedDumpable(tbinfo));
16640 /* index name is not qualified in this syntax */
16641 appendPQExpBuffer(q, " ON %s;\n",
16642 fmtId(indxinfo->dobj.name));
16643 }
16644
16645 /* If the index defines identity, we need to record that. */
16646 if (indxinfo->indisreplident)
16647 {
16648 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
16649 fmtQualifiedDumpable(tbinfo));
16650 /* index name is not qualified in this syntax */
16651 appendPQExpBuffer(q, " INDEX %s;\n",
16652 fmtId(indxinfo->dobj.name));
16653 }
16654
16655 appendPQExpBuffer(delq, "ALTER TABLE ONLY %s ",
16656 fmtQualifiedDumpable(tbinfo));
16657 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16658 fmtId(coninfo->dobj.name));
16659
16660 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16661
16662 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16663 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16664 ARCHIVE_OPTS(.tag = tag,
16665 .namespace = tbinfo->dobj.namespace->dobj.name,
16666 .tablespace = indxinfo->tablespace,
16667 .owner = tbinfo->rolname,
16668 .description = "CONSTRAINT",
16669 .section = SECTION_POST_DATA,
16670 .createStmt = q->data,
16671 .dropStmt = delq->data));
16672 }
16673 else if (coninfo->contype == 'f')
16674 {
16675 char *only;
16676
16677 /*
16678 * Foreign keys on partitioned tables are always declared as
16679 * inheriting to partitions; for all other cases, emit them as
16680 * applying ONLY directly to the named table, because that's how they
16681 * work for regular inherited tables.
16682 */
16683 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
16684
16685 /*
16686 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
16687 * current table data is not processed
16688 */
16689 appendPQExpBuffer(q, "ALTER TABLE %s%s\n",
16690 only, fmtQualifiedDumpable(tbinfo));
16691 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16692 fmtId(coninfo->dobj.name),
16693 coninfo->condef);
16694
16695 appendPQExpBuffer(delq, "ALTER TABLE %s%s ",
16696 only, fmtQualifiedDumpable(tbinfo));
16697 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16698 fmtId(coninfo->dobj.name));
16699
16700 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16701
16702 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16703 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16704 ARCHIVE_OPTS(.tag = tag,
16705 .namespace = tbinfo->dobj.namespace->dobj.name,
16706 .owner = tbinfo->rolname,
16707 .description = "FK CONSTRAINT",
16708 .section = SECTION_POST_DATA,
16709 .createStmt = q->data,
16710 .dropStmt = delq->data));
16711 }
16712 else if (coninfo->contype == 'c' && tbinfo)
16713 {
16714 /* CHECK constraint on a table */
16715
16716 /* Ignore if not to be dumped separately, or if it was inherited */
16717 if (coninfo->separate && coninfo->conislocal)
16718 {
16719 /* not ONLY since we want it to propagate to children */
16720 appendPQExpBuffer(q, "ALTER TABLE %s\n",
16721 fmtQualifiedDumpable(tbinfo));
16722 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16723 fmtId(coninfo->dobj.name),
16724 coninfo->condef);
16725
16726 appendPQExpBuffer(delq, "ALTER TABLE %s ",
16727 fmtQualifiedDumpable(tbinfo));
16728 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16729 fmtId(coninfo->dobj.name));
16730
16731 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
16732
16733 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16734 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16735 ARCHIVE_OPTS(.tag = tag,
16736 .namespace = tbinfo->dobj.namespace->dobj.name,
16737 .owner = tbinfo->rolname,
16738 .description = "CHECK CONSTRAINT",
16739 .section = SECTION_POST_DATA,
16740 .createStmt = q->data,
16741 .dropStmt = delq->data));
16742 }
16743 }
16744 else if (coninfo->contype == 'c' && tbinfo == NULL)
16745 {
16746 /* CHECK constraint on a domain */
16747 TypeInfo *tyinfo = coninfo->condomain;
16748
16749 /* Ignore if not to be dumped separately */
16750 if (coninfo->separate)
16751 {
16752 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
16753 fmtQualifiedDumpable(tyinfo));
16754 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
16755 fmtId(coninfo->dobj.name),
16756 coninfo->condef);
16757
16758 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
16759 fmtQualifiedDumpable(tyinfo));
16760 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
16761 fmtId(coninfo->dobj.name));
16762
16763 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
16764
16765 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16766 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
16767 ARCHIVE_OPTS(.tag = tag,
16768 .namespace = tyinfo->dobj.namespace->dobj.name,
16769 .owner = tyinfo->rolname,
16770 .description = "CHECK CONSTRAINT",
16771 .section = SECTION_POST_DATA,
16772 .createStmt = q->data,
16773 .dropStmt = delq->data));
16774 }
16775 }
16776 else
16777 {
16778 fatal("unrecognized constraint type: %c",
16779 coninfo->contype);
16780 }
16781
16782 /* Dump Constraint Comments --- only works for table constraints */
16783 if (tbinfo && coninfo->separate &&
16784 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16785 dumpTableConstraintComment(fout, coninfo);
16786
16787 free(tag);
16788 destroyPQExpBuffer(q);
16789 destroyPQExpBuffer(delq);
16790}
16791
16792/*
16793 * dumpTableConstraintComment --- dump a constraint's comment if any
16794 *
16795 * This is split out because we need the function in two different places
16796 * depending on whether the constraint is dumped as part of CREATE TABLE
16797 * or as a separate ALTER command.
16798 */
16799static void
16800dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo)
16801{
16802 TableInfo *tbinfo = coninfo->contable;
16803 PQExpBuffer conprefix = createPQExpBuffer();
16804 char *qtabname;
16805
16806 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
16807
16808 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
16809 fmtId(coninfo->dobj.name));
16810
16811 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16812 dumpComment(fout, conprefix->data, qtabname,
16813 tbinfo->dobj.namespace->dobj.name,
16814 tbinfo->rolname,
16815 coninfo->dobj.catId, 0,
16816 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
16817
16818 destroyPQExpBuffer(conprefix);
16819 free(qtabname);
16820}
16821
16822/*
16823 * findLastBuiltinOid_V71 -
16824 *
16825 * find the last built in oid
16826 *
16827 * For 7.1 through 8.0, we do this by retrieving datlastsysoid from the
16828 * pg_database entry for the current database. (Note: current_database()
16829 * requires 7.3; pg_dump requires 8.0 now.)
16830 */
16831static Oid
16832findLastBuiltinOid_V71(Archive *fout)
16833{
16834 PGresult *res;
16835 Oid last_oid;
16836
16837 res = ExecuteSqlQueryForSingleRow(fout,
16838 "SELECT datlastsysoid FROM pg_database WHERE datname = current_database()");
16839 last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
16840 PQclear(res);
16841
16842 return last_oid;
16843}
16844
16845/*
16846 * dumpSequence
16847 * write the declaration (not data) of one user-defined sequence
16848 */
16849static void
16850dumpSequence(Archive *fout, TableInfo *tbinfo)
16851{
16852 DumpOptions *dopt = fout->dopt;
16853 PGresult *res;
16854 char *startv,
16855 *incby,
16856 *maxv,
16857 *minv,
16858 *cache,
16859 *seqtype;
16860 bool cycled;
16861 bool is_ascending;
16862 int64 default_minv,
16863 default_maxv;
16864 char bufm[32],
16865 bufx[32];
16866 PQExpBuffer query = createPQExpBuffer();
16867 PQExpBuffer delqry = createPQExpBuffer();
16868 char *qseqname;
16869
16870 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
16871
16872 if (fout->remoteVersion >= 100000)
16873 {
16874 appendPQExpBuffer(query,
16875 "SELECT format_type(seqtypid, NULL), "
16876 "seqstart, seqincrement, "
16877 "seqmax, seqmin, "
16878 "seqcache, seqcycle "
16879 "FROM pg_catalog.pg_sequence "
16880 "WHERE seqrelid = '%u'::oid",
16881 tbinfo->dobj.catId.oid);
16882 }
16883 else if (fout->remoteVersion >= 80400)
16884 {
16885 /*
16886 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
16887 *
16888 * Note: it might seem that 'bigint' potentially needs to be
16889 * schema-qualified, but actually that's a keyword.
16890 */
16891 appendPQExpBuffer(query,
16892 "SELECT 'bigint' AS sequence_type, "
16893 "start_value, increment_by, max_value, min_value, "
16894 "cache_value, is_cycled FROM %s",
16895 fmtQualifiedDumpable(tbinfo));
16896 }
16897 else
16898 {
16899 appendPQExpBuffer(query,
16900 "SELECT 'bigint' AS sequence_type, "
16901 "0 AS start_value, increment_by, max_value, min_value, "
16902 "cache_value, is_cycled FROM %s",
16903 fmtQualifiedDumpable(tbinfo));
16904 }
16905
16906 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16907
16908 if (PQntuples(res) != 1)
16909 {
16910 pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
16911 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
16912 PQntuples(res)),
16913 tbinfo->dobj.name, PQntuples(res));
16914 exit_nicely(1);
16915 }
16916
16917 seqtype = PQgetvalue(res, 0, 0);
16918 startv = PQgetvalue(res, 0, 1);
16919 incby = PQgetvalue(res, 0, 2);
16920 maxv = PQgetvalue(res, 0, 3);
16921 minv = PQgetvalue(res, 0, 4);
16922 cache = PQgetvalue(res, 0, 5);
16923 cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
16924
16925 /* Calculate default limits for a sequence of this type */
16926 is_ascending = (incby[0] != '-');
16927 if (strcmp(seqtype, "smallint") == 0)
16928 {
16929 default_minv = is_ascending ? 1 : PG_INT16_MIN;
16930 default_maxv = is_ascending ? PG_INT16_MAX : -1;
16931 }
16932 else if (strcmp(seqtype, "integer") == 0)
16933 {
16934 default_minv = is_ascending ? 1 : PG_INT32_MIN;
16935 default_maxv = is_ascending ? PG_INT32_MAX : -1;
16936 }
16937 else if (strcmp(seqtype, "bigint") == 0)
16938 {
16939 default_minv = is_ascending ? 1 : PG_INT64_MIN;
16940 default_maxv = is_ascending ? PG_INT64_MAX : -1;
16941 }
16942 else
16943 {
16944 fatal("unrecognized sequence type: %s", seqtype);
16945 default_minv = default_maxv = 0; /* keep compiler quiet */
16946 }
16947
16948 /*
16949 * 64-bit strtol() isn't very portable, so convert the limits to strings
16950 * and compare that way.
16951 */
16952 snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv);
16953 snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv);
16954
16955 /* Don't print minv/maxv if they match the respective default limit */
16956 if (strcmp(minv, bufm) == 0)
16957 minv = NULL;
16958 if (strcmp(maxv, bufx) == 0)
16959 maxv = NULL;
16960
16961 /*
16962 * Identity sequences are not to be dropped separately.
16963 */
16964 if (!tbinfo->is_identity_sequence)
16965 {
16966 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
16967 fmtQualifiedDumpable(tbinfo));
16968 }
16969
16970 resetPQExpBuffer(query);
16971
16972 if (dopt->binary_upgrade)
16973 {
16974 binary_upgrade_set_pg_class_oids(fout, query,
16975 tbinfo->dobj.catId.oid, false);
16976 binary_upgrade_set_type_oids_by_rel_oid(fout, query,
16977 tbinfo->dobj.catId.oid);
16978 }
16979
16980 if (tbinfo->is_identity_sequence)
16981 {
16982 TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
16983
16984 appendPQExpBuffer(query,
16985 "ALTER TABLE %s ",
16986 fmtQualifiedDumpable(owning_tab));
16987 appendPQExpBuffer(query,
16988 "ALTER COLUMN %s ADD GENERATED ",
16989 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
16990 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
16991 appendPQExpBuffer(query, "ALWAYS");
16992 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
16993 appendPQExpBuffer(query, "BY DEFAULT");
16994 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
16995 fmtQualifiedDumpable(tbinfo));
16996 }
16997 else
16998 {
16999 appendPQExpBuffer(query,
17000 "CREATE SEQUENCE %s\n",
17001 fmtQualifiedDumpable(tbinfo));
17002
17003 if (strcmp(seqtype, "bigint") != 0)
17004 appendPQExpBuffer(query, " AS %s\n", seqtype);
17005 }
17006
17007 if (fout->remoteVersion >= 80400)
17008 appendPQExpBuffer(query, " START WITH %s\n", startv);
17009
17010 appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
17011
17012 if (minv)
17013 appendPQExpBuffer(query, " MINVALUE %s\n", minv);
17014 else
17015 appendPQExpBufferStr(query, " NO MINVALUE\n");
17016
17017 if (maxv)
17018 appendPQExpBuffer(query, " MAXVALUE %s\n", maxv);
17019 else
17020 appendPQExpBufferStr(query, " NO MAXVALUE\n");
17021
17022 appendPQExpBuffer(query,
17023 " CACHE %s%s",
17024 cache, (cycled ? "\n CYCLE" : ""));
17025
17026 if (tbinfo->is_identity_sequence)
17027 appendPQExpBufferStr(query, "\n);\n");
17028 else
17029 appendPQExpBufferStr(query, ";\n");
17030
17031 /* binary_upgrade: no need to clear TOAST table oid */
17032
17033 if (dopt->binary_upgrade)
17034 binary_upgrade_extension_member(query, &tbinfo->dobj,
17035 "SEQUENCE", qseqname,
17036 tbinfo->dobj.namespace->dobj.name);
17037
17038 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17039 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17040 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17041 .namespace = tbinfo->dobj.namespace->dobj.name,
17042 .owner = tbinfo->rolname,
17043 .description = "SEQUENCE",
17044 .section = SECTION_PRE_DATA,
17045 .createStmt = query->data,
17046 .dropStmt = delqry->data));
17047
17048 /*
17049 * If the sequence is owned by a table column, emit the ALTER for it as a
17050 * separate TOC entry immediately following the sequence's own entry. It's
17051 * OK to do this rather than using full sorting logic, because the
17052 * dependency that tells us it's owned will have forced the table to be
17053 * created first. We can't just include the ALTER in the TOC entry
17054 * because it will fail if we haven't reassigned the sequence owner to
17055 * match the table's owner.
17056 *
17057 * We need not schema-qualify the table reference because both sequence
17058 * and table must be in the same schema.
17059 */
17060 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17061 {
17062 TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
17063
17064 if (owning_tab == NULL)
17065 fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17066 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17067
17068 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17069 {
17070 resetPQExpBuffer(query);
17071 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17072 fmtQualifiedDumpable(tbinfo));
17073 appendPQExpBuffer(query, " OWNED BY %s",
17074 fmtQualifiedDumpable(owning_tab));
17075 appendPQExpBuffer(query, ".%s;\n",
17076 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17077
17078 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17079 ArchiveEntry(fout, nilCatalogId, createDumpId(),
17080 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17081 .namespace = tbinfo->dobj.namespace->dobj.name,
17082 .owner = tbinfo->rolname,
17083 .description = "SEQUENCE OWNED BY",
17084 .section = SECTION_PRE_DATA,
17085 .createStmt = query->data,
17086 .deps = &(tbinfo->dobj.dumpId),
17087 .nDeps = 1));
17088 }
17089 }
17090
17091 /* Dump Sequence Comments and Security Labels */
17092 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17093 dumpComment(fout, "SEQUENCE", qseqname,
17094 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17095 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17096
17097 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17098 dumpSecLabel(fout, "SEQUENCE", qseqname,
17099 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17100 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17101
17102 PQclear(res);
17103
17104 destroyPQExpBuffer(query);
17105 destroyPQExpBuffer(delqry);
17106 free(qseqname);
17107}
17108
17109/*
17110 * dumpSequenceData
17111 * write the data of one user-defined sequence
17112 */
17113static void
17114dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
17115{
17116 TableInfo *tbinfo = tdinfo->tdtable;
17117 PGresult *res;
17118 char *last;
17119 bool called;
17120 PQExpBuffer query = createPQExpBuffer();
17121
17122 appendPQExpBuffer(query,
17123 "SELECT last_value, is_called FROM %s",
17124 fmtQualifiedDumpable(tbinfo));
17125
17126 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17127
17128 if (PQntuples(res) != 1)
17129 {
17130 pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17131 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17132 PQntuples(res)),
17133 tbinfo->dobj.name, PQntuples(res));
17134 exit_nicely(1);
17135 }
17136
17137 last = PQgetvalue(res, 0, 0);
17138 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
17139
17140 resetPQExpBuffer(query);
17141 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
17142 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
17143 appendPQExpBuffer(query, ", %s, %s);\n",
17144 last, (called ? "true" : "false"));
17145
17146 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
17147 ArchiveEntry(fout, nilCatalogId, createDumpId(),
17148 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17149 .namespace = tbinfo->dobj.namespace->dobj.name,
17150 .owner = tbinfo->rolname,
17151 .description = "SEQUENCE SET",
17152 .section = SECTION_DATA,
17153 .createStmt = query->data,
17154 .deps = &(tbinfo->dobj.dumpId),
17155 .nDeps = 1));
17156
17157 PQclear(res);
17158
17159 destroyPQExpBuffer(query);
17160}
17161
17162/*
17163 * dumpTrigger
17164 * write the declaration of one user-defined table trigger
17165 */
17166static void
17167dumpTrigger(Archive *fout, TriggerInfo *tginfo)
17168{
17169 DumpOptions *dopt = fout->dopt;
17170 TableInfo *tbinfo = tginfo->tgtable;
17171 PQExpBuffer query;
17172 PQExpBuffer delqry;
17173 PQExpBuffer trigprefix;
17174 char *qtabname;
17175 char *tgargs;
17176 size_t lentgargs;
17177 const char *p;
17178 int findx;
17179 char *tag;
17180
17181 /*
17182 * we needn't check dobj.dump because TriggerInfo wouldn't have been
17183 * created in the first place for non-dumpable triggers
17184 */
17185 if (dopt->dataOnly)
17186 return;
17187
17188 query = createPQExpBuffer();
17189 delqry = createPQExpBuffer();
17190 trigprefix = createPQExpBuffer();
17191
17192 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17193
17194 appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
17195 fmtId(tginfo->dobj.name));
17196 appendPQExpBuffer(delqry, "ON %s;\n",
17197 fmtQualifiedDumpable(tbinfo));
17198
17199 if (tginfo->tgdef)
17200 {
17201 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
17202 }
17203 else
17204 {
17205 if (tginfo->tgisconstraint)
17206 {
17207 appendPQExpBufferStr(query, "CREATE CONSTRAINT TRIGGER ");
17208 appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
17209 }
17210 else
17211 {
17212 appendPQExpBufferStr(query, "CREATE TRIGGER ");
17213 appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
17214 }
17215 appendPQExpBufferStr(query, "\n ");
17216
17217 /* Trigger type */
17218 if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
17219 appendPQExpBufferStr(query, "BEFORE");
17220 else if (TRIGGER_FOR_AFTER(tginfo->tgtype))
17221 appendPQExpBufferStr(query, "AFTER");
17222 else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
17223 appendPQExpBufferStr(query, "INSTEAD OF");
17224 else
17225 {
17226 pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
17227 exit_nicely(1);
17228 }
17229
17230 findx = 0;
17231 if (TRIGGER_FOR_INSERT(tginfo->tgtype))
17232 {
17233 appendPQExpBufferStr(query, " INSERT");
17234 findx++;
17235 }
17236 if (TRIGGER_FOR_DELETE(tginfo->tgtype))
17237 {
17238 if (findx > 0)
17239 appendPQExpBufferStr(query, " OR DELETE");
17240 else
17241 appendPQExpBufferStr(query, " DELETE");
17242 findx++;
17243 }
17244 if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
17245 {
17246 if (findx > 0)
17247 appendPQExpBufferStr(query, " OR UPDATE");
17248 else
17249 appendPQExpBufferStr(query, " UPDATE");
17250 findx++;
17251 }
17252 if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
17253 {
17254 if (findx > 0)
17255 appendPQExpBufferStr(query, " OR TRUNCATE");
17256 else
17257 appendPQExpBufferStr(query, " TRUNCATE");
17258 findx++;
17259 }
17260 appendPQExpBuffer(query, " ON %s\n",
17261 fmtQualifiedDumpable(tbinfo));
17262
17263 if (tginfo->tgisconstraint)
17264 {
17265 if (OidIsValid(tginfo->tgconstrrelid))
17266 {
17267 /* regclass output is already quoted */
17268 appendPQExpBuffer(query, " FROM %s\n ",
17269 tginfo->tgconstrrelname);
17270 }
17271 if (!tginfo->tgdeferrable)
17272 appendPQExpBufferStr(query, "NOT ");
17273 appendPQExpBufferStr(query, "DEFERRABLE INITIALLY ");
17274 if (tginfo->tginitdeferred)
17275 appendPQExpBufferStr(query, "DEFERRED\n");
17276 else
17277 appendPQExpBufferStr(query, "IMMEDIATE\n");
17278 }
17279
17280 if (TRIGGER_FOR_ROW(tginfo->tgtype))
17281 appendPQExpBufferStr(query, " FOR EACH ROW\n ");
17282 else
17283 appendPQExpBufferStr(query, " FOR EACH STATEMENT\n ");
17284
17285 /* regproc output is already sufficiently quoted */
17286 appendPQExpBuffer(query, "EXECUTE FUNCTION %s(",
17287 tginfo->tgfname);
17288
17289 tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs,
17290 &lentgargs);
17291 p = tgargs;
17292 for (findx = 0; findx < tginfo->tgnargs; findx++)
17293 {
17294 /* find the embedded null that terminates this trigger argument */
17295 size_t tlen = strlen(p);
17296
17297 if (p + tlen >= tgargs + lentgargs)
17298 {
17299 /* hm, not found before end of bytea value... */
17300 pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
17301 tginfo->tgargs,
17302 tginfo->dobj.name,
17303 tbinfo->dobj.name);
17304 exit_nicely(1);
17305 }
17306
17307 if (findx > 0)
17308 appendPQExpBufferStr(query, ", ");
17309 appendStringLiteralAH(query, p, fout);
17310 p += tlen + 1;
17311 }
17312 free(tgargs);
17313 appendPQExpBufferStr(query, ");\n");
17314 }
17315
17316 if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
17317 {
17318 appendPQExpBuffer(query, "\nALTER TABLE %s ",
17319 fmtQualifiedDumpable(tbinfo));
17320 switch (tginfo->tgenabled)
17321 {
17322 case 'D':
17323 case 'f':
17324 appendPQExpBufferStr(query, "DISABLE");
17325 break;
17326 case 'A':
17327 appendPQExpBufferStr(query, "ENABLE ALWAYS");
17328 break;
17329 case 'R':
17330 appendPQExpBufferStr(query, "ENABLE REPLICA");
17331 break;
17332 default:
17333 appendPQExpBufferStr(query, "ENABLE");
17334 break;
17335 }
17336 appendPQExpBuffer(query, " TRIGGER %s;\n",
17337 fmtId(tginfo->dobj.name));
17338 }
17339
17340 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
17341 fmtId(tginfo->dobj.name));
17342
17343 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
17344
17345 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17346 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
17347 ARCHIVE_OPTS(.tag = tag,
17348 .namespace = tbinfo->dobj.namespace->dobj.name,
17349 .owner = tbinfo->rolname,
17350 .description = "TRIGGER",
17351 .section = SECTION_POST_DATA,
17352 .createStmt = query->data,
17353 .dropStmt = delqry->data));
17354
17355 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17356 dumpComment(fout, trigprefix->data, qtabname,
17357 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17358 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
17359
17360 free(tag);
17361 destroyPQExpBuffer(query);
17362 destroyPQExpBuffer(delqry);
17363 destroyPQExpBuffer(trigprefix);
17364 free(qtabname);
17365}
17366
17367/*
17368 * dumpEventTrigger
17369 * write the declaration of one user-defined event trigger
17370 */
17371static void
17372dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
17373{
17374 DumpOptions *dopt = fout->dopt;
17375 PQExpBuffer query;
17376 PQExpBuffer delqry;
17377 char *qevtname;
17378
17379 /* Skip if not to be dumped */
17380 if (!evtinfo->dobj.dump || dopt->dataOnly)
17381 return;
17382
17383 query = createPQExpBuffer();
17384 delqry = createPQExpBuffer();
17385
17386 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
17387
17388 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
17389 appendPQExpBufferStr(query, qevtname);
17390 appendPQExpBufferStr(query, " ON ");
17391 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
17392
17393 if (strcmp("", evtinfo->evttags) != 0)
17394 {
17395 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
17396 appendPQExpBufferStr(query, evtinfo->evttags);
17397 appendPQExpBufferChar(query, ')');
17398 }
17399
17400 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
17401 appendPQExpBufferStr(query, evtinfo->evtfname);
17402 appendPQExpBufferStr(query, "();\n");
17403
17404 if (evtinfo->evtenabled != 'O')
17405 {
17406 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
17407 qevtname);
17408 switch (evtinfo->evtenabled)
17409 {
17410 case 'D':
17411 appendPQExpBufferStr(query, "DISABLE");
17412 break;
17413 case 'A':
17414 appendPQExpBufferStr(query, "ENABLE ALWAYS");
17415 break;
17416 case 'R':
17417 appendPQExpBufferStr(query, "ENABLE REPLICA");
17418 break;
17419 default:
17420 appendPQExpBufferStr(query, "ENABLE");
17421 break;
17422 }
17423 appendPQExpBufferStr(query, ";\n");
17424 }
17425
17426 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
17427 qevtname);
17428
17429 if (dopt->binary_upgrade)
17430 binary_upgrade_extension_member(query, &evtinfo->dobj,
17431 "EVENT TRIGGER", qevtname, NULL);
17432
17433 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17434 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
17435 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
17436 .owner = evtinfo->evtowner,
17437 .description = "EVENT TRIGGER",
17438 .section = SECTION_POST_DATA,
17439 .createStmt = query->data,
17440 .dropStmt = delqry->data));
17441
17442 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17443 dumpComment(fout, "EVENT TRIGGER", qevtname,
17444 NULL, evtinfo->evtowner,
17445 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
17446
17447 destroyPQExpBuffer(query);
17448 destroyPQExpBuffer(delqry);
17449 free(qevtname);
17450}
17451
17452/*
17453 * dumpRule
17454 * Dump a rule
17455 */
17456static void
17457dumpRule(Archive *fout, RuleInfo *rinfo)
17458{
17459 DumpOptions *dopt = fout->dopt;
17460 TableInfo *tbinfo = rinfo->ruletable;
17461 bool is_view;
17462 PQExpBuffer query;
17463 PQExpBuffer cmd;
17464 PQExpBuffer delcmd;
17465 PQExpBuffer ruleprefix;
17466 char *qtabname;
17467 PGresult *res;
17468 char *tag;
17469
17470 /* Skip if not to be dumped */
17471 if (!rinfo->dobj.dump || dopt->dataOnly)
17472 return;
17473
17474 /*
17475 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
17476 * we do not want to dump it as a separate object.
17477 */
17478 if (!rinfo->separate)
17479 return;
17480
17481 /*
17482 * If it's an ON SELECT rule, we want to print it as a view definition,
17483 * instead of a rule.
17484 */
17485 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
17486
17487 query = createPQExpBuffer();
17488 cmd = createPQExpBuffer();
17489 delcmd = createPQExpBuffer();
17490 ruleprefix = createPQExpBuffer();
17491
17492 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17493
17494 if (is_view)
17495 {
17496 PQExpBuffer result;
17497
17498 /*
17499 * We need OR REPLACE here because we'll be replacing a dummy view.
17500 * Otherwise this should look largely like the regular view dump code.
17501 */
17502 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
17503 fmtQualifiedDumpable(tbinfo));
17504 if (nonemptyReloptions(tbinfo->reloptions))
17505 {
17506 appendPQExpBufferStr(cmd, " WITH (");
17507 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
17508 appendPQExpBufferChar(cmd, ')');
17509 }
17510 result = createViewAsClause(fout, tbinfo);
17511 appendPQExpBuffer(cmd, " AS\n%s", result->data);
17512 destroyPQExpBuffer(result);
17513 if (tbinfo->checkoption != NULL)
17514 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
17515 tbinfo->checkoption);
17516 appendPQExpBufferStr(cmd, ";\n");
17517 }
17518 else
17519 {
17520 /* In the rule case, just print pg_get_ruledef's result verbatim */
17521 appendPQExpBuffer(query,
17522 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
17523 rinfo->dobj.catId.oid);
17524
17525 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17526
17527 if (PQntuples(res) != 1)
17528 {
17529 pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
17530 rinfo->dobj.name, tbinfo->dobj.name);
17531 exit_nicely(1);
17532 }
17533
17534 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
17535
17536 PQclear(res);
17537 }
17538
17539 /*
17540 * Add the command to alter the rules replication firing semantics if it
17541 * differs from the default.
17542 */
17543 if (rinfo->ev_enabled != 'O')
17544 {
17545 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
17546 switch (rinfo->ev_enabled)
17547 {
17548 case 'A':
17549 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
17550 fmtId(rinfo->dobj.name));
17551 break;
17552 case 'R':
17553 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
17554 fmtId(rinfo->dobj.name));
17555 break;
17556 case 'D':
17557 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
17558 fmtId(rinfo->dobj.name));
17559 break;
17560 }
17561 }
17562
17563 if (is_view)
17564 {
17565 /*
17566 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
17567 * REPLACE VIEW to replace the rule with something with minimal
17568 * dependencies.
17569 */
17570 PQExpBuffer result;
17571
17572 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
17573 fmtQualifiedDumpable(tbinfo));
17574 result = createDummyViewAsClause(fout, tbinfo);
17575 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
17576 destroyPQExpBuffer(result);
17577 }
17578 else
17579 {
17580 appendPQExpBuffer(delcmd, "DROP RULE %s ",
17581 fmtId(rinfo->dobj.name));
17582 appendPQExpBuffer(delcmd, "ON %s;\n",
17583 fmtQualifiedDumpable(tbinfo));
17584 }
17585
17586 appendPQExpBuffer(ruleprefix, "RULE %s ON",
17587 fmtId(rinfo->dobj.name));
17588
17589 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
17590
17591 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17592 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
17593 ARCHIVE_OPTS(.tag = tag,
17594 .namespace = tbinfo->dobj.namespace->dobj.name,
17595 .owner = tbinfo->rolname,
17596 .description = "RULE",
17597 .section = SECTION_POST_DATA,
17598 .createStmt = cmd->data,
17599 .dropStmt = delcmd->data));
17600
17601 /* Dump rule comments */
17602 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17603 dumpComment(fout, ruleprefix->data, qtabname,
17604 tbinfo->dobj.namespace->dobj.name,
17605 tbinfo->rolname,
17606 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
17607
17608 free(tag);
17609 destroyPQExpBuffer(query);
17610 destroyPQExpBuffer(cmd);
17611 destroyPQExpBuffer(delcmd);
17612 destroyPQExpBuffer(ruleprefix);
17613 free(qtabname);
17614}
17615
17616/*
17617 * getExtensionMembership --- obtain extension membership data
17618 *
17619 * We need to identify objects that are extension members as soon as they're
17620 * loaded, so that we can correctly determine whether they need to be dumped.
17621 * Generally speaking, extension member objects will get marked as *not* to
17622 * be dumped, as they will be recreated by the single CREATE EXTENSION
17623 * command. However, in binary upgrade mode we still need to dump the members
17624 * individually.
17625 */
17626void
17627getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
17628 int numExtensions)
17629{
17630 PQExpBuffer query;
17631 PGresult *res;
17632 int ntups,
17633 nextmembers,
17634 i;
17635 int i_classid,
17636 i_objid,
17637 i_refobjid;
17638 ExtensionMemberId *extmembers;
17639 ExtensionInfo *ext;
17640
17641 /* Nothing to do if no extensions */
17642 if (numExtensions == 0)
17643 return;
17644
17645 query = createPQExpBuffer();
17646
17647 /* refclassid constraint is redundant but may speed the search */
17648 appendPQExpBufferStr(query, "SELECT "
17649 "classid, objid, refobjid "
17650 "FROM pg_depend "
17651 "WHERE refclassid = 'pg_extension'::regclass "
17652 "AND deptype = 'e' "
17653 "ORDER BY 3");
17654
17655 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17656
17657 ntups = PQntuples(res);
17658
17659 i_classid = PQfnumber(res, "classid");
17660 i_objid = PQfnumber(res, "objid");
17661 i_refobjid = PQfnumber(res, "refobjid");
17662
17663 extmembers = (ExtensionMemberId *) pg_malloc(ntups * sizeof(ExtensionMemberId));
17664 nextmembers = 0;
17665
17666 /*
17667 * Accumulate data into extmembers[].
17668 *
17669 * Since we ordered the SELECT by referenced ID, we can expect that
17670 * multiple entries for the same extension will appear together; this
17671 * saves on searches.
17672 */
17673 ext = NULL;
17674
17675 for (i = 0; i < ntups; i++)
17676 {
17677 CatalogId objId;
17678 Oid extId;
17679
17680 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
17681 objId.oid = atooid(PQgetvalue(res, i, i_objid));
17682 extId = atooid(PQgetvalue(res, i, i_refobjid));
17683
17684 if (ext == NULL ||
17685 ext->dobj.catId.oid != extId)
17686 ext = findExtensionByOid(extId);
17687
17688 if (ext == NULL)
17689 {
17690 /* shouldn't happen */
17691 pg_log_warning("could not find referenced extension %u", extId);
17692 continue;
17693 }
17694
17695 extmembers[nextmembers].catId = objId;
17696 extmembers[nextmembers].ext = ext;
17697 nextmembers++;
17698 }
17699
17700 PQclear(res);
17701
17702 /* Remember the data for use later */
17703 setExtensionMembership(extmembers, nextmembers);
17704
17705 destroyPQExpBuffer(query);
17706}
17707
17708/*
17709 * processExtensionTables --- deal with extension configuration tables
17710 *
17711 * There are two parts to this process:
17712 *
17713 * 1. Identify and create dump records for extension configuration tables.
17714 *
17715 * Extensions can mark tables as "configuration", which means that the user
17716 * is able and expected to modify those tables after the extension has been
17717 * loaded. For these tables, we dump out only the data- the structure is
17718 * expected to be handled at CREATE EXTENSION time, including any indexes or
17719 * foreign keys, which brings us to-
17720 *
17721 * 2. Record FK dependencies between configuration tables.
17722 *
17723 * Due to the FKs being created at CREATE EXTENSION time and therefore before
17724 * the data is loaded, we have to work out what the best order for reloading
17725 * the data is, to avoid FK violations when the tables are restored. This is
17726 * not perfect- we can't handle circular dependencies and if any exist they
17727 * will cause an invalid dump to be produced (though at least all of the data
17728 * is included for a user to manually restore). This is currently documented
17729 * but perhaps we can provide a better solution in the future.
17730 */
17731void
17732processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
17733 int numExtensions)
17734{
17735 DumpOptions *dopt = fout->dopt;
17736 PQExpBuffer query;
17737 PGresult *res;
17738 int ntups,
17739 i;
17740 int i_conrelid,
17741 i_confrelid;
17742
17743 /* Nothing to do if no extensions */
17744 if (numExtensions == 0)
17745 return;
17746
17747 /*
17748 * Identify extension configuration tables and create TableDataInfo
17749 * objects for them, ensuring their data will be dumped even though the
17750 * tables themselves won't be.
17751 *
17752 * Note that we create TableDataInfo objects even in schemaOnly mode, ie,
17753 * user data in a configuration table is treated like schema data. This
17754 * seems appropriate since system data in a config table would get
17755 * reloaded by CREATE EXTENSION.
17756 */
17757 for (i = 0; i < numExtensions; i++)
17758 {
17759 ExtensionInfo *curext = &(extinfo[i]);
17760 char *extconfig = curext->extconfig;
17761 char *extcondition = curext->extcondition;
17762 char **extconfigarray = NULL;
17763 char **extconditionarray = NULL;
17764 int nconfigitems;
17765 int nconditionitems;
17766
17767 if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
17768 parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
17769 nconfigitems == nconditionitems)
17770 {
17771 int j;
17772
17773 for (j = 0; j < nconfigitems; j++)
17774 {
17775 TableInfo *configtbl;
17776 Oid configtbloid = atooid(extconfigarray[j]);
17777 bool dumpobj =
17778 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
17779
17780 configtbl = findTableByOid(configtbloid);
17781 if (configtbl == NULL)
17782 continue;
17783
17784 /*
17785 * Tables of not-to-be-dumped extensions shouldn't be dumped
17786 * unless the table or its schema is explicitly included
17787 */
17788 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
17789 {
17790 /* check table explicitly requested */
17791 if (table_include_oids.head != NULL &&
17792 simple_oid_list_member(&table_include_oids,
17793 configtbloid))
17794 dumpobj = true;
17795
17796 /* check table's schema explicitly requested */
17797 if (configtbl->dobj.namespace->dobj.dump &
17798 DUMP_COMPONENT_DATA)
17799 dumpobj = true;
17800 }
17801
17802 /* check table excluded by an exclusion switch */
17803 if (table_exclude_oids.head != NULL &&
17804 simple_oid_list_member(&table_exclude_oids,
17805 configtbloid))
17806 dumpobj = false;
17807
17808 /* check schema excluded by an exclusion switch */
17809 if (simple_oid_list_member(&schema_exclude_oids,
17810 configtbl->dobj.namespace->dobj.catId.oid))
17811 dumpobj = false;
17812
17813 if (dumpobj)
17814 {
17815 makeTableDataInfo(dopt, configtbl);
17816 if (configtbl->dataObj != NULL)
17817 {
17818 if (strlen(extconditionarray[j]) > 0)
17819 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
17820 }
17821 }
17822 }
17823 }
17824 if (extconfigarray)
17825 free(extconfigarray);
17826 if (extconditionarray)
17827 free(extconditionarray);
17828 }
17829
17830 /*
17831 * Now that all the TableInfoData objects have been created for all the
17832 * extensions, check their FK dependencies and register them to try and
17833 * dump the data out in an order that they can be restored in.
17834 *
17835 * Note that this is not a problem for user tables as their FKs are
17836 * recreated after the data has been loaded.
17837 */
17838
17839 query = createPQExpBuffer();
17840
17841 printfPQExpBuffer(query,
17842 "SELECT conrelid, confrelid "
17843 "FROM pg_constraint "
17844 "JOIN pg_depend ON (objid = confrelid) "
17845 "WHERE contype = 'f' "
17846 "AND refclassid = 'pg_extension'::regclass "
17847 "AND classid = 'pg_class'::regclass;");
17848
17849 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17850 ntups = PQntuples(res);
17851
17852 i_conrelid = PQfnumber(res, "conrelid");
17853 i_confrelid = PQfnumber(res, "confrelid");
17854
17855 /* Now get the dependencies and register them */
17856 for (i = 0; i < ntups; i++)
17857 {
17858 Oid conrelid,
17859 confrelid;
17860 TableInfo *reftable,
17861 *contable;
17862
17863 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
17864 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
17865 contable = findTableByOid(conrelid);
17866 reftable = findTableByOid(confrelid);
17867
17868 if (reftable == NULL ||
17869 reftable->dataObj == NULL ||
17870 contable == NULL ||
17871 contable->dataObj == NULL)
17872 continue;
17873
17874 /*
17875 * Make referencing TABLE_DATA object depend on the referenced table's
17876 * TABLE_DATA object.
17877 */
17878 addObjectDependency(&contable->dataObj->dobj,
17879 reftable->dataObj->dobj.dumpId);
17880 }
17881 PQclear(res);
17882 destroyPQExpBuffer(query);
17883}
17884
17885/*
17886 * getDependencies --- obtain available dependency data
17887 */
17888static void
17889getDependencies(Archive *fout)
17890{
17891 PQExpBuffer query;
17892 PGresult *res;
17893 int ntups,
17894 i;
17895 int i_classid,
17896 i_objid,
17897 i_refclassid,
17898 i_refobjid,
17899 i_deptype;
17900 DumpableObject *dobj,
17901 *refdobj;
17902
17903 pg_log_info("reading dependency data");
17904
17905 query = createPQExpBuffer();
17906
17907 /*
17908 * Messy query to collect the dependency data we need. Note that we
17909 * ignore the sub-object column, so that dependencies of or on a column
17910 * look the same as dependencies of or on a whole table.
17911 *
17912 * PIN dependencies aren't interesting, and EXTENSION dependencies were
17913 * already processed by getExtensionMembership.
17914 */
17915 appendPQExpBufferStr(query, "SELECT "
17916 "classid, objid, refclassid, refobjid, deptype "
17917 "FROM pg_depend "
17918 "WHERE deptype != 'p' AND deptype != 'e'\n");
17919
17920 /*
17921 * Since we don't treat pg_amop entries as separate DumpableObjects, we
17922 * have to translate their dependencies into dependencies of their parent
17923 * opfamily. Ignore internal dependencies though, as those will point to
17924 * their parent opclass, which we needn't consider here (and if we did,
17925 * it'd just result in circular dependencies). Also, "loose" opfamily
17926 * entries will have dependencies on their parent opfamily, which we
17927 * should drop since they'd likewise become useless self-dependencies.
17928 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
17929 *
17930 * Skip this for pre-8.3 source servers: pg_opfamily doesn't exist there,
17931 * and the (known) cases where it would matter to have these dependencies
17932 * can't arise anyway.
17933 */
17934 if (fout->remoteVersion >= 80300)
17935 {
17936 appendPQExpBufferStr(query, "UNION ALL\n"
17937 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
17938 "FROM pg_depend d, pg_amop o "
17939 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
17940 "classid = 'pg_amop'::regclass AND objid = o.oid "
17941 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
17942
17943 /* Likewise for pg_amproc entries */
17944 appendPQExpBufferStr(query, "UNION ALL\n"
17945 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
17946 "FROM pg_depend d, pg_amproc p "
17947 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
17948 "classid = 'pg_amproc'::regclass AND objid = p.oid "
17949 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
17950 }
17951
17952 /* Sort the output for efficiency below */
17953 appendPQExpBufferStr(query, "ORDER BY 1,2");
17954
17955 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17956
17957 ntups = PQntuples(res);
17958
17959 i_classid = PQfnumber(res, "classid");
17960 i_objid = PQfnumber(res, "objid");
17961 i_refclassid = PQfnumber(res, "refclassid");
17962 i_refobjid = PQfnumber(res, "refobjid");
17963 i_deptype = PQfnumber(res, "deptype");
17964
17965 /*
17966 * Since we ordered the SELECT by referencing ID, we can expect that
17967 * multiple entries for the same object will appear together; this saves
17968 * on searches.
17969 */
17970 dobj = NULL;
17971
17972 for (i = 0; i < ntups; i++)
17973 {
17974 CatalogId objId;
17975 CatalogId refobjId;
17976 char deptype;
17977
17978 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
17979 objId.oid = atooid(PQgetvalue(res, i, i_objid));
17980 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
17981 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
17982 deptype = *(PQgetvalue(res, i, i_deptype));
17983
17984 if (dobj == NULL ||
17985 dobj->catId.tableoid != objId.tableoid ||
17986 dobj->catId.oid != objId.oid)
17987 dobj = findObjectByCatalogId(objId);
17988
17989 /*
17990 * Failure to find objects mentioned in pg_depend is not unexpected,
17991 * since for example we don't collect info about TOAST tables.
17992 */
17993 if (dobj == NULL)
17994 {
17995#ifdef NOT_USED
17996 pg_log_warning("no referencing object %u %u",
17997 objId.tableoid, objId.oid);
17998#endif
17999 continue;
18000 }
18001
18002 refdobj = findObjectByCatalogId(refobjId);
18003
18004 if (refdobj == NULL)
18005 {
18006#ifdef NOT_USED
18007 pg_log_warning("no referenced object %u %u",
18008 refobjId.tableoid, refobjId.oid);
18009#endif
18010 continue;
18011 }
18012
18013 /*
18014 * Ordinarily, table rowtypes have implicit dependencies on their
18015 * tables. However, for a composite type the implicit dependency goes
18016 * the other way in pg_depend; which is the right thing for DROP but
18017 * it doesn't produce the dependency ordering we need. So in that one
18018 * case, we reverse the direction of the dependency.
18019 */
18020 if (deptype == 'i' &&
18021 dobj->objType == DO_TABLE &&
18022 refdobj->objType == DO_TYPE)
18023 addObjectDependency(refdobj, dobj->dumpId);
18024 else
18025 /* normal case */
18026 addObjectDependency(dobj, refdobj->dumpId);
18027 }
18028
18029 PQclear(res);
18030
18031 destroyPQExpBuffer(query);
18032}
18033
18034
18035/*
18036 * createBoundaryObjects - create dummy DumpableObjects to represent
18037 * dump section boundaries.
18038 */
18039static DumpableObject *
18040createBoundaryObjects(void)
18041{
18042 DumpableObject *dobjs;
18043
18044 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18045
18046 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18047 dobjs[0].catId = nilCatalogId;
18048 AssignDumpId(dobjs + 0);
18049 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18050
18051 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18052 dobjs[1].catId = nilCatalogId;
18053 AssignDumpId(dobjs + 1);
18054 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18055
18056 return dobjs;
18057}
18058
18059/*
18060 * addBoundaryDependencies - add dependencies as needed to enforce the dump
18061 * section boundaries.
18062 */
18063static void
18064addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18065 DumpableObject *boundaryObjs)
18066{
18067 DumpableObject *preDataBound = boundaryObjs + 0;
18068 DumpableObject *postDataBound = boundaryObjs + 1;
18069 int i;
18070
18071 for (i = 0; i < numObjs; i++)
18072 {
18073 DumpableObject *dobj = dobjs[i];
18074
18075 /*
18076 * The classification of object types here must match the SECTION_xxx
18077 * values assigned during subsequent ArchiveEntry calls!
18078 */
18079 switch (dobj->objType)
18080 {
18081 case DO_NAMESPACE:
18082 case DO_EXTENSION:
18083 case DO_TYPE:
18084 case DO_SHELL_TYPE:
18085 case DO_FUNC:
18086 case DO_AGG:
18087 case DO_OPERATOR:
18088 case DO_ACCESS_METHOD:
18089 case DO_OPCLASS:
18090 case DO_OPFAMILY:
18091 case DO_COLLATION:
18092 case DO_CONVERSION:
18093 case DO_TABLE:
18094 case DO_ATTRDEF:
18095 case DO_PROCLANG:
18096 case DO_CAST:
18097 case DO_DUMMY_TYPE:
18098 case DO_TSPARSER:
18099 case DO_TSDICT:
18100 case DO_TSTEMPLATE:
18101 case DO_TSCONFIG:
18102 case DO_FDW:
18103 case DO_FOREIGN_SERVER:
18104 case DO_TRANSFORM:
18105 case DO_BLOB:
18106 /* Pre-data objects: must come before the pre-data boundary */
18107 addObjectDependency(preDataBound, dobj->dumpId);
18108 break;
18109 case DO_TABLE_DATA:
18110 case DO_SEQUENCE_SET:
18111 case DO_BLOB_DATA:
18112 /* Data objects: must come between the boundaries */
18113 addObjectDependency(dobj, preDataBound->dumpId);
18114 addObjectDependency(postDataBound, dobj->dumpId);
18115 break;
18116 case DO_INDEX:
18117 case DO_INDEX_ATTACH:
18118 case DO_STATSEXT:
18119 case DO_REFRESH_MATVIEW:
18120 case DO_TRIGGER:
18121 case DO_EVENT_TRIGGER:
18122 case DO_DEFAULT_ACL:
18123 case DO_POLICY:
18124 case DO_PUBLICATION:
18125 case DO_PUBLICATION_REL:
18126 case DO_SUBSCRIPTION:
18127 /* Post-data objects: must come after the post-data boundary */
18128 addObjectDependency(dobj, postDataBound->dumpId);
18129 break;
18130 case DO_RULE:
18131 /* Rules are post-data, but only if dumped separately */
18132 if (((RuleInfo *) dobj)->separate)
18133 addObjectDependency(dobj, postDataBound->dumpId);
18134 break;
18135 case DO_CONSTRAINT:
18136 case DO_FK_CONSTRAINT:
18137 /* Constraints are post-data, but only if dumped separately */
18138 if (((ConstraintInfo *) dobj)->separate)
18139 addObjectDependency(dobj, postDataBound->dumpId);
18140 break;
18141 case DO_PRE_DATA_BOUNDARY:
18142 /* nothing to do */
18143 break;
18144 case DO_POST_DATA_BOUNDARY:
18145 /* must come after the pre-data boundary */
18146 addObjectDependency(dobj, preDataBound->dumpId);
18147 break;
18148 }
18149 }
18150}
18151
18152
18153/*
18154 * BuildArchiveDependencies - create dependency data for archive TOC entries
18155 *
18156 * The raw dependency data obtained by getDependencies() is not terribly
18157 * useful in an archive dump, because in many cases there are dependency
18158 * chains linking through objects that don't appear explicitly in the dump.
18159 * For example, a view will depend on its _RETURN rule while the _RETURN rule
18160 * will depend on other objects --- but the rule will not appear as a separate
18161 * object in the dump. We need to adjust the view's dependencies to include
18162 * whatever the rule depends on that is included in the dump.
18163 *
18164 * Just to make things more complicated, there are also "special" dependencies
18165 * such as the dependency of a TABLE DATA item on its TABLE, which we must
18166 * not rearrange because pg_restore knows that TABLE DATA only depends on
18167 * its table. In these cases we must leave the dependencies strictly as-is
18168 * even if they refer to not-to-be-dumped objects.
18169 *
18170 * To handle this, the convention is that "special" dependencies are created
18171 * during ArchiveEntry calls, and an archive TOC item that has any such
18172 * entries will not be touched here. Otherwise, we recursively search the
18173 * DumpableObject data structures to build the correct dependencies for each
18174 * archive TOC item.
18175 */
18176static void
18177BuildArchiveDependencies(Archive *fout)
18178{
18179 ArchiveHandle *AH = (ArchiveHandle *) fout;
18180 TocEntry *te;
18181
18182 /* Scan all TOC entries in the archive */
18183 for (te = AH->toc->next; te != AH->toc; te = te->next)
18184 {
18185 DumpableObject *dobj;
18186 DumpId *dependencies;
18187 int nDeps;
18188 int allocDeps;
18189
18190 /* No need to process entries that will not be dumped */
18191 if (te->reqs == 0)
18192 continue;
18193 /* Ignore entries that already have "special" dependencies */
18194 if (te->nDeps > 0)
18195 continue;
18196 /* Otherwise, look up the item's original DumpableObject, if any */
18197 dobj = findObjectByDumpId(te->dumpId);
18198 if (dobj == NULL)
18199 continue;
18200 /* No work if it has no dependencies */
18201 if (dobj->nDeps <= 0)
18202 continue;
18203 /* Set up work array */
18204 allocDeps = 64;
18205 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
18206 nDeps = 0;
18207 /* Recursively find all dumpable dependencies */
18208 findDumpableDependencies(AH, dobj,
18209 &dependencies, &nDeps, &allocDeps);
18210 /* And save 'em ... */
18211 if (nDeps > 0)
18212 {
18213 dependencies = (DumpId *) pg_realloc(dependencies,
18214 nDeps * sizeof(DumpId));
18215 te->dependencies = dependencies;
18216 te->nDeps = nDeps;
18217 }
18218 else
18219 free(dependencies);
18220 }
18221}
18222
18223/* Recursive search subroutine for BuildArchiveDependencies */
18224static void
18225findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj,
18226 DumpId **dependencies, int *nDeps, int *allocDeps)
18227{
18228 int i;
18229
18230 /*
18231 * Ignore section boundary objects: if we search through them, we'll
18232 * report lots of bogus dependencies.
18233 */
18234 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
18235 dobj->objType == DO_POST_DATA_BOUNDARY)
18236 return;
18237
18238 for (i = 0; i < dobj->nDeps; i++)
18239 {
18240 DumpId depid = dobj->dependencies[i];
18241
18242 if (TocIDRequired(AH, depid) != 0)
18243 {
18244 /* Object will be dumped, so just reference it as a dependency */
18245 if (*nDeps >= *allocDeps)
18246 {
18247 *allocDeps *= 2;
18248 *dependencies = (DumpId *) pg_realloc(*dependencies,
18249 *allocDeps * sizeof(DumpId));
18250 }
18251 (*dependencies)[*nDeps] = depid;
18252 (*nDeps)++;
18253 }
18254 else
18255 {
18256 /*
18257 * Object will not be dumped, so recursively consider its deps. We
18258 * rely on the assumption that sortDumpableObjects already broke
18259 * any dependency loops, else we might recurse infinitely.
18260 */
18261 DumpableObject *otherdobj = findObjectByDumpId(depid);
18262
18263 if (otherdobj)
18264 findDumpableDependencies(AH, otherdobj,
18265 dependencies, nDeps, allocDeps);
18266 }
18267 }
18268}
18269
18270
18271/*
18272 * getFormattedTypeName - retrieve a nicely-formatted type name for the
18273 * given type OID.
18274 *
18275 * This does not guarantee to schema-qualify the output, so it should not
18276 * be used to create the target object name for CREATE or ALTER commands.
18277 *
18278 * TODO: there might be some value in caching the results.
18279 */
18280static char *
18281getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
18282{
18283 char *result;
18284 PQExpBuffer query;
18285 PGresult *res;
18286
18287 if (oid == 0)
18288 {
18289 if ((opts & zeroAsOpaque) != 0)
18290 return pg_strdup(g_opaque_type);
18291 else if ((opts & zeroAsAny) != 0)
18292 return pg_strdup("'any'");
18293 else if ((opts & zeroAsStar) != 0)
18294 return pg_strdup("*");
18295 else if ((opts & zeroAsNone) != 0)
18296 return pg_strdup("NONE");
18297 }
18298
18299 query = createPQExpBuffer();
18300 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
18301 oid);
18302
18303 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18304
18305 /* result of format_type is already quoted */
18306 result = pg_strdup(PQgetvalue(res, 0, 0));
18307
18308 PQclear(res);
18309 destroyPQExpBuffer(query);
18310
18311 return result;
18312}
18313
18314/*
18315 * Return a column list clause for the given relation.
18316 *
18317 * Special case: if there are no undropped columns in the relation, return
18318 * "", not an invalid "()" column list.
18319 */
18320static const char *
18321fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
18322{
18323 int numatts = ti->numatts;
18324 char **attnames = ti->attnames;
18325 bool *attisdropped = ti->attisdropped;
18326 char *attgenerated = ti->attgenerated;
18327 bool needComma;
18328 int i;
18329
18330 appendPQExpBufferChar(buffer, '(');
18331 needComma = false;
18332 for (i = 0; i < numatts; i++)
18333 {
18334 if (attisdropped[i])
18335 continue;
18336 if (attgenerated[i])
18337 continue;
18338 if (needComma)
18339 appendPQExpBufferStr(buffer, ", ");
18340 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
18341 needComma = true;
18342 }
18343
18344 if (!needComma)
18345 return ""; /* no undropped columns */
18346
18347 appendPQExpBufferChar(buffer, ')');
18348 return buffer->data;
18349}
18350
18351/*
18352 * Check if a reloptions array is nonempty.
18353 */
18354static bool
18355nonemptyReloptions(const char *reloptions)
18356{
18357 /* Don't want to print it if it's just "{}" */
18358 return (reloptions != NULL && strlen(reloptions) > 2);
18359}
18360
18361/*
18362 * Format a reloptions array and append it to the given buffer.
18363 *
18364 * "prefix" is prepended to the option names; typically it's "" or "toast.".
18365 */
18366static void
18367appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
18368 const char *prefix, Archive *fout)
18369{
18370 bool res;
18371
18372 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
18373 fout->std_strings);
18374 if (!res)
18375 pg_log_warning("could not parse reloptions array");
18376}
18377