PostgreSQL Source Code git master
pg_dump.c
Go to the documentation of this file.
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-2025, 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 "access/attnum.h"
42#include "access/sysattr.h"
43#include "access/transam.h"
44#include "catalog/pg_aggregate_d.h"
45#include "catalog/pg_am_d.h"
46#include "catalog/pg_attribute_d.h"
47#include "catalog/pg_authid_d.h"
48#include "catalog/pg_cast_d.h"
49#include "catalog/pg_class_d.h"
50#include "catalog/pg_constraint_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_publication_d.h"
56#include "catalog/pg_shdepend_d.h"
57#include "catalog/pg_subscription_d.h"
58#include "catalog/pg_type_d.h"
59#include "common/connect.h"
60#include "common/int.h"
61#include "common/relpath.h"
62#include "common/shortest_dec.h"
63#include "compress_io.h"
64#include "dumputils.h"
67#include "filter.h"
68#include "getopt_long.h"
69#include "libpq/libpq-fs.h"
70#include "parallel.h"
71#include "pg_backup_db.h"
72#include "pg_backup_utils.h"
73#include "pg_dump.h"
74#include "storage/block.h"
75
76typedef struct
77{
78 Oid roleoid; /* role's OID */
79 const char *rolename; /* role's name */
81
82typedef struct
83{
84 const char *descr; /* comment for an object */
85 Oid classoid; /* object class (catalog OID) */
86 Oid objoid; /* object OID */
87 int objsubid; /* subobject (table column #) */
89
90typedef struct
91{
92 const char *provider; /* label provider of this security label */
93 const char *label; /* security label for an object */
94 Oid classoid; /* object class (catalog OID) */
95 Oid objoid; /* object OID */
96 int objsubid; /* subobject (table column #) */
98
99typedef struct
100{
101 Oid oid; /* object OID */
102 char relkind; /* object kind */
103 RelFileNumber relfilenumber; /* object filenode */
104 Oid toast_oid; /* toast table OID */
105 RelFileNumber toast_relfilenumber; /* toast table filenode */
106 Oid toast_index_oid; /* toast table index OID */
107 RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
109
110/* sequence types */
111typedef enum SeqType
112{
117
118static const char *const SeqTypeNames[] =
119{
120 [SEQTYPE_SMALLINT] = "smallint",
121 [SEQTYPE_INTEGER] = "integer",
122 [SEQTYPE_BIGINT] = "bigint",
123};
124
126 "array length mismatch");
127
128typedef struct
129{
130 Oid oid; /* sequence OID */
131 SeqType seqtype; /* data type of sequence */
132 bool cycled; /* whether sequence cycles */
133 int64 minv; /* minimum value */
134 int64 maxv; /* maximum value */
135 int64 startv; /* start value */
136 int64 incby; /* increment value */
137 int64 cache; /* cache size */
138 int64 last_value; /* last value of sequence */
139 bool is_called; /* whether nextval advances before returning */
141
142typedef enum OidOptions
143{
148
149/* global decls */
150static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
151
152static Oid g_last_builtin_oid; /* value of the last builtin oid */
153
154/* The specified names/patterns should to match at least one entity */
155static int strict_names = 0;
156
158
159/*
160 * Object inclusion/exclusion lists
161 *
162 * The string lists record the patterns given by command-line switches,
163 * which we then convert to lists of OIDs of matching objects.
164 */
166static SimpleOidList schema_include_oids = {NULL, NULL};
168static SimpleOidList schema_exclude_oids = {NULL, NULL};
169
172static SimpleOidList table_include_oids = {NULL, NULL};
175static SimpleOidList table_exclude_oids = {NULL, NULL};
179
182
185
188
189static const CatalogId nilCatalogId = {0, 0};
190
191/* override for standard extra_float_digits setting */
192static bool have_extra_float_digits = false;
194
195/* sorted table of role names */
196static RoleNameItem *rolenames = NULL;
197static int nrolenames = 0;
198
199/* sorted table of comments */
200static CommentItem *comments = NULL;
201static int ncomments = 0;
202
203/* sorted table of security labels */
204static SecLabelItem *seclabels = NULL;
205static int nseclabels = 0;
206
207/* sorted table of pg_class information for binary upgrade */
210
211/* sorted table of sequences */
212static SequenceItem *sequences = NULL;
213static int nsequences = 0;
214
215/*
216 * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
217 * as a dependency for pg_shdepend and any large object comments/seclabels.
218 */
220
221/* Maximum number of relations to fetch in a fetchAttributeStats() call. */
222#define MAX_ATTR_STATS_RELS 64
223
224/*
225 * The default number of rows per INSERT when
226 * --inserts is specified without --rows-per-insert
227 */
228#define DUMP_DEFAULT_ROWS_PER_INSERT 1
229
230/*
231 * Maximum number of large objects to group into a single ArchiveEntry.
232 * At some point we might want to make this user-controllable, but for now
233 * a hard-wired setting will suffice.
234 */
235#define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
236
237/*
238 * Macro for producing quoted, schema-qualified name of a dumpable object.
239 */
240#define fmtQualifiedDumpable(obj) \
241 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
242 (obj)->dobj.name)
243
244static void help(const char *progname);
245static void setup_connection(Archive *AH,
246 const char *dumpencoding, const char *dumpsnapshot,
247 char *use_role);
249static void expand_schema_name_patterns(Archive *fout,
250 SimpleStringList *patterns,
251 SimpleOidList *oids,
252 bool strict_names);
254 SimpleStringList *patterns,
255 SimpleOidList *oids,
256 bool strict_names);
258 SimpleStringList *patterns,
259 SimpleOidList *oids);
260static void expand_table_name_patterns(Archive *fout,
261 SimpleStringList *patterns,
262 SimpleOidList *oids,
263 bool strict_names,
264 bool with_child_tables);
265static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
266 const char *pattern);
267
268static NamespaceInfo *findNamespace(Oid nsoid);
269static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
270static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
271static const char *getRoleName(const char *roleoid_str);
272static void collectRoleNames(Archive *fout);
273static void getAdditionalACLs(Archive *fout);
274static void dumpCommentExtended(Archive *fout, const char *type,
275 const char *name, const char *namespace,
276 const char *owner, CatalogId catalogId,
277 int subid, DumpId dumpId,
278 const char *initdb_comment);
279static inline void dumpComment(Archive *fout, const char *type,
280 const char *name, const char *namespace,
281 const char *owner, CatalogId catalogId,
282 int subid, DumpId dumpId);
283static int findComments(Oid classoid, Oid objoid, CommentItem **items);
284static void collectComments(Archive *fout);
285static void dumpSecLabel(Archive *fout, const char *type, const char *name,
286 const char *namespace, const char *owner,
287 CatalogId catalogId, int subid, DumpId dumpId);
288static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
289static void collectSecLabels(Archive *fout);
290static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
291static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
292static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
293static void dumpType(Archive *fout, const TypeInfo *tyinfo);
294static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
295static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
296static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
297static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
298static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
299static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
300static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
301 PGresult *res);
302static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
303static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
304static void dumpFunc(Archive *fout, const FuncInfo *finfo);
305static void dumpCast(Archive *fout, const CastInfo *cast);
306static void dumpTransform(Archive *fout, const TransformInfo *transform);
307static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
308static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
309static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
310static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
311static void dumpCollation(Archive *fout, const CollInfo *collinfo);
312static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
313static void dumpRule(Archive *fout, const RuleInfo *rinfo);
314static void dumpAgg(Archive *fout, const AggInfo *agginfo);
315static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
316static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
317static void dumpTable(Archive *fout, const TableInfo *tbinfo);
318static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
319static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
320static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
321static void collectSequences(Archive *fout);
322static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
323static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
324static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
325static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
326static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
327static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
328static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
329static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
330static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
331static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
332static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
333static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
334static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
335static void dumpUserMappings(Archive *fout,
336 const char *servername, const char *namespace,
337 const char *owner, CatalogId catalogId, DumpId dumpId);
338static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
339
340static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
341 const char *type, const char *name, const char *subname,
342 const char *nspname, const char *tag, const char *owner,
343 const DumpableAcl *dacl);
344
345static void getDependencies(Archive *fout);
346static void BuildArchiveDependencies(Archive *fout);
347static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
348 DumpId **dependencies, int *nDeps, int *allocDeps);
349
351static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
352 DumpableObject *boundaryObjs);
353
354static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
355static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
356static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
357static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
359static void getTableDataFKConstraints(void);
360static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
361 TableInfo *tbinfo, int j,
362 int i_notnull_name,
363 int i_notnull_comment,
364 int i_notnull_invalidoid,
365 int i_notnull_noinherit,
366 int i_notnull_islocal,
367 PQExpBuffer *invalidnotnulloids);
368static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
369 bool is_agg);
370static char *format_function_signature(Archive *fout,
371 const FuncInfo *finfo, bool honor_quotes);
372static char *convertRegProcReference(const char *proc);
373static char *getFormattedOperatorName(const char *oproid);
374static char *convertTSFunction(Archive *fout, Oid funcOid);
375static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
376static void getLOs(Archive *fout);
377static void dumpLO(Archive *fout, const LoInfo *loinfo);
378static int dumpLOs(Archive *fout, const void *arg);
379static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
380static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
381static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
382static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
383static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
384static void dumpDatabase(Archive *fout);
385static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
386 const char *dbname, Oid dboid);
387static void dumpEncoding(Archive *AH);
388static void dumpStdStrings(Archive *AH);
389static void dumpSearchPath(Archive *AH);
391 PQExpBuffer upgrade_buffer,
392 Oid pg_type_oid,
393 bool force_array_type,
394 bool include_multirange_type);
396 PQExpBuffer upgrade_buffer,
397 const TableInfo *tbinfo);
398static void collectBinaryUpgradeClassOids(Archive *fout);
400 PQExpBuffer upgrade_buffer,
401 Oid pg_class_oid);
402static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
403 const DumpableObject *dobj,
404 const char *objtype,
405 const char *objname,
406 const char *objnamespace);
407static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
408static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
409static bool nonemptyReloptions(const char *reloptions);
410static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
411 const char *prefix, Archive *fout);
412static char *get_synchronized_snapshot(Archive *fout);
413static void set_restrict_relation_kind(Archive *AH, const char *value);
414static void setupDumpWorker(Archive *AH);
415static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
416static bool forcePartitionRootLoad(const TableInfo *tbinfo);
417static void read_dump_filters(const char *filename, DumpOptions *dopt);
418
419
420int
421main(int argc, char **argv)
422{
423 int c;
424 const char *filename = NULL;
425 const char *format = "p";
426 TableInfo *tblinfo;
427 int numTables;
428 DumpableObject **dobjs;
429 int numObjs;
430 DumpableObject *boundaryObjs;
431 int i;
432 int optindex;
433 RestoreOptions *ropt;
434 Archive *fout; /* the script file */
435 bool g_verbose = false;
436 const char *dumpencoding = NULL;
437 const char *dumpsnapshot = NULL;
438 char *use_role = NULL;
439 int numWorkers = 1;
440 int plainText = 0;
441 ArchiveFormat archiveFormat = archUnknown;
442 ArchiveMode archiveMode;
443 pg_compress_specification compression_spec = {0};
444 char *compression_detail = NULL;
445 char *compression_algorithm_str = "none";
446 char *error_detail = NULL;
447 bool user_compression_defined = false;
449 bool data_only = false;
450 bool schema_only = false;
451 bool statistics_only = false;
452 bool with_statistics = false;
453 bool no_data = false;
454 bool no_schema = false;
455 bool no_statistics = false;
456
457 static DumpOptions dopt;
458
459 static struct option long_options[] = {
460 {"data-only", no_argument, NULL, 'a'},
461 {"blobs", no_argument, NULL, 'b'},
462 {"large-objects", no_argument, NULL, 'b'},
463 {"no-blobs", no_argument, NULL, 'B'},
464 {"no-large-objects", no_argument, NULL, 'B'},
465 {"clean", no_argument, NULL, 'c'},
466 {"create", no_argument, NULL, 'C'},
467 {"dbname", required_argument, NULL, 'd'},
468 {"extension", required_argument, NULL, 'e'},
469 {"file", required_argument, NULL, 'f'},
470 {"format", required_argument, NULL, 'F'},
471 {"host", required_argument, NULL, 'h'},
472 {"jobs", 1, NULL, 'j'},
473 {"no-reconnect", no_argument, NULL, 'R'},
474 {"no-owner", no_argument, NULL, 'O'},
475 {"port", required_argument, NULL, 'p'},
476 {"schema", required_argument, NULL, 'n'},
477 {"exclude-schema", required_argument, NULL, 'N'},
478 {"schema-only", no_argument, NULL, 's'},
479 {"superuser", required_argument, NULL, 'S'},
480 {"table", required_argument, NULL, 't'},
481 {"exclude-table", required_argument, NULL, 'T'},
482 {"no-password", no_argument, NULL, 'w'},
483 {"password", no_argument, NULL, 'W'},
484 {"username", required_argument, NULL, 'U'},
485 {"verbose", no_argument, NULL, 'v'},
486 {"no-privileges", no_argument, NULL, 'x'},
487 {"no-acl", no_argument, NULL, 'x'},
488 {"compress", required_argument, NULL, 'Z'},
489 {"encoding", required_argument, NULL, 'E'},
490 {"help", no_argument, NULL, '?'},
491 {"version", no_argument, NULL, 'V'},
492
493 /*
494 * the following options don't have an equivalent short option letter
495 */
496 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
497 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
498 {"column-inserts", no_argument, &dopt.column_inserts, 1},
499 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
500 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
501 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
502 {"exclude-table-data", required_argument, NULL, 4},
503 {"extra-float-digits", required_argument, NULL, 8},
504 {"if-exists", no_argument, &dopt.if_exists, 1},
505 {"inserts", no_argument, NULL, 9},
506 {"lock-wait-timeout", required_argument, NULL, 2},
507 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
508 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
509 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
510 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
511 {"role", required_argument, NULL, 3},
512 {"section", required_argument, NULL, 5},
513 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
514 {"snapshot", required_argument, NULL, 6},
515 {"statistics", no_argument, NULL, 22},
516 {"statistics-only", no_argument, NULL, 18},
517 {"strict-names", no_argument, &strict_names, 1},
518 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
519 {"no-comments", no_argument, &dopt.no_comments, 1},
520 {"no-data", no_argument, NULL, 19},
521 {"no-policies", no_argument, &dopt.no_policies, 1},
522 {"no-publications", no_argument, &dopt.no_publications, 1},
523 {"no-schema", no_argument, NULL, 20},
524 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
525 {"no-statistics", no_argument, NULL, 21},
526 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
527 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
528 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
529 {"no-sync", no_argument, NULL, 7},
530 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
531 {"rows-per-insert", required_argument, NULL, 10},
532 {"include-foreign-data", required_argument, NULL, 11},
533 {"table-and-children", required_argument, NULL, 12},
534 {"exclude-table-and-children", required_argument, NULL, 13},
535 {"exclude-table-data-and-children", required_argument, NULL, 14},
536 {"sync-method", required_argument, NULL, 15},
537 {"filter", required_argument, NULL, 16},
538 {"exclude-extension", required_argument, NULL, 17},
539 {"sequence-data", no_argument, &dopt.sequence_data, 1},
540 {"restrict-key", required_argument, NULL, 25},
541
542 {NULL, 0, NULL, 0}
543 };
544
545 pg_logging_init(argv[0]);
547 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
548
549 /*
550 * Initialize what we need for parallel execution, especially for thread
551 * support on Windows.
552 */
554
555 progname = get_progname(argv[0]);
556
557 if (argc > 1)
558 {
559 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 {
561 help(progname);
562 exit_nicely(0);
563 }
564 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 {
566 puts("pg_dump (PostgreSQL) " PG_VERSION);
567 exit_nicely(0);
568 }
569 }
570
571 InitDumpOptions(&dopt);
572
573 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 long_options, &optindex)) != -1)
575 {
576 switch (c)
577 {
578 case 'a': /* Dump data only */
579 data_only = true;
580 break;
581
582 case 'b': /* Dump LOs */
583 dopt.outputLOs = true;
584 break;
585
586 case 'B': /* Don't dump LOs */
587 dopt.dontOutputLOs = true;
588 break;
589
590 case 'c': /* clean (i.e., drop) schema prior to create */
591 dopt.outputClean = 1;
592 break;
593
594 case 'C': /* Create DB */
595 dopt.outputCreateDB = 1;
596 break;
597
598 case 'd': /* database name */
600 break;
601
602 case 'e': /* include extension(s) */
604 dopt.include_everything = false;
605 break;
606
607 case 'E': /* Dump encoding */
608 dumpencoding = pg_strdup(optarg);
609 break;
610
611 case 'f':
613 break;
614
615 case 'F':
617 break;
618
619 case 'h': /* server host */
621 break;
622
623 case 'j': /* number of dump jobs */
624 if (!option_parse_int(optarg, "-j/--jobs", 1,
626 &numWorkers))
627 exit_nicely(1);
628 break;
629
630 case 'n': /* include schema(s) */
632 dopt.include_everything = false;
633 break;
634
635 case 'N': /* exclude schema(s) */
637 break;
638
639 case 'O': /* Don't reconnect to match owner */
640 dopt.outputNoOwner = 1;
641 break;
642
643 case 'p': /* server port */
645 break;
646
647 case 'R':
648 /* no-op, still accepted for backwards compatibility */
649 break;
650
651 case 's': /* dump schema only */
652 schema_only = true;
653 break;
654
655 case 'S': /* Username for superuser in plain text output */
657 break;
658
659 case 't': /* include table(s) */
661 dopt.include_everything = false;
662 break;
663
664 case 'T': /* exclude table(s) */
666 break;
667
668 case 'U':
670 break;
671
672 case 'v': /* verbose */
673 g_verbose = true;
675 break;
676
677 case 'w':
679 break;
680
681 case 'W':
683 break;
684
685 case 'x': /* skip ACL dump */
686 dopt.aclsSkip = true;
687 break;
688
689 case 'Z': /* Compression */
690 parse_compress_options(optarg, &compression_algorithm_str,
691 &compression_detail);
692 user_compression_defined = true;
693 break;
694
695 case 0:
696 /* This covers the long options. */
697 break;
698
699 case 2: /* lock-wait-timeout */
701 break;
702
703 case 3: /* SET ROLE */
704 use_role = pg_strdup(optarg);
705 break;
706
707 case 4: /* exclude table(s) data */
709 break;
710
711 case 5: /* section */
713 break;
714
715 case 6: /* snapshot */
716 dumpsnapshot = pg_strdup(optarg);
717 break;
718
719 case 7: /* no-sync */
720 dosync = false;
721 break;
722
723 case 8:
725 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
727 exit_nicely(1);
728 break;
729
730 case 9: /* inserts */
731
732 /*
733 * dump_inserts also stores --rows-per-insert, careful not to
734 * overwrite that.
735 */
736 if (dopt.dump_inserts == 0)
738 break;
739
740 case 10: /* rows per insert */
741 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
742 &dopt.dump_inserts))
743 exit_nicely(1);
744 break;
745
746 case 11: /* include foreign data */
748 optarg);
749 break;
750
751 case 12: /* include table(s) and their children */
753 optarg);
754 dopt.include_everything = false;
755 break;
756
757 case 13: /* exclude table(s) and their children */
759 optarg);
760 break;
761
762 case 14: /* exclude data of table(s) and children */
764 optarg);
765 break;
766
767 case 15:
769 exit_nicely(1);
770 break;
771
772 case 16: /* read object filters from file */
774 break;
775
776 case 17: /* exclude extension(s) */
778 optarg);
779 break;
780
781 case 18:
782 statistics_only = true;
783 break;
784
785 case 19:
786 no_data = true;
787 break;
788
789 case 20:
790 no_schema = true;
791 break;
792
793 case 21:
794 no_statistics = true;
795 break;
796
797 case 22:
798 with_statistics = true;
799 break;
800
801 case 25:
803 break;
804
805 default:
806 /* getopt_long already emitted a complaint */
807 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
808 exit_nicely(1);
809 }
810 }
811
812 /*
813 * Non-option argument specifies database name as long as it wasn't
814 * already specified with -d / --dbname
815 */
816 if (optind < argc && dopt.cparams.dbname == NULL)
817 dopt.cparams.dbname = argv[optind++];
818
819 /* Complain if any arguments remain */
820 if (optind < argc)
821 {
822 pg_log_error("too many command-line arguments (first is \"%s\")",
823 argv[optind]);
824 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
825 exit_nicely(1);
826 }
827
828 /* --column-inserts implies --inserts */
829 if (dopt.column_inserts && dopt.dump_inserts == 0)
831
832 /* reject conflicting "-only" options */
833 if (data_only && schema_only)
834 pg_fatal("options %s and %s cannot be used together",
835 "-s/--schema-only", "-a/--data-only");
836 if (schema_only && statistics_only)
837 pg_fatal("options %s and %s cannot be used together",
838 "-s/--schema-only", "--statistics-only");
839 if (data_only && statistics_only)
840 pg_fatal("options %s and %s cannot be used together",
841 "-a/--data-only", "--statistics-only");
842
843 /* reject conflicting "-only" and "no-" options */
844 if (data_only && no_data)
845 pg_fatal("options %s and %s cannot be used together",
846 "-a/--data-only", "--no-data");
847 if (schema_only && no_schema)
848 pg_fatal("options %s and %s cannot be used together",
849 "-s/--schema-only", "--no-schema");
851 pg_fatal("options %s and %s cannot be used together",
852 "--statistics-only", "--no-statistics");
853
854 /* reject conflicting "no-" options */
856 pg_fatal("options %s and %s cannot be used together",
857 "--statistics", "--no-statistics");
858
859 /* reject conflicting "-only" options */
860 if (data_only && with_statistics)
861 pg_fatal("options %s and %s cannot be used together",
862 "-a/--data-only", "--statistics");
863 if (schema_only && with_statistics)
864 pg_fatal("options %s and %s cannot be used together",
865 "-s/--schema-only", "--statistics");
866
867 if (schema_only && foreign_servers_include_patterns.head != NULL)
868 pg_fatal("options %s and %s cannot be used together",
869 "-s/--schema-only", "--include-foreign-data");
870
871 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
872 pg_fatal("option %s is not supported with parallel backup",
873 "--include-foreign-data");
874
875 if (data_only && dopt.outputClean)
876 pg_fatal("options %s and %s cannot be used together",
877 "-c/--clean", "-a/--data-only");
878
879 if (dopt.if_exists && !dopt.outputClean)
880 pg_fatal("option %s requires option %s",
881 "--if-exists", "-c/--clean");
882
883 /*
884 * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
885 * "--schema-only --no-schema", will have already caused an error in one
886 * of the checks above.
887 */
888 dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
889 data_only) && !no_data;
890 dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
891 schema_only) && !no_schema;
892 dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
894
895
896 /*
897 * --inserts are already implied above if --column-inserts or
898 * --rows-per-insert were specified.
899 */
900 if (dopt.do_nothing && dopt.dump_inserts == 0)
901 pg_fatal("option %s requires option %s, %s, or %s",
902 "--on-conflict-do-nothing",
903 "--inserts", "--rows-per-insert", "--column-inserts");
904
905 /* Identify archive format to emit */
906 archiveFormat = parseArchiveFormat(format, &archiveMode);
907
908 /* archiveFormat specific setup */
909 if (archiveFormat == archNull)
910 {
911 plainText = 1;
912
913 /*
914 * If you don't provide a restrict key, one will be appointed for you.
915 */
916 if (!dopt.restrict_key)
918 if (!dopt.restrict_key)
919 pg_fatal("could not generate restrict key");
921 pg_fatal("invalid restrict key");
922 }
923 else if (dopt.restrict_key)
924 pg_fatal("option %s can only be used with %s",
925 "--restrict-key", "--format=plain");
926
927 /*
928 * Custom and directory formats are compressed by default with gzip when
929 * available, not the others. If gzip is not available, no compression is
930 * done by default.
931 */
932 if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
933 !user_compression_defined)
934 {
935#ifdef HAVE_LIBZ
936 compression_algorithm_str = "gzip";
937#else
938 compression_algorithm_str = "none";
939#endif
940 }
941
942 /*
943 * Compression options
944 */
945 if (!parse_compress_algorithm(compression_algorithm_str,
947 pg_fatal("unrecognized compression algorithm: \"%s\"",
948 compression_algorithm_str);
949
951 &compression_spec);
952 error_detail = validate_compress_specification(&compression_spec);
953 if (error_detail != NULL)
954 pg_fatal("invalid compression specification: %s",
955 error_detail);
956
957 error_detail = supports_compression(compression_spec);
958 if (error_detail != NULL)
959 pg_fatal("%s", error_detail);
960
961 /*
962 * Disable support for zstd workers for now - these are based on
963 * threading, and it's unclear how it interacts with parallel dumps on
964 * platforms where that relies on threads too (e.g. Windows).
965 */
966 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
967 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
968 "workers");
969
970 /*
971 * If emitting an archive format, we always want to emit a DATABASE item,
972 * in case --create is specified at pg_restore time.
973 */
974 if (!plainText)
975 dopt.outputCreateDB = 1;
976
977 /* Parallel backup only in the directory archive format so far */
978 if (archiveFormat != archDirectory && numWorkers > 1)
979 pg_fatal("parallel backup only supported by the directory format");
980
981 /* Open the output file */
982 fout = CreateArchive(filename, archiveFormat, compression_spec,
983 dosync, archiveMode, setupDumpWorker, sync_method);
984
985 /* Make dump options accessible right away */
986 SetArchiveOptions(fout, &dopt, NULL);
987
988 /* Register the cleanup hook */
990
991 /* Let the archiver know how noisy to be */
992 fout->verbose = g_verbose;
993
994
995 /*
996 * We allow the server to be back to 9.2, and up to any minor release of
997 * our own major version. (See also version check in pg_dumpall.c.)
998 */
999 fout->minRemoteVersion = 90200;
1000 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
1001
1002 fout->numWorkers = numWorkers;
1003
1004 /*
1005 * Open the database using the Archiver, so it knows about it. Errors mean
1006 * death.
1007 */
1008 ConnectDatabaseAhx(fout, &dopt.cparams, false);
1009 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
1010
1011 /*
1012 * On hot standbys, never try to dump unlogged table data, since it will
1013 * just throw an error.
1014 */
1015 if (fout->isStandby)
1016 dopt.no_unlogged_table_data = true;
1017
1018 /*
1019 * Find the last built-in OID, if needed (prior to 8.1)
1020 *
1021 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1022 */
1024
1025 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1026
1027 /* Expand schema selection patterns into OID lists */
1028 if (schema_include_patterns.head != NULL)
1029 {
1032 strict_names);
1033 if (schema_include_oids.head == NULL)
1034 pg_fatal("no matching schemas were found");
1035 }
1038 false);
1039 /* non-matching exclusion patterns aren't an error */
1040
1041 /* Expand table selection patterns into OID lists */
1044 strict_names, false);
1047 strict_names, true);
1048 if ((table_include_patterns.head != NULL ||
1050 table_include_oids.head == NULL)
1051 pg_fatal("no matching tables were found");
1052
1055 false, false);
1058 false, true);
1059
1062 false, false);
1065 false, true);
1066
1069
1070 /* non-matching exclusion patterns aren't an error */
1071
1072 /* Expand extension selection patterns into OID lists */
1073 if (extension_include_patterns.head != NULL)
1074 {
1077 strict_names);
1078 if (extension_include_oids.head == NULL)
1079 pg_fatal("no matching extensions were found");
1080 }
1083 false);
1084 /* non-matching exclusion patterns aren't an error */
1085
1086 /*
1087 * Dumping LOs is the default for dumps where an inclusion switch is not
1088 * used (an "include everything" dump). -B can be used to exclude LOs
1089 * from those dumps. -b can be used to include LOs even when an inclusion
1090 * switch is used.
1091 *
1092 * -s means "schema only" and LOs are data, not schema, so we never
1093 * include LOs when -s is used.
1094 */
1095 if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1096 dopt.outputLOs = true;
1097
1098 /*
1099 * Collect role names so we can map object owner OIDs to names.
1100 */
1101 collectRoleNames(fout);
1102
1103 /*
1104 * Now scan the database and create DumpableObject structs for all the
1105 * objects we intend to dump.
1106 */
1107 tblinfo = getSchemaData(fout, &numTables);
1108
1109 if (dopt.dumpData)
1110 {
1111 getTableData(&dopt, tblinfo, numTables, 0);
1113 if (!dopt.dumpSchema)
1115 }
1116
1117 if (!dopt.dumpData && dopt.sequence_data)
1118 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1119
1120 /*
1121 * For binary upgrade mode, dump pg_largeobject_metadata and the
1122 * associated pg_shdepend rows. This is faster to restore than the
1123 * equivalent set of large object commands. We can only do this for
1124 * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1125 * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1126 */
1127 if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1128 {
1129 TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1130 TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1131
1132 makeTableDataInfo(&dopt, lo_metadata);
1133 makeTableDataInfo(&dopt, shdepend);
1134
1135 /*
1136 * Save pg_largeobject_metadata's dump ID for use as a dependency for
1137 * pg_shdepend and any large object comments/seclabels.
1138 */
1139 lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1141
1142 /*
1143 * Only dump large object shdepend rows for this database.
1144 */
1145 shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1146 "AND dbid = (SELECT oid FROM pg_database "
1147 " WHERE datname = current_database())";
1148
1149 /*
1150 * If upgrading from v16 or newer, only dump large objects with
1151 * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1152 * pg_largeobject_metadata's files (which is usually faster) but we
1153 * still need to dump LOs with comments/seclabels here so that the
1154 * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1155 * can't copy/link the files from older versions because aclitem
1156 * (needed by pg_largeobject_metadata.lomacl) changed its storage
1157 * format in v16.
1158 */
1159 if (fout->remoteVersion >= 160000)
1160 lo_metadata->dataObj->filtercond = "WHERE oid IN "
1161 "(SELECT objoid FROM pg_description "
1162 "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1163 "UNION SELECT objoid FROM pg_seclabel "
1164 "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1165 }
1166
1167 /*
1168 * In binary-upgrade mode, we do not have to worry about the actual LO
1169 * data or the associated metadata that resides in the pg_largeobject and
1170 * pg_largeobject_metadata tables, respectively.
1171 *
1172 * However, we do need to collect LO information as there may be comments
1173 * or other information on LOs that we do need to dump out.
1174 */
1175 if (dopt.outputLOs || dopt.binary_upgrade)
1176 getLOs(fout);
1177
1178 /*
1179 * Collect dependency data to assist in ordering the objects.
1180 */
1181 getDependencies(fout);
1182
1183 /*
1184 * Collect ACLs, comments, and security labels, if wanted.
1185 */
1186 if (!dopt.aclsSkip)
1187 getAdditionalACLs(fout);
1188 if (!dopt.no_comments)
1189 collectComments(fout);
1190 if (!dopt.no_security_labels)
1191 collectSecLabels(fout);
1192
1193 /* For binary upgrade mode, collect required pg_class information. */
1194 if (dopt.binary_upgrade)
1196
1197 /* Collect sequence information. */
1198 collectSequences(fout);
1199
1200 /* Lastly, create dummy objects to represent the section boundaries */
1201 boundaryObjs = createBoundaryObjects();
1202
1203 /* Get pointers to all the known DumpableObjects */
1204 getDumpableObjects(&dobjs, &numObjs);
1205
1206 /*
1207 * Add dummy dependencies to enforce the dump section ordering.
1208 */
1209 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1210
1211 /*
1212 * Sort the objects into a safe dump order (no forward references).
1213 *
1214 * We rely on dependency information to help us determine a safe order, so
1215 * the initial sort is mostly for cosmetic purposes: we sort by name to
1216 * ensure that logically identical schemas will dump identically.
1217 */
1218 sortDumpableObjectsByTypeName(dobjs, numObjs);
1219
1220 sortDumpableObjects(dobjs, numObjs,
1221 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1222
1223 /*
1224 * Create archive TOC entries for all the objects to be dumped, in a safe
1225 * order.
1226 */
1227
1228 /*
1229 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1230 */
1231 dumpEncoding(fout);
1232 dumpStdStrings(fout);
1233 dumpSearchPath(fout);
1234
1235 /* The database items are always next, unless we don't want them at all */
1236 if (dopt.outputCreateDB)
1237 dumpDatabase(fout);
1238
1239 /* Now the rearrangeable objects. */
1240 for (i = 0; i < numObjs; i++)
1241 dumpDumpableObject(fout, dobjs[i]);
1242
1243 /*
1244 * Set up options info to ensure we dump what we want.
1245 */
1246 ropt = NewRestoreOptions();
1247 ropt->filename = filename;
1248
1249 /* if you change this list, see dumpOptionsFromRestoreOptions */
1250 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1251 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1252 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1253 ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1255 ropt->dropSchema = dopt.outputClean;
1256 ropt->dumpData = dopt.dumpData;
1257 ropt->dumpSchema = dopt.dumpSchema;
1258 ropt->dumpStatistics = dopt.dumpStatistics;
1259 ropt->if_exists = dopt.if_exists;
1260 ropt->column_inserts = dopt.column_inserts;
1261 ropt->dumpSections = dopt.dumpSections;
1262 ropt->aclsSkip = dopt.aclsSkip;
1263 ropt->superuser = dopt.outputSuperuser;
1264 ropt->createDB = dopt.outputCreateDB;
1265 ropt->noOwner = dopt.outputNoOwner;
1266 ropt->noTableAm = dopt.outputNoTableAm;
1267 ropt->noTablespace = dopt.outputNoTablespaces;
1269 ropt->use_setsessauth = dopt.use_setsessauth;
1271 ropt->dump_inserts = dopt.dump_inserts;
1272 ropt->no_comments = dopt.no_comments;
1273 ropt->no_policies = dopt.no_policies;
1274 ropt->no_publications = dopt.no_publications;
1277 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1280 ropt->sequence_data = dopt.sequence_data;
1281 ropt->binary_upgrade = dopt.binary_upgrade;
1282 ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1283
1284 ropt->compression_spec = compression_spec;
1285
1286 ropt->suppressDumpWarnings = true; /* We've already shown them */
1287
1288 SetArchiveOptions(fout, &dopt, ropt);
1289
1290 /* Mark which entries should be output */
1292
1293 /*
1294 * The archive's TOC entries are now marked as to which ones will actually
1295 * be output, so we can set up their dependency lists properly. This isn't
1296 * necessary for plain-text output, though.
1297 */
1298 if (!plainText)
1300
1301 /*
1302 * And finally we can do the actual output.
1303 *
1304 * Note: for non-plain-text output formats, the output file is written
1305 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1306 * right now.
1307 */
1308 if (plainText)
1309 RestoreArchive(fout);
1310
1311 CloseArchive(fout);
1312
1313 exit_nicely(0);
1314}
1315
1316
1317static void
1318help(const char *progname)
1319{
1320 printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1321 printf(_("Usage:\n"));
1322 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1323
1324 printf(_("\nGeneral options:\n"));
1325 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1326 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1327 " plain text (default))\n"));
1328 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1329 printf(_(" -v, --verbose verbose mode\n"));
1330 printf(_(" -V, --version output version information, then exit\n"));
1331 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1332 " compress as specified\n"));
1333 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1334 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1335 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1336 printf(_(" -?, --help show this help, then exit\n"));
1337
1338 printf(_("\nOptions controlling the output content:\n"));
1339 printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1340 printf(_(" -b, --large-objects include large objects in dump\n"));
1341 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1342 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1343 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1344 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1345 printf(_(" -C, --create include commands to create database in dump\n"));
1346 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1347 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1348 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1349 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1350 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1351 " plain-text format\n"));
1352 printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1353 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1354 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1355 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1356 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1357 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1358 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1359 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1360 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1361 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1362 " access to)\n"));
1363 printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1364 printf(_(" --exclude-table-and-children=PATTERN\n"
1365 " do NOT dump the specified table(s), including\n"
1366 " child and partition tables\n"));
1367 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1368 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1369 " do NOT dump data for the specified table(s),\n"
1370 " including child and partition tables\n"));
1371 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1372 printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1373 " based on expressions in FILENAME\n"));
1374 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1375 printf(_(" --include-foreign-data=PATTERN\n"
1376 " include data of foreign tables on foreign\n"
1377 " servers matching PATTERN\n"));
1378 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1379 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1380 printf(_(" --no-comments do not dump comment commands\n"));
1381 printf(_(" --no-data do not dump data\n"));
1382 printf(_(" --no-policies do not dump row security policies\n"));
1383 printf(_(" --no-publications do not dump publications\n"));
1384 printf(_(" --no-schema do not dump schema\n"));
1385 printf(_(" --no-security-labels do not dump security label assignments\n"));
1386 printf(_(" --no-statistics do not dump statistics\n"));
1387 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1388 printf(_(" --no-table-access-method do not dump table access methods\n"));
1389 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1390 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1391 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1392 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1393 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1394 printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1395 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1396 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1397 printf(_(" --sequence-data include sequence data in dump\n"));
1398 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1399 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1400 printf(_(" --statistics dump the statistics\n"));
1401 printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1402 printf(_(" --strict-names require table and/or schema include patterns to\n"
1403 " match at least one entity each\n"));
1404 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1405 " child and partition tables\n"));
1406 printf(_(" --use-set-session-authorization\n"
1407 " use SET SESSION AUTHORIZATION commands instead of\n"
1408 " ALTER OWNER commands to set ownership\n"));
1409
1410 printf(_("\nConnection options:\n"));
1411 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1412 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1413 printf(_(" -p, --port=PORT database server port number\n"));
1414 printf(_(" -U, --username=NAME connect as specified database user\n"));
1415 printf(_(" -w, --no-password never prompt for password\n"));
1416 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1417 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1418
1419 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1420 "variable value is used.\n\n"));
1421 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1422 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1423}
1424
1425static void
1426setup_connection(Archive *AH, const char *dumpencoding,
1427 const char *dumpsnapshot, char *use_role)
1428{
1429 DumpOptions *dopt = AH->dopt;
1430 PGconn *conn = GetConnection(AH);
1431 const char *std_strings;
1432
1434
1435 /*
1436 * Set the client encoding if requested.
1437 */
1438 if (dumpencoding)
1439 {
1440 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1441 pg_fatal("invalid client encoding \"%s\" specified",
1442 dumpencoding);
1443 }
1444
1445 /*
1446 * Get the active encoding and the standard_conforming_strings setting, so
1447 * we know how to escape strings.
1448 */
1451
1452 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1453 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1454
1455 /*
1456 * Set the role if requested. In a parallel dump worker, we'll be passed
1457 * use_role == NULL, but AH->use_role is already set (if user specified it
1458 * originally) and we should use that.
1459 */
1460 if (!use_role && AH->use_role)
1461 use_role = AH->use_role;
1462
1463 /* Set the role if requested */
1464 if (use_role)
1465 {
1467
1468 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1469 ExecuteSqlStatement(AH, query->data);
1470 destroyPQExpBuffer(query);
1471
1472 /* save it for possible later use by parallel workers */
1473 if (!AH->use_role)
1474 AH->use_role = pg_strdup(use_role);
1475 }
1476
1477 /* Set the datestyle to ISO to ensure the dump's portability */
1478 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1479
1480 /* Likewise, avoid using sql_standard intervalstyle */
1481 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1482
1483 /*
1484 * Use an explicitly specified extra_float_digits if it has been provided.
1485 * Otherwise, set extra_float_digits so that we can dump float data
1486 * exactly (given correctly implemented float I/O code, anyway).
1487 */
1489 {
1491
1492 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1494 ExecuteSqlStatement(AH, q->data);
1496 }
1497 else
1498 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1499
1500 /*
1501 * Disable synchronized scanning, to prevent unpredictable changes in row
1502 * ordering across a dump and reload.
1503 */
1504 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1505
1506 /*
1507 * Disable timeouts if supported.
1508 */
1509 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1510 if (AH->remoteVersion >= 90300)
1511 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1512 if (AH->remoteVersion >= 90600)
1513 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1514 if (AH->remoteVersion >= 170000)
1515 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1516
1517 /*
1518 * Quote all identifiers, if requested.
1519 */
1521 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1522
1523 /*
1524 * Adjust row-security mode, if supported.
1525 */
1526 if (AH->remoteVersion >= 90500)
1527 {
1528 if (dopt->enable_row_security)
1529 ExecuteSqlStatement(AH, "SET row_security = on");
1530 else
1531 ExecuteSqlStatement(AH, "SET row_security = off");
1532 }
1533
1534 /*
1535 * For security reasons, we restrict the expansion of non-system views and
1536 * access to foreign tables during the pg_dump process. This restriction
1537 * is adjusted when dumping foreign table data.
1538 */
1539 set_restrict_relation_kind(AH, "view, foreign-table");
1540
1541 /*
1542 * Initialize prepared-query state to "nothing prepared". We do this here
1543 * so that a parallel dump worker will have its own state.
1544 */
1545 AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1546
1547 /*
1548 * Start transaction-snapshot mode transaction to dump consistent data.
1549 */
1550 ExecuteSqlStatement(AH, "BEGIN");
1551
1552 /*
1553 * To support the combination of serializable_deferrable with the jobs
1554 * option we use REPEATABLE READ for the worker connections that are
1555 * passed a snapshot. As long as the snapshot is acquired in a
1556 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1557 * REPEATABLE READ transaction provides the appropriate integrity
1558 * guarantees. This is a kluge, but safe for back-patching.
1559 */
1560 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1562 "SET TRANSACTION ISOLATION LEVEL "
1563 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1564 else
1566 "SET TRANSACTION ISOLATION LEVEL "
1567 "REPEATABLE READ, READ ONLY");
1568
1569 /*
1570 * If user specified a snapshot to use, select that. In a parallel dump
1571 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1572 * is already set (if the server can handle it) and we should use that.
1573 */
1574 if (dumpsnapshot)
1575 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1576
1577 if (AH->sync_snapshot_id)
1578 {
1580
1581 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1583 ExecuteSqlStatement(AH, query->data);
1584 destroyPQExpBuffer(query);
1585 }
1586 else if (AH->numWorkers > 1)
1587 {
1588 if (AH->isStandby && AH->remoteVersion < 100000)
1589 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1591 }
1592}
1593
1594/* Set up connection for a parallel worker process */
1595static void
1597{
1598 /*
1599 * We want to re-select all the same values the leader connection is
1600 * using. We'll have inherited directly-usable values in
1601 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1602 * inherited encoding value back to a string to pass to setup_connection.
1603 */
1606 NULL,
1607 NULL);
1608}
1609
1610static char *
1612{
1613 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1614 char *result;
1615 PGresult *res;
1616
1617 res = ExecuteSqlQueryForSingleRow(fout, query);
1618 result = pg_strdup(PQgetvalue(res, 0, 0));
1619 PQclear(res);
1620
1621 return result;
1622}
1623
1624static ArchiveFormat
1626{
1627 ArchiveFormat archiveFormat;
1628
1630
1631 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1632 {
1633 /* This is used by pg_dumpall, and is not documented */
1634 archiveFormat = archNull;
1636 }
1637 else if (pg_strcasecmp(format, "c") == 0)
1638 archiveFormat = archCustom;
1639 else if (pg_strcasecmp(format, "custom") == 0)
1640 archiveFormat = archCustom;
1641 else if (pg_strcasecmp(format, "d") == 0)
1642 archiveFormat = archDirectory;
1643 else if (pg_strcasecmp(format, "directory") == 0)
1644 archiveFormat = archDirectory;
1645 else if (pg_strcasecmp(format, "p") == 0)
1646 archiveFormat = archNull;
1647 else if (pg_strcasecmp(format, "plain") == 0)
1648 archiveFormat = archNull;
1649 else if (pg_strcasecmp(format, "t") == 0)
1650 archiveFormat = archTar;
1651 else if (pg_strcasecmp(format, "tar") == 0)
1652 archiveFormat = archTar;
1653 else
1654 pg_fatal("invalid output format \"%s\" specified", format);
1655 return archiveFormat;
1656}
1657
1658/*
1659 * Find the OIDs of all schemas matching the given list of patterns,
1660 * and append them to the given OID list.
1661 */
1662static void
1664 SimpleStringList *patterns,
1665 SimpleOidList *oids,
1666 bool strict_names)
1667{
1668 PQExpBuffer query;
1669 PGresult *res;
1671 int i;
1672
1673 if (patterns->head == NULL)
1674 return; /* nothing to do */
1675
1676 query = createPQExpBuffer();
1677
1678 /*
1679 * The loop below runs multiple SELECTs might sometimes result in
1680 * duplicate entries in the OID list, but we don't care.
1681 */
1682
1683 for (cell = patterns->head; cell; cell = cell->next)
1684 {
1685 PQExpBufferData dbbuf;
1686 int dotcnt;
1687
1689 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1690 initPQExpBuffer(&dbbuf);
1691 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1692 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1693 &dotcnt);
1694 if (dotcnt > 1)
1695 pg_fatal("improper qualified name (too many dotted names): %s",
1696 cell->val);
1697 else if (dotcnt == 1)
1698 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1699 termPQExpBuffer(&dbbuf);
1700
1701 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1702 if (strict_names && PQntuples(res) == 0)
1703 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1704
1705 for (i = 0; i < PQntuples(res); i++)
1706 {
1707 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1708 }
1709
1710 PQclear(res);
1711 resetPQExpBuffer(query);
1712 }
1713
1714 destroyPQExpBuffer(query);
1715}
1716
1717/*
1718 * Find the OIDs of all extensions matching the given list of patterns,
1719 * and append them to the given OID list.
1720 */
1721static void
1723 SimpleStringList *patterns,
1724 SimpleOidList *oids,
1725 bool strict_names)
1726{
1727 PQExpBuffer query;
1728 PGresult *res;
1730 int i;
1731
1732 if (patterns->head == NULL)
1733 return; /* nothing to do */
1734
1735 query = createPQExpBuffer();
1736
1737 /*
1738 * The loop below runs multiple SELECTs might sometimes result in
1739 * duplicate entries in the OID list, but we don't care.
1740 */
1741 for (cell = patterns->head; cell; cell = cell->next)
1742 {
1743 int dotcnt;
1744
1746 "SELECT oid FROM pg_catalog.pg_extension e\n");
1747 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1748 false, NULL, "e.extname", NULL, NULL, NULL,
1749 &dotcnt);
1750 if (dotcnt > 0)
1751 pg_fatal("improper qualified name (too many dotted names): %s",
1752 cell->val);
1753
1754 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1755 if (strict_names && PQntuples(res) == 0)
1756 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1757
1758 for (i = 0; i < PQntuples(res); i++)
1759 {
1760 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1761 }
1762
1763 PQclear(res);
1764 resetPQExpBuffer(query);
1765 }
1766
1767 destroyPQExpBuffer(query);
1768}
1769
1770/*
1771 * Find the OIDs of all foreign servers matching the given list of patterns,
1772 * and append them to the given OID list.
1773 */
1774static void
1776 SimpleStringList *patterns,
1777 SimpleOidList *oids)
1778{
1779 PQExpBuffer query;
1780 PGresult *res;
1782 int i;
1783
1784 if (patterns->head == NULL)
1785 return; /* nothing to do */
1786
1787 query = createPQExpBuffer();
1788
1789 /*
1790 * The loop below runs multiple SELECTs might sometimes result in
1791 * duplicate entries in the OID list, but we don't care.
1792 */
1793
1794 for (cell = patterns->head; cell; cell = cell->next)
1795 {
1796 int dotcnt;
1797
1799 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1800 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1801 false, NULL, "s.srvname", NULL, NULL, NULL,
1802 &dotcnt);
1803 if (dotcnt > 0)
1804 pg_fatal("improper qualified name (too many dotted names): %s",
1805 cell->val);
1806
1807 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1808 if (PQntuples(res) == 0)
1809 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1810
1811 for (i = 0; i < PQntuples(res); i++)
1812 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1813
1814 PQclear(res);
1815 resetPQExpBuffer(query);
1816 }
1817
1818 destroyPQExpBuffer(query);
1819}
1820
1821/*
1822 * Find the OIDs of all tables matching the given list of patterns,
1823 * and append them to the given OID list. See also expand_dbname_patterns()
1824 * in pg_dumpall.c
1825 */
1826static void
1828 SimpleStringList *patterns, SimpleOidList *oids,
1829 bool strict_names, bool with_child_tables)
1830{
1831 PQExpBuffer query;
1832 PGresult *res;
1834 int i;
1835
1836 if (patterns->head == NULL)
1837 return; /* nothing to do */
1838
1839 query = createPQExpBuffer();
1840
1841 /*
1842 * this might sometimes result in duplicate entries in the OID list, but
1843 * we don't care.
1844 */
1845
1846 for (cell = patterns->head; cell; cell = cell->next)
1847 {
1848 PQExpBufferData dbbuf;
1849 int dotcnt;
1850
1851 /*
1852 * Query must remain ABSOLUTELY devoid of unqualified names. This
1853 * would be unnecessary given a pg_table_is_visible() variant taking a
1854 * search_path argument.
1855 *
1856 * For with_child_tables, we start with the basic query's results and
1857 * recursively search the inheritance tree to add child tables.
1858 */
1859 if (with_child_tables)
1860 {
1861 appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1862 }
1863
1864 appendPQExpBuffer(query,
1865 "SELECT c.oid"
1866 "\nFROM pg_catalog.pg_class c"
1867 "\n LEFT JOIN pg_catalog.pg_namespace n"
1868 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1869 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1870 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1871 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1872 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1873 RELKIND_PARTITIONED_TABLE);
1874 initPQExpBuffer(&dbbuf);
1875 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1876 false, "n.nspname", "c.relname", NULL,
1877 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1878 &dotcnt);
1879 if (dotcnt > 2)
1880 pg_fatal("improper relation name (too many dotted names): %s",
1881 cell->val);
1882 else if (dotcnt == 2)
1883 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1884 termPQExpBuffer(&dbbuf);
1885
1886 if (with_child_tables)
1887 {
1888 appendPQExpBufferStr(query, "UNION"
1889 "\nSELECT i.inhrelid"
1890 "\nFROM partition_tree p"
1891 "\n JOIN pg_catalog.pg_inherits i"
1892 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1893 "\n)"
1894 "\nSELECT relid FROM partition_tree");
1895 }
1896
1897 ExecuteSqlStatement(fout, "RESET search_path");
1898 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1901 if (strict_names && PQntuples(res) == 0)
1902 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1903
1904 for (i = 0; i < PQntuples(res); i++)
1905 {
1906 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1907 }
1908
1909 PQclear(res);
1910 resetPQExpBuffer(query);
1911 }
1912
1913 destroyPQExpBuffer(query);
1914}
1915
1916/*
1917 * Verifies that the connected database name matches the given database name,
1918 * and if not, dies with an error about the given pattern.
1919 *
1920 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1921 */
1922static void
1923prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1924{
1925 const char *db;
1926
1927 db = PQdb(conn);
1928 if (db == NULL)
1929 pg_fatal("You are currently not connected to a database.");
1930
1931 if (strcmp(db, dbname) != 0)
1932 pg_fatal("cross-database references are not implemented: %s",
1933 pattern);
1934}
1935
1936/*
1937 * checkExtensionMembership
1938 * Determine whether object is an extension member, and if so,
1939 * record an appropriate dependency and set the object's dump flag.
1940 *
1941 * It's important to call this for each object that could be an extension
1942 * member. Generally, we integrate this with determining the object's
1943 * to-be-dumped-ness, since extension membership overrides other rules for that.
1944 *
1945 * Returns true if object is an extension member, else false.
1946 */
1947static bool
1949{
1951
1952 if (ext == NULL)
1953 return false;
1954
1955 dobj->ext_member = true;
1956
1957 /* Record dependency so that getDependencies needn't deal with that */
1958 addObjectDependency(dobj, ext->dobj.dumpId);
1959
1960 /*
1961 * In 9.6 and above, mark the member object to have any non-initial ACLs
1962 * dumped. (Any initial ACLs will be removed later, using data from
1963 * pg_init_privs, so that we'll dump only the delta from the extension's
1964 * initial setup.)
1965 *
1966 * Prior to 9.6, we do not include any extension member components.
1967 *
1968 * In binary upgrades, we still dump all components of the members
1969 * individually, since the idea is to exactly reproduce the database
1970 * contents rather than replace the extension contents with something
1971 * different.
1972 *
1973 * Note: it might be interesting someday to implement storage and delta
1974 * dumping of extension members' RLS policies and/or security labels.
1975 * However there is a pitfall for RLS policies: trying to dump them
1976 * requires getting a lock on their tables, and the calling user might not
1977 * have privileges for that. We need no lock to examine a table's ACLs,
1978 * so the current feature doesn't have a problem of that sort.
1979 */
1980 if (fout->dopt->binary_upgrade)
1981 dobj->dump = ext->dobj.dump;
1982 else
1983 {
1984 if (fout->remoteVersion < 90600)
1985 dobj->dump = DUMP_COMPONENT_NONE;
1986 else
1987 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1988 }
1989
1990 return true;
1991}
1992
1993/*
1994 * selectDumpableNamespace: policy-setting subroutine
1995 * Mark a namespace as to be dumped or not
1996 */
1997static void
1999{
2000 /*
2001 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
2002 * and (for --clean) a DROP SCHEMA statement. (In the absence of
2003 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
2004 */
2005 nsinfo->create = true;
2006
2007 /*
2008 * If specific tables are being dumped, do not dump any complete
2009 * namespaces. If specific namespaces are being dumped, dump just those
2010 * namespaces. Otherwise, dump all non-system namespaces.
2011 */
2012 if (table_include_oids.head != NULL)
2013 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2014 else if (schema_include_oids.head != NULL)
2015 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2017 nsinfo->dobj.catId.oid) ?
2019 else if (fout->remoteVersion >= 90600 &&
2020 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2021 {
2022 /*
2023 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2024 * they are interesting (and not the original ACLs which were set at
2025 * initdb time, see pg_init_privs).
2026 */
2027 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2028 }
2029 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2030 strcmp(nsinfo->dobj.name, "information_schema") == 0)
2031 {
2032 /* Other system schemas don't get dumped */
2033 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2034 }
2035 else if (strcmp(nsinfo->dobj.name, "public") == 0)
2036 {
2037 /*
2038 * The public schema is a strange beast that sits in a sort of
2039 * no-mans-land between being a system object and a user object.
2040 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2041 * a comment and an indication of ownership. If the owner is the
2042 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2043 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2044 */
2045 nsinfo->create = false;
2046 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2047 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2048 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2050
2051 /*
2052 * Also, make like it has a comment even if it doesn't; this is so
2053 * that we'll emit a command to drop the comment, if appropriate.
2054 * (Without this, we'd not call dumpCommentExtended for it.)
2055 */
2057 }
2058 else
2059 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2060
2061 /*
2062 * In any case, a namespace can be excluded by an exclusion switch
2063 */
2064 if (nsinfo->dobj.dump_contains &&
2066 nsinfo->dobj.catId.oid))
2067 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2068
2069 /*
2070 * If the schema belongs to an extension, allow extension membership to
2071 * override the dump decision for the schema itself. However, this does
2072 * not change dump_contains, so this won't change what we do with objects
2073 * within the schema. (If they belong to the extension, they'll get
2074 * suppressed by it, otherwise not.)
2075 */
2076 (void) checkExtensionMembership(&nsinfo->dobj, fout);
2077}
2078
2079/*
2080 * selectDumpableTable: policy-setting subroutine
2081 * Mark a table as to be dumped or not
2082 */
2083static void
2085{
2086 if (checkExtensionMembership(&tbinfo->dobj, fout))
2087 return; /* extension membership overrides all else */
2088
2089 /*
2090 * If specific tables are being dumped, dump just those tables; else, dump
2091 * according to the parent namespace's dump flag.
2092 */
2093 if (table_include_oids.head != NULL)
2095 tbinfo->dobj.catId.oid) ?
2097 else
2098 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2099
2100 /*
2101 * In any case, a table can be excluded by an exclusion switch
2102 */
2103 if (tbinfo->dobj.dump &&
2105 tbinfo->dobj.catId.oid))
2106 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2107}
2108
2109/*
2110 * selectDumpableType: policy-setting subroutine
2111 * Mark a type as to be dumped or not
2112 *
2113 * If it's a table's rowtype or an autogenerated array type, we also apply a
2114 * special type code to facilitate sorting into the desired order. (We don't
2115 * want to consider those to be ordinary types because that would bring tables
2116 * up into the datatype part of the dump order.) We still set the object's
2117 * dump flag; that's not going to cause the dummy type to be dumped, but we
2118 * need it so that casts involving such types will be dumped correctly -- see
2119 * dumpCast. This means the flag should be set the same as for the underlying
2120 * object (the table or base type).
2121 */
2122static void
2124{
2125 /* skip complex types, except for standalone composite types */
2126 if (OidIsValid(tyinfo->typrelid) &&
2127 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2128 {
2129 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2130
2131 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2132 if (tytable != NULL)
2133 tyinfo->dobj.dump = tytable->dobj.dump;
2134 else
2135 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2136 return;
2137 }
2138
2139 /* skip auto-generated array and multirange types */
2140 if (tyinfo->isArray || tyinfo->isMultirange)
2141 {
2142 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2143
2144 /*
2145 * Fall through to set the dump flag; we assume that the subsequent
2146 * rules will do the same thing as they would for the array's base
2147 * type or multirange's range type. (We cannot reliably look up the
2148 * base type here, since getTypes may not have processed it yet.)
2149 */
2150 }
2151
2152 if (checkExtensionMembership(&tyinfo->dobj, fout))
2153 return; /* extension membership overrides all else */
2154
2155 /* Dump based on if the contents of the namespace are being dumped */
2156 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2157}
2158
2159/*
2160 * selectDumpableDefaultACL: policy-setting subroutine
2161 * Mark a default ACL as to be dumped or not
2162 *
2163 * For per-schema default ACLs, dump if the schema is to be dumped.
2164 * Otherwise dump if we are dumping "everything". Note that dumpSchema
2165 * and aclsSkip are checked separately.
2166 */
2167static void
2169{
2170 /* Default ACLs can't be extension members */
2171
2172 if (dinfo->dobj.namespace)
2173 /* default ACLs are considered part of the namespace */
2174 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2175 else
2176 dinfo->dobj.dump = dopt->include_everything ?
2178}
2179
2180/*
2181 * selectDumpableCast: policy-setting subroutine
2182 * Mark a cast as to be dumped or not
2183 *
2184 * Casts do not belong to any particular namespace (since they haven't got
2185 * names), nor do they have identifiable owners. To distinguish user-defined
2186 * casts from built-in ones, we must resort to checking whether the cast's
2187 * OID is in the range reserved for initdb.
2188 */
2189static void
2191{
2192 if (checkExtensionMembership(&cast->dobj, fout))
2193 return; /* extension membership overrides all else */
2194
2195 /*
2196 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2197 * support ACLs currently.
2198 */
2199 if (cast->dobj.catId.oid <= g_last_builtin_oid)
2201 else
2202 cast->dobj.dump = fout->dopt->include_everything ?
2204}
2205
2206/*
2207 * selectDumpableProcLang: policy-setting subroutine
2208 * Mark a procedural language as to be dumped or not
2209 *
2210 * Procedural languages do not belong to any particular namespace. To
2211 * identify built-in languages, we must resort to checking whether the
2212 * language's OID is in the range reserved for initdb.
2213 */
2214static void
2216{
2217 if (checkExtensionMembership(&plang->dobj, fout))
2218 return; /* extension membership overrides all else */
2219
2220 /*
2221 * Only include procedural languages when we are dumping everything.
2222 *
2223 * For from-initdb procedural languages, only include ACLs, as we do for
2224 * the pg_catalog namespace. We need this because procedural languages do
2225 * not live in any namespace.
2226 */
2227 if (!fout->dopt->include_everything)
2228 plang->dobj.dump = DUMP_COMPONENT_NONE;
2229 else
2230 {
2231 if (plang->dobj.catId.oid <= g_last_builtin_oid)
2232 plang->dobj.dump = fout->remoteVersion < 90600 ?
2234 else
2235 plang->dobj.dump = DUMP_COMPONENT_ALL;
2236 }
2237}
2238
2239/*
2240 * selectDumpableAccessMethod: policy-setting subroutine
2241 * Mark an access method as to be dumped or not
2242 *
2243 * Access methods do not belong to any particular namespace. To identify
2244 * built-in access methods, we must resort to checking whether the
2245 * method's OID is in the range reserved for initdb.
2246 */
2247static void
2249{
2250 /* see getAccessMethods() comment about v9.6. */
2251 if (fout->remoteVersion < 90600)
2252 {
2253 method->dobj.dump = DUMP_COMPONENT_NONE;
2254 return;
2255 }
2256
2257 if (checkExtensionMembership(&method->dobj, fout))
2258 return; /* extension membership overrides all else */
2259
2260 /*
2261 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2262 * they do not support ACLs currently.
2263 */
2264 if (method->dobj.catId.oid <= g_last_builtin_oid)
2265 method->dobj.dump = DUMP_COMPONENT_NONE;
2266 else
2267 method->dobj.dump = fout->dopt->include_everything ?
2269}
2270
2271/*
2272 * selectDumpableExtension: policy-setting subroutine
2273 * Mark an extension as to be dumped or not
2274 *
2275 * Built-in extensions should be skipped except for checking ACLs, since we
2276 * assume those will already be installed in the target database. We identify
2277 * such extensions by their having OIDs in the range reserved for initdb.
2278 * We dump all user-added extensions by default. No extensions are dumped
2279 * if include_everything is false (i.e., a --schema or --table switch was
2280 * given), except if --extension specifies a list of extensions to dump.
2281 */
2282static void
2284{
2285 /*
2286 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2287 * change permissions on their member objects, if they wish to, and have
2288 * those changes preserved.
2289 */
2290 if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2291 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2292 else
2293 {
2294 /* check if there is a list of extensions to dump */
2295 if (extension_include_oids.head != NULL)
2296 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2298 extinfo->dobj.catId.oid) ?
2300 else
2301 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2302 dopt->include_everything ?
2304
2305 /* check that the extension is not explicitly excluded */
2306 if (extinfo->dobj.dump &&
2308 extinfo->dobj.catId.oid))
2309 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2310 }
2311}
2312
2313/*
2314 * selectDumpablePublicationObject: policy-setting subroutine
2315 * Mark a publication object as to be dumped or not
2316 *
2317 * A publication can have schemas and tables which have schemas, but those are
2318 * ignored in decision making, because publications are only dumped when we are
2319 * dumping everything.
2320 */
2321static void
2323{
2324 if (checkExtensionMembership(dobj, fout))
2325 return; /* extension membership overrides all else */
2326
2327 dobj->dump = fout->dopt->include_everything ?
2329}
2330
2331/*
2332 * selectDumpableStatisticsObject: policy-setting subroutine
2333 * Mark an extended statistics object as to be dumped or not
2334 *
2335 * We dump an extended statistics object if the schema it's in and the table
2336 * it's for are being dumped. (This'll need more thought if statistics
2337 * objects ever support cross-table stats.)
2338 */
2339static void
2341{
2342 if (checkExtensionMembership(&sobj->dobj, fout))
2343 return; /* extension membership overrides all else */
2344
2345 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2346 if (sobj->stattable == NULL ||
2349}
2350
2351/*
2352 * selectDumpableObject: policy-setting subroutine
2353 * Mark a generic dumpable object as to be dumped or not
2354 *
2355 * Use this only for object types without a special-case routine above.
2356 */
2357static void
2359{
2360 if (checkExtensionMembership(dobj, fout))
2361 return; /* extension membership overrides all else */
2362
2363 /*
2364 * Default policy is to dump if parent namespace is dumpable, or for
2365 * non-namespace-associated items, dump if we're dumping "everything".
2366 */
2367 if (dobj->namespace)
2368 dobj->dump = dobj->namespace->dobj.dump_contains;
2369 else
2370 dobj->dump = fout->dopt->include_everything ?
2372}
2373
2374/*
2375 * Dump a table's contents for loading using the COPY command
2376 * - this routine is called by the Archiver when it wants the table
2377 * to be dumped.
2378 */
2379static int
2380dumpTableData_copy(Archive *fout, const void *dcontext)
2381{
2382 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2383 TableInfo *tbinfo = tdinfo->tdtable;
2384 const char *classname = tbinfo->dobj.name;
2386
2387 /*
2388 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2389 * which uses it already.
2390 */
2391 PQExpBuffer clistBuf = createPQExpBuffer();
2392 PGconn *conn = GetConnection(fout);
2393 PGresult *res;
2394 int ret;
2395 char *copybuf;
2396 const char *column_list;
2397
2398 pg_log_info("dumping contents of table \"%s.%s\"",
2399 tbinfo->dobj.namespace->dobj.name, classname);
2400
2401 /*
2402 * Specify the column list explicitly so that we have no possibility of
2403 * retrieving data in the wrong column order. (The default column
2404 * ordering of COPY will not be what we want in certain corner cases
2405 * involving ADD COLUMN and inheritance.)
2406 */
2407 column_list = fmtCopyColumnList(tbinfo, clistBuf);
2408
2409 /*
2410 * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2411 * a filter condition was specified. For other cases a simple COPY
2412 * suffices.
2413 */
2414 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2415 {
2416 /* Temporary allows to access to foreign tables to dump data */
2417 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2418 set_restrict_relation_kind(fout, "view");
2419
2420 appendPQExpBufferStr(q, "COPY (SELECT ");
2421 /* klugery to get rid of parens in column list */
2422 if (strlen(column_list) > 2)
2423 {
2424 appendPQExpBufferStr(q, column_list + 1);
2425 q->data[q->len - 1] = ' ';
2426 }
2427 else
2428 appendPQExpBufferStr(q, "* ");
2429
2430 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2431 fmtQualifiedDumpable(tbinfo),
2432 tdinfo->filtercond ? tdinfo->filtercond : "");
2433 }
2434 else
2435 {
2436 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2437 fmtQualifiedDumpable(tbinfo),
2438 column_list);
2439 }
2440 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2441 PQclear(res);
2442 destroyPQExpBuffer(clistBuf);
2443
2444 for (;;)
2445 {
2446 ret = PQgetCopyData(conn, &copybuf, 0);
2447
2448 if (ret < 0)
2449 break; /* done or error */
2450
2451 if (copybuf)
2452 {
2453 WriteData(fout, copybuf, ret);
2455 }
2456
2457 /* ----------
2458 * THROTTLE:
2459 *
2460 * There was considerable discussion in late July, 2000 regarding
2461 * slowing down pg_dump when backing up large tables. Users with both
2462 * slow & fast (multi-processor) machines experienced performance
2463 * degradation when doing a backup.
2464 *
2465 * Initial attempts based on sleeping for a number of ms for each ms
2466 * of work were deemed too complex, then a simple 'sleep in each loop'
2467 * implementation was suggested. The latter failed because the loop
2468 * was too tight. Finally, the following was implemented:
2469 *
2470 * If throttle is non-zero, then
2471 * See how long since the last sleep.
2472 * Work out how long to sleep (based on ratio).
2473 * If sleep is more than 100ms, then
2474 * sleep
2475 * reset timer
2476 * EndIf
2477 * EndIf
2478 *
2479 * where the throttle value was the number of ms to sleep per ms of
2480 * work. The calculation was done in each loop.
2481 *
2482 * Most of the hard work is done in the backend, and this solution
2483 * still did not work particularly well: on slow machines, the ratio
2484 * was 50:1, and on medium paced machines, 1:1, and on fast
2485 * multi-processor machines, it had little or no effect, for reasons
2486 * that were unclear.
2487 *
2488 * Further discussion ensued, and the proposal was dropped.
2489 *
2490 * For those people who want this feature, it can be implemented using
2491 * gettimeofday in each loop, calculating the time since last sleep,
2492 * multiplying that by the sleep ratio, then if the result is more
2493 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2494 * function to sleep for a subsecond period ie.
2495 *
2496 * select(0, NULL, NULL, NULL, &tvi);
2497 *
2498 * This will return after the interval specified in the structure tvi.
2499 * Finally, call gettimeofday again to save the 'last sleep time'.
2500 * ----------
2501 */
2502 }
2503 archprintf(fout, "\\.\n\n\n");
2504
2505 if (ret == -2)
2506 {
2507 /* copy data transfer failed */
2508 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2509 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2510 pg_log_error_detail("Command was: %s", q->data);
2511 exit_nicely(1);
2512 }
2513
2514 /* Check command status and return to normal libpq state */
2515 res = PQgetResult(conn);
2516 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2517 {
2518 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2519 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2520 pg_log_error_detail("Command was: %s", q->data);
2521 exit_nicely(1);
2522 }
2523 PQclear(res);
2524
2525 /* Do this to ensure we've pumped libpq back to idle state */
2526 if (PQgetResult(conn) != NULL)
2527 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2528 classname);
2529
2531
2532 /* Revert back the setting */
2533 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2534 set_restrict_relation_kind(fout, "view, foreign-table");
2535
2536 return 1;
2537}
2538
2539/*
2540 * Dump table data using INSERT commands.
2541 *
2542 * Caution: when we restore from an archive file direct to database, the
2543 * INSERT commands emitted by this function have to be parsed by
2544 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2545 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2546 */
2547static int
2548dumpTableData_insert(Archive *fout, const void *dcontext)
2549{
2550 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2551 TableInfo *tbinfo = tdinfo->tdtable;
2552 DumpOptions *dopt = fout->dopt;
2554 PQExpBuffer insertStmt = NULL;
2555 char *attgenerated;
2556 PGresult *res;
2557 int nfields,
2558 i;
2559 int rows_per_statement = dopt->dump_inserts;
2560 int rows_this_statement = 0;
2561
2562 /* Temporary allows to access to foreign tables to dump data */
2563 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2564 set_restrict_relation_kind(fout, "view");
2565
2566 /*
2567 * If we're going to emit INSERTs with column names, the most efficient
2568 * way to deal with generated columns is to exclude them entirely. For
2569 * INSERTs without column names, we have to emit DEFAULT rather than the
2570 * actual column value --- but we can save a few cycles by fetching nulls
2571 * rather than the uninteresting-to-us value.
2572 */
2573 attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2574 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2575 nfields = 0;
2576 for (i = 0; i < tbinfo->numatts; i++)
2577 {
2578 if (tbinfo->attisdropped[i])
2579 continue;
2580 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2581 continue;
2582 if (nfields > 0)
2583 appendPQExpBufferStr(q, ", ");
2584 if (tbinfo->attgenerated[i])
2585 appendPQExpBufferStr(q, "NULL");
2586 else
2587 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2588 attgenerated[nfields] = tbinfo->attgenerated[i];
2589 nfields++;
2590 }
2591 /* Servers before 9.4 will complain about zero-column SELECT */
2592 if (nfields == 0)
2593 appendPQExpBufferStr(q, "NULL");
2594 appendPQExpBuffer(q, " FROM ONLY %s",
2595 fmtQualifiedDumpable(tbinfo));
2596 if (tdinfo->filtercond)
2597 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2598
2599 ExecuteSqlStatement(fout, q->data);
2600
2601 while (1)
2602 {
2603 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2605
2606 /* cross-check field count, allowing for dummy NULL if any */
2607 if (nfields != PQnfields(res) &&
2608 !(nfields == 0 && PQnfields(res) == 1))
2609 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2610 tbinfo->dobj.name);
2611
2612 /*
2613 * First time through, we build as much of the INSERT statement as
2614 * possible in "insertStmt", which we can then just print for each
2615 * statement. If the table happens to have zero dumpable columns then
2616 * this will be a complete statement, otherwise it will end in
2617 * "VALUES" and be ready to have the row's column values printed.
2618 */
2619 if (insertStmt == NULL)
2620 {
2621 TableInfo *targettab;
2622
2623 insertStmt = createPQExpBuffer();
2624
2625 /*
2626 * When load-via-partition-root is set or forced, get the root
2627 * table name for the partition table, so that we can reload data
2628 * through the root table.
2629 */
2630 if (tbinfo->ispartition &&
2631 (dopt->load_via_partition_root ||
2632 forcePartitionRootLoad(tbinfo)))
2633 targettab = getRootTableInfo(tbinfo);
2634 else
2635 targettab = tbinfo;
2636
2637 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2638 fmtQualifiedDumpable(targettab));
2639
2640 /* corner case for zero-column table */
2641 if (nfields == 0)
2642 {
2643 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2644 }
2645 else
2646 {
2647 /* append the list of column names if required */
2648 if (dopt->column_inserts)
2649 {
2650 appendPQExpBufferChar(insertStmt, '(');
2651 for (int field = 0; field < nfields; field++)
2652 {
2653 if (field > 0)
2654 appendPQExpBufferStr(insertStmt, ", ");
2655 appendPQExpBufferStr(insertStmt,
2656 fmtId(PQfname(res, field)));
2657 }
2658 appendPQExpBufferStr(insertStmt, ") ");
2659 }
2660
2661 if (tbinfo->needs_override)
2662 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2663
2664 appendPQExpBufferStr(insertStmt, "VALUES");
2665 }
2666 }
2667
2668 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2669 {
2670 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2671 if (rows_this_statement == 0)
2672 archputs(insertStmt->data, fout);
2673
2674 /*
2675 * If it is zero-column table then we've already written the
2676 * complete statement, which will mean we've disobeyed
2677 * --rows-per-insert when it's set greater than 1. We do support
2678 * a way to make this multi-row with: SELECT UNION ALL SELECT
2679 * UNION ALL ... but that's non-standard so we should avoid it
2680 * given that using INSERTs is mostly only ever needed for
2681 * cross-database exports.
2682 */
2683 if (nfields == 0)
2684 continue;
2685
2686 /* Emit a row heading */
2687 if (rows_per_statement == 1)
2688 archputs(" (", fout);
2689 else if (rows_this_statement > 0)
2690 archputs(",\n\t(", fout);
2691 else
2692 archputs("\n\t(", fout);
2693
2694 for (int field = 0; field < nfields; field++)
2695 {
2696 if (field > 0)
2697 archputs(", ", fout);
2698 if (attgenerated[field])
2699 {
2700 archputs("DEFAULT", fout);
2701 continue;
2702 }
2703 if (PQgetisnull(res, tuple, field))
2704 {
2705 archputs("NULL", fout);
2706 continue;
2707 }
2708
2709 /* XXX This code is partially duplicated in ruleutils.c */
2710 switch (PQftype(res, field))
2711 {
2712 case INT2OID:
2713 case INT4OID:
2714 case INT8OID:
2715 case OIDOID:
2716 case FLOAT4OID:
2717 case FLOAT8OID:
2718 case NUMERICOID:
2719 {
2720 /*
2721 * These types are printed without quotes unless
2722 * they contain values that aren't accepted by the
2723 * scanner unquoted (e.g., 'NaN'). Note that
2724 * strtod() and friends might accept NaN, so we
2725 * can't use that to test.
2726 *
2727 * In reality we only need to defend against
2728 * infinity and NaN, so we need not get too crazy
2729 * about pattern matching here.
2730 */
2731 const char *s = PQgetvalue(res, tuple, field);
2732
2733 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2734 archputs(s, fout);
2735 else
2736 archprintf(fout, "'%s'", s);
2737 }
2738 break;
2739
2740 case BITOID:
2741 case VARBITOID:
2742 archprintf(fout, "B'%s'",
2743 PQgetvalue(res, tuple, field));
2744 break;
2745
2746 case BOOLOID:
2747 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2748 archputs("true", fout);
2749 else
2750 archputs("false", fout);
2751 break;
2752
2753 default:
2754 /* All other types are printed as string literals. */
2757 PQgetvalue(res, tuple, field),
2758 fout);
2759 archputs(q->data, fout);
2760 break;
2761 }
2762 }
2763
2764 /* Terminate the row ... */
2765 archputs(")", fout);
2766
2767 /* ... and the statement, if the target no. of rows is reached */
2768 if (++rows_this_statement >= rows_per_statement)
2769 {
2770 if (dopt->do_nothing)
2771 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2772 else
2773 archputs(";\n", fout);
2774 /* Reset the row counter */
2775 rows_this_statement = 0;
2776 }
2777 }
2778
2779 if (PQntuples(res) <= 0)
2780 {
2781 PQclear(res);
2782 break;
2783 }
2784 PQclear(res);
2785 }
2786
2787 /* Terminate any statements that didn't make the row count. */
2788 if (rows_this_statement > 0)
2789 {
2790 if (dopt->do_nothing)
2791 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2792 else
2793 archputs(";\n", fout);
2794 }
2795
2796 archputs("\n\n", fout);
2797
2798 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2799
2801 if (insertStmt != NULL)
2802 destroyPQExpBuffer(insertStmt);
2803 free(attgenerated);
2804
2805 /* Revert back the setting */
2806 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2807 set_restrict_relation_kind(fout, "view, foreign-table");
2808
2809 return 1;
2810}
2811
2812/*
2813 * getRootTableInfo:
2814 * get the root TableInfo for the given partition table.
2815 */
2816static TableInfo *
2818{
2819 TableInfo *parentTbinfo;
2820
2821 Assert(tbinfo->ispartition);
2822 Assert(tbinfo->numParents == 1);
2823
2824 parentTbinfo = tbinfo->parents[0];
2825 while (parentTbinfo->ispartition)
2826 {
2827 Assert(parentTbinfo->numParents == 1);
2828 parentTbinfo = parentTbinfo->parents[0];
2829 }
2830
2831 return parentTbinfo;
2832}
2833
2834/*
2835 * forcePartitionRootLoad
2836 * Check if we must force load_via_partition_root for this partition.
2837 *
2838 * This is required if any level of ancestral partitioned table has an
2839 * unsafe partitioning scheme.
2840 */
2841static bool
2843{
2844 TableInfo *parentTbinfo;
2845
2846 Assert(tbinfo->ispartition);
2847 Assert(tbinfo->numParents == 1);
2848
2849 parentTbinfo = tbinfo->parents[0];
2850 if (parentTbinfo->unsafe_partitions)
2851 return true;
2852 while (parentTbinfo->ispartition)
2853 {
2854 Assert(parentTbinfo->numParents == 1);
2855 parentTbinfo = parentTbinfo->parents[0];
2856 if (parentTbinfo->unsafe_partitions)
2857 return true;
2858 }
2859
2860 return false;
2861}
2862
2863/*
2864 * dumpTableData -
2865 * dump the contents of a single table
2866 *
2867 * Actually, this just makes an ArchiveEntry for the table contents.
2868 */
2869static void
2871{
2872 DumpOptions *dopt = fout->dopt;
2873 TableInfo *tbinfo = tdinfo->tdtable;
2874 PQExpBuffer copyBuf = createPQExpBuffer();
2875 PQExpBuffer clistBuf = createPQExpBuffer();
2876 DataDumperPtr dumpFn;
2877 char *tdDefn = NULL;
2878 char *copyStmt;
2879 const char *copyFrom;
2880
2881 /* We had better have loaded per-column details about this table */
2882 Assert(tbinfo->interesting);
2883
2884 /*
2885 * When load-via-partition-root is set or forced, get the root table name
2886 * for the partition table, so that we can reload data through the root
2887 * table. Then construct a comment to be inserted into the TOC entry's
2888 * defn field, so that such cases can be identified reliably.
2889 */
2890 if (tbinfo->ispartition &&
2891 (dopt->load_via_partition_root ||
2892 forcePartitionRootLoad(tbinfo)))
2893 {
2894 TableInfo *parentTbinfo;
2895 char *sanitized;
2896
2897 parentTbinfo = getRootTableInfo(tbinfo);
2898 copyFrom = fmtQualifiedDumpable(parentTbinfo);
2899 sanitized = sanitize_line(copyFrom, true);
2900 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2901 sanitized);
2902 free(sanitized);
2903 tdDefn = pg_strdup(copyBuf->data);
2904 }
2905 else
2906 copyFrom = fmtQualifiedDumpable(tbinfo);
2907
2908 if (dopt->dump_inserts == 0)
2909 {
2910 /* Dump/restore using COPY */
2911 dumpFn = dumpTableData_copy;
2912 /* must use 2 steps here 'cause fmtId is nonreentrant */
2913 printfPQExpBuffer(copyBuf, "COPY %s ",
2914 copyFrom);
2915 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2916 fmtCopyColumnList(tbinfo, clistBuf));
2917 copyStmt = copyBuf->data;
2918 }
2919 else
2920 {
2921 /* Restore using INSERT */
2922 dumpFn = dumpTableData_insert;
2923 copyStmt = NULL;
2924 }
2925
2926 /*
2927 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2928 * dependency on its table as "special" and pass it to ArchiveEntry now.
2929 * See comments for BuildArchiveDependencies.
2930 */
2931 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2932 {
2933 TocEntry *te;
2934
2935 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2936 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2937 .namespace = tbinfo->dobj.namespace->dobj.name,
2938 .owner = tbinfo->rolname,
2939 .description = "TABLE DATA",
2940 .section = SECTION_DATA,
2941 .createStmt = tdDefn,
2942 .copyStmt = copyStmt,
2943 .deps = &(tbinfo->dobj.dumpId),
2944 .nDeps = 1,
2945 .dumpFn = dumpFn,
2946 .dumpArg = tdinfo));
2947
2948 /*
2949 * Set the TocEntry's dataLength in case we are doing a parallel dump
2950 * and want to order dump jobs by table size. We choose to measure
2951 * dataLength in table pages (including TOAST pages) during dump, so
2952 * no scaling is needed.
2953 *
2954 * However, relpages is declared as "integer" in pg_class, and hence
2955 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2956 * Cast so that we get the right interpretation of table sizes
2957 * exceeding INT_MAX pages.
2958 */
2959 te->dataLength = (BlockNumber) tbinfo->relpages;
2960 te->dataLength += (BlockNumber) tbinfo->toastpages;
2961
2962 /*
2963 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2964 * and instead we'd better worry about integer overflow. Clamp to
2965 * INT_MAX if the correct result exceeds that.
2966 */
2967 if (sizeof(te->dataLength) == 4 &&
2968 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2969 te->dataLength < 0))
2970 te->dataLength = INT_MAX;
2971 }
2972
2973 destroyPQExpBuffer(copyBuf);
2974 destroyPQExpBuffer(clistBuf);
2975}
2976
2977/*
2978 * refreshMatViewData -
2979 * load or refresh the contents of a single materialized view
2980 *
2981 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2982 * statement.
2983 */
2984static void
2986{
2987 TableInfo *tbinfo = tdinfo->tdtable;
2988 PQExpBuffer q;
2989
2990 /* If the materialized view is not flagged as populated, skip this. */
2991 if (!tbinfo->relispopulated)
2992 return;
2993
2994 q = createPQExpBuffer();
2995
2996 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2997 fmtQualifiedDumpable(tbinfo));
2998
2999 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
3000 ArchiveEntry(fout,
3001 tdinfo->dobj.catId, /* catalog ID */
3002 tdinfo->dobj.dumpId, /* dump ID */
3003 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
3004 .namespace = tbinfo->dobj.namespace->dobj.name,
3005 .owner = tbinfo->rolname,
3006 .description = "MATERIALIZED VIEW DATA",
3007 .section = SECTION_POST_DATA,
3008 .createStmt = q->data,
3009 .deps = tdinfo->dobj.dependencies,
3010 .nDeps = tdinfo->dobj.nDeps));
3011
3013}
3014
3015/*
3016 * getTableData -
3017 * set up dumpable objects representing the contents of tables
3018 */
3019static void
3020getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3021{
3022 int i;
3023
3024 for (i = 0; i < numTables; i++)
3025 {
3026 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3027 (!relkind || tblinfo[i].relkind == relkind))
3028 makeTableDataInfo(dopt, &(tblinfo[i]));
3029 }
3030}
3031
3032/*
3033 * Make a dumpable object for the data of this specific table
3034 *
3035 * Note: we make a TableDataInfo if and only if we are going to dump the
3036 * table data; the "dump" field in such objects isn't very interesting.
3037 */
3038static void
3040{
3041 TableDataInfo *tdinfo;
3042
3043 /*
3044 * Nothing to do if we already decided to dump the table. This will
3045 * happen for "config" tables.
3046 */
3047 if (tbinfo->dataObj != NULL)
3048 return;
3049
3050 /* Skip VIEWs (no data to dump) */
3051 if (tbinfo->relkind == RELKIND_VIEW)
3052 return;
3053 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3054 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3057 tbinfo->foreign_server)))
3058 return;
3059 /* Skip partitioned tables (data in partitions) */
3060 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3061 return;
3062
3063 /* Don't dump data in unlogged tables, if so requested */
3064 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3066 return;
3067
3068 /* Check that the data is not explicitly excluded */
3070 tbinfo->dobj.catId.oid))
3071 return;
3072
3073 /* OK, let's dump it */
3074 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3075
3076 if (tbinfo->relkind == RELKIND_MATVIEW)
3078 else if (tbinfo->relkind == RELKIND_SEQUENCE)
3079 tdinfo->dobj.objType = DO_SEQUENCE_SET;
3080 else
3081 tdinfo->dobj.objType = DO_TABLE_DATA;
3082
3083 /*
3084 * Note: use tableoid 0 so that this object won't be mistaken for
3085 * something that pg_depend entries apply to.
3086 */
3087 tdinfo->dobj.catId.tableoid = 0;
3088 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3089 AssignDumpId(&tdinfo->dobj);
3090 tdinfo->dobj.name = tbinfo->dobj.name;
3091 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3092 tdinfo->tdtable = tbinfo;
3093 tdinfo->filtercond = NULL; /* might get set later */
3094 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3095
3096 /* A TableDataInfo contains data, of course */
3098
3099 tbinfo->dataObj = tdinfo;
3100
3101 /*
3102 * Materialized view statistics must be restored after the data, because
3103 * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3104 *
3105 * The dependency is added here because the statistics objects are created
3106 * first.
3107 */
3108 if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3109 {
3110 tbinfo->stats->section = SECTION_POST_DATA;
3111 addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3112 }
3113
3114 /* Make sure that we'll collect per-column info for this table. */
3115 tbinfo->interesting = true;
3116}
3117
3118/*
3119 * The refresh for a materialized view must be dependent on the refresh for
3120 * any materialized view that this one is dependent on.
3121 *
3122 * This must be called after all the objects are created, but before they are
3123 * sorted.
3124 */
3125static void
3127{
3128 PQExpBuffer query;
3129 PGresult *res;
3130 int ntups,
3131 i;
3132 int i_classid,
3133 i_objid,
3134 i_refobjid;
3135
3136 /* No Mat Views before 9.3. */
3137 if (fout->remoteVersion < 90300)
3138 return;
3139
3140 query = createPQExpBuffer();
3141
3142 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3143 "( "
3144 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3145 "FROM pg_depend d1 "
3146 "JOIN pg_class c1 ON c1.oid = d1.objid "
3147 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3148 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3149 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3150 "AND d2.objid = r1.oid "
3151 "AND d2.refobjid <> d1.objid "
3152 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3153 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3154 CppAsString2(RELKIND_VIEW) ") "
3155 "WHERE d1.classid = 'pg_class'::regclass "
3156 "UNION "
3157 "SELECT w.objid, d3.refobjid, c3.relkind "
3158 "FROM w "
3159 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3160 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3161 "AND d3.objid = r3.oid "
3162 "AND d3.refobjid <> w.refobjid "
3163 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3164 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3165 CppAsString2(RELKIND_VIEW) ") "
3166 ") "
3167 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3168 "FROM w "
3169 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3170
3171 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3172
3173 ntups = PQntuples(res);
3174
3175 i_classid = PQfnumber(res, "classid");
3176 i_objid = PQfnumber(res, "objid");
3177 i_refobjid = PQfnumber(res, "refobjid");
3178
3179 for (i = 0; i < ntups; i++)
3180 {
3181 CatalogId objId;
3182 CatalogId refobjId;
3183 DumpableObject *dobj;
3184 DumpableObject *refdobj;
3185 TableInfo *tbinfo;
3186 TableInfo *reftbinfo;
3187
3188 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3189 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3190 refobjId.tableoid = objId.tableoid;
3191 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3192
3193 dobj = findObjectByCatalogId(objId);
3194 if (dobj == NULL)
3195 continue;
3196
3197 Assert(dobj->objType == DO_TABLE);
3198 tbinfo = (TableInfo *) dobj;
3199 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3200 dobj = (DumpableObject *) tbinfo->dataObj;
3201 if (dobj == NULL)
3202 continue;
3204
3205 refdobj = findObjectByCatalogId(refobjId);
3206 if (refdobj == NULL)
3207 continue;
3208
3209 Assert(refdobj->objType == DO_TABLE);
3210 reftbinfo = (TableInfo *) refdobj;
3211 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3212 refdobj = (DumpableObject *) reftbinfo->dataObj;
3213 if (refdobj == NULL)
3214 continue;
3215 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3216
3217 addObjectDependency(dobj, refdobj->dumpId);
3218
3219 if (!reftbinfo->relispopulated)
3220 tbinfo->relispopulated = false;
3221 }
3222
3223 PQclear(res);
3224
3225 destroyPQExpBuffer(query);
3226}
3227
3228/*
3229 * getTableDataFKConstraints -
3230 * add dump-order dependencies reflecting foreign key constraints
3231 *
3232 * This code is executed only in a data-only dump --- in schema+data dumps
3233 * we handle foreign key issues by not creating the FK constraints until
3234 * after the data is loaded. In a data-only dump, however, we want to
3235 * order the table data objects in such a way that a table's referenced
3236 * tables are restored first. (In the presence of circular references or
3237 * self-references this may be impossible; we'll detect and complain about
3238 * that during the dependency sorting step.)
3239 */
3240static void
3242{
3243 DumpableObject **dobjs;
3244 int numObjs;
3245 int i;
3246
3247 /* Search through all the dumpable objects for FK constraints */
3248 getDumpableObjects(&dobjs, &numObjs);
3249 for (i = 0; i < numObjs; i++)
3250 {
3251 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3252 {
3253 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3254 TableInfo *ftable;
3255
3256 /* Not interesting unless both tables are to be dumped */
3257 if (cinfo->contable == NULL ||
3258 cinfo->contable->dataObj == NULL)
3259 continue;
3260 ftable = findTableByOid(cinfo->confrelid);
3261 if (ftable == NULL ||
3262 ftable->dataObj == NULL)
3263 continue;
3264
3265 /*
3266 * Okay, make referencing table's TABLE_DATA object depend on the
3267 * referenced table's TABLE_DATA object.
3268 */
3270 ftable->dataObj->dobj.dumpId);
3271 }
3272 }
3273 free(dobjs);
3274}
3275
3276
3277/*
3278 * dumpDatabase:
3279 * dump the database definition
3280 */
3281static void
3283{
3284 DumpOptions *dopt = fout->dopt;
3286 PQExpBuffer delQry = createPQExpBuffer();
3287 PQExpBuffer creaQry = createPQExpBuffer();
3288 PQExpBuffer labelq = createPQExpBuffer();
3289 PGconn *conn = GetConnection(fout);
3290 PGresult *res;
3291 int i_tableoid,
3292 i_oid,
3293 i_datname,
3294 i_datdba,
3295 i_encoding,
3296 i_datlocprovider,
3297 i_collate,
3298 i_ctype,
3299 i_datlocale,
3300 i_daticurules,
3301 i_frozenxid,
3302 i_minmxid,
3303 i_datacl,
3304 i_acldefault,
3305 i_datistemplate,
3306 i_datconnlimit,
3307 i_datcollversion,
3308 i_tablespace;
3309 CatalogId dbCatId;
3310 DumpId dbDumpId;
3311 DumpableAcl dbdacl;
3312 const char *datname,
3313 *dba,
3314 *encoding,
3316 *collate,
3317 *ctype,
3318 *locale,
3319 *icurules,
3321 *datconnlimit,
3322 *tablespace;
3323 uint32 frozenxid,
3324 minmxid;
3325 char *qdatname;
3326
3327 pg_log_info("saving database definition");
3328
3329 /*
3330 * Fetch the database-level properties for this database.
3331 */
3332 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3333 "datdba, "
3334 "pg_encoding_to_char(encoding) AS encoding, "
3335 "datcollate, datctype, datfrozenxid, "
3336 "datacl, acldefault('d', datdba) AS acldefault, "
3337 "datistemplate, datconnlimit, ");
3338 if (fout->remoteVersion >= 90300)
3339 appendPQExpBufferStr(dbQry, "datminmxid, ");
3340 else
3341 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3342 if (fout->remoteVersion >= 170000)
3343 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3344 else if (fout->remoteVersion >= 150000)
3345 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3346 else
3347 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3348 if (fout->remoteVersion >= 160000)
3349 appendPQExpBufferStr(dbQry, "daticurules, ");
3350 else
3351 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3353 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3354 "shobj_description(oid, 'pg_database') AS description "
3355 "FROM pg_database "
3356 "WHERE datname = current_database()");
3357
3358 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3359
3360 i_tableoid = PQfnumber(res, "tableoid");
3361 i_oid = PQfnumber(res, "oid");
3362 i_datname = PQfnumber(res, "datname");
3363 i_datdba = PQfnumber(res, "datdba");
3364 i_encoding = PQfnumber(res, "encoding");
3365 i_datlocprovider = PQfnumber(res, "datlocprovider");
3366 i_collate = PQfnumber(res, "datcollate");
3367 i_ctype = PQfnumber(res, "datctype");
3368 i_datlocale = PQfnumber(res, "datlocale");
3369 i_daticurules = PQfnumber(res, "daticurules");
3370 i_frozenxid = PQfnumber(res, "datfrozenxid");
3371 i_minmxid = PQfnumber(res, "datminmxid");
3372 i_datacl = PQfnumber(res, "datacl");
3373 i_acldefault = PQfnumber(res, "acldefault");
3374 i_datistemplate = PQfnumber(res, "datistemplate");
3375 i_datconnlimit = PQfnumber(res, "datconnlimit");
3376 i_datcollversion = PQfnumber(res, "datcollversion");
3377 i_tablespace = PQfnumber(res, "tablespace");
3378
3379 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3380 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3381 datname = PQgetvalue(res, 0, i_datname);
3382 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3383 encoding = PQgetvalue(res, 0, i_encoding);
3384 datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3385 collate = PQgetvalue(res, 0, i_collate);
3386 ctype = PQgetvalue(res, 0, i_ctype);
3387 if (!PQgetisnull(res, 0, i_datlocale))
3388 locale = PQgetvalue(res, 0, i_datlocale);
3389 else
3390 locale = NULL;
3391 if (!PQgetisnull(res, 0, i_daticurules))
3392 icurules = PQgetvalue(res, 0, i_daticurules);
3393 else
3394 icurules = NULL;
3395 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3396 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3397 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3398 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3399 datistemplate = PQgetvalue(res, 0, i_datistemplate);
3400 datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3401 tablespace = PQgetvalue(res, 0, i_tablespace);
3402
3403 qdatname = pg_strdup(fmtId(datname));
3404
3405 /*
3406 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3407 * to preserve that), as well as the encoding, locale, and tablespace
3408 * since those can't be altered later. Other DB properties are left to
3409 * the DATABASE PROPERTIES entry, so that they can be applied after
3410 * reconnecting to the target DB.
3411 *
3412 * For binary upgrade, we use the FILE_COPY strategy because testing has
3413 * shown it to be faster. When the server is in binary upgrade mode, it
3414 * will also skip the checkpoints this strategy ordinarily performs.
3415 */
3416 if (dopt->binary_upgrade)
3417 {
3418 appendPQExpBuffer(creaQry,
3419 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3420 "OID = %u STRATEGY = FILE_COPY",
3421 qdatname, dbCatId.oid);
3422 }
3423 else
3424 {
3425 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3426 qdatname);
3427 }
3428 if (strlen(encoding) > 0)
3429 {
3430 appendPQExpBufferStr(creaQry, " ENCODING = ");
3431 appendStringLiteralAH(creaQry, encoding, fout);
3432 }
3433
3434 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3435 if (datlocprovider[0] == 'b')
3436 appendPQExpBufferStr(creaQry, "builtin");
3437 else if (datlocprovider[0] == 'c')
3438 appendPQExpBufferStr(creaQry, "libc");
3439 else if (datlocprovider[0] == 'i')
3440 appendPQExpBufferStr(creaQry, "icu");
3441 else
3442 pg_fatal("unrecognized locale provider: %s",
3444
3445 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3446 {
3447 appendPQExpBufferStr(creaQry, " LOCALE = ");
3448 appendStringLiteralAH(creaQry, collate, fout);
3449 }
3450 else
3451 {
3452 if (strlen(collate) > 0)
3453 {
3454 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3455 appendStringLiteralAH(creaQry, collate, fout);
3456 }
3457 if (strlen(ctype) > 0)
3458 {
3459 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3460 appendStringLiteralAH(creaQry, ctype, fout);
3461 }
3462 }
3463 if (locale)
3464 {
3465 if (datlocprovider[0] == 'b')
3466 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3467 else
3468 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3469
3470 appendStringLiteralAH(creaQry, locale, fout);
3471 }
3472
3473 if (icurules)
3474 {
3475 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3476 appendStringLiteralAH(creaQry, icurules, fout);
3477 }
3478
3479 /*
3480 * For binary upgrade, carry over the collation version. For normal
3481 * dump/restore, omit the version, so that it is computed upon restore.
3482 */
3483 if (dopt->binary_upgrade)
3484 {
3485 if (!PQgetisnull(res, 0, i_datcollversion))
3486 {
3487 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3488 appendStringLiteralAH(creaQry,
3489 PQgetvalue(res, 0, i_datcollversion),
3490 fout);
3491 }
3492 }
3493
3494 /*
3495 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3496 * thing; the decision whether to specify a tablespace should be left till
3497 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3498 * label the DATABASE entry with the tablespace and let the normal
3499 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3500 * attention to default_tablespace, so that won't work.
3501 */
3502 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3503 !dopt->outputNoTablespaces)
3504 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3505 fmtId(tablespace));
3506 appendPQExpBufferStr(creaQry, ";\n");
3507
3508 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3509 qdatname);
3510
3511 dbDumpId = createDumpId();
3512
3513 ArchiveEntry(fout,
3514 dbCatId, /* catalog ID */
3515 dbDumpId, /* dump ID */
3516 ARCHIVE_OPTS(.tag = datname,
3517 .owner = dba,
3518 .description = "DATABASE",
3519 .section = SECTION_PRE_DATA,
3520 .createStmt = creaQry->data,
3521 .dropStmt = delQry->data));
3522
3523 /* Compute correct tag for archive entry */
3524 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3525
3526 /* Dump DB comment if any */
3527 {
3528 /*
3529 * 8.2 and up keep comments on shared objects in a shared table, so we
3530 * cannot use the dumpComment() code used for other database objects.
3531 * Be careful that the ArchiveEntry parameters match that function.
3532 */
3533 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3534
3535 if (comment && *comment && !dopt->no_comments)
3536 {
3537 resetPQExpBuffer(dbQry);
3538
3539 /*
3540 * Generates warning when loaded into a differently-named
3541 * database.
3542 */
3543 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3544 appendStringLiteralAH(dbQry, comment, fout);
3545 appendPQExpBufferStr(dbQry, ";\n");
3546
3548 ARCHIVE_OPTS(.tag = labelq->data,
3549 .owner = dba,
3550 .description = "COMMENT",
3551 .section = SECTION_NONE,
3552 .createStmt = dbQry->data,
3553 .deps = &dbDumpId,
3554 .nDeps = 1));
3555 }
3556 }
3557
3558 /* Dump DB security label, if enabled */
3559 if (!dopt->no_security_labels)
3560 {
3561 PGresult *shres;
3562 PQExpBuffer seclabelQry;
3563
3564 seclabelQry = createPQExpBuffer();
3565
3566 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3567 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3568 resetPQExpBuffer(seclabelQry);
3569 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3570 if (seclabelQry->len > 0)
3572 ARCHIVE_OPTS(.tag = labelq->data,
3573 .owner = dba,
3574 .description = "SECURITY LABEL",
3575 .section = SECTION_NONE,
3576 .createStmt = seclabelQry->data,
3577 .deps = &dbDumpId,
3578 .nDeps = 1));
3579 destroyPQExpBuffer(seclabelQry);
3580 PQclear(shres);
3581 }
3582
3583 /*
3584 * Dump ACL if any. Note that we do not support initial privileges
3585 * (pg_init_privs) on databases.
3586 */
3587 dbdacl.privtype = 0;
3588 dbdacl.initprivs = NULL;
3589
3590 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3591 qdatname, NULL, NULL,
3592 NULL, dba, &dbdacl);
3593
3594 /*
3595 * Now construct a DATABASE PROPERTIES archive entry to restore any
3596 * non-default database-level properties. (The reason this must be
3597 * separate is that we cannot put any additional commands into the TOC
3598 * entry that has CREATE DATABASE. pg_restore would execute such a group
3599 * in an implicit transaction block, and the backend won't allow CREATE
3600 * DATABASE in that context.)
3601 */
3602 resetPQExpBuffer(creaQry);
3603 resetPQExpBuffer(delQry);
3604
3605 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3606 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3607 qdatname, datconnlimit);
3608
3609 if (strcmp(datistemplate, "t") == 0)
3610 {
3611 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3612 qdatname);
3613
3614 /*
3615 * The backend won't accept DROP DATABASE on a template database. We
3616 * can deal with that by removing the template marking before the DROP
3617 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3618 * since no such command is currently supported, fake it with a direct
3619 * UPDATE on pg_database.
3620 */
3621 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3622 "SET datistemplate = false WHERE datname = ");
3623 appendStringLiteralAH(delQry, datname, fout);
3624 appendPQExpBufferStr(delQry, ";\n");
3625 }
3626
3627 /*
3628 * We do not restore pg_database.dathasloginevt because it is set
3629 * automatically on login event trigger creation.
3630 */
3631
3632 /* Add database-specific SET options */
3633 dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3634
3635 /*
3636 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3637 * entry, too, for lack of a better place.
3638 */
3639 if (dopt->binary_upgrade)
3640 {
3641 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3642 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3643 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3644 "WHERE datname = ",
3645 frozenxid, minmxid);
3646 appendStringLiteralAH(creaQry, datname, fout);
3647 appendPQExpBufferStr(creaQry, ";\n");
3648 }
3649
3650 if (creaQry->len > 0)
3652 ARCHIVE_OPTS(.tag = datname,
3653 .owner = dba,
3654 .description = "DATABASE PROPERTIES",
3655 .section = SECTION_PRE_DATA,
3656 .createStmt = creaQry->data,
3657 .dropStmt = delQry->data,
3658 .deps = &dbDumpId));
3659
3660 /*
3661 * pg_largeobject comes from the old system intact, so set its
3662 * relfrozenxids, relminmxids and relfilenode.
3663 *
3664 * pg_largeobject_metadata also comes from the old system intact for
3665 * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3666 * relfilenode, too. pg_upgrade can't copy/link the files from older
3667 * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3668 * changed its storage format in v16.
3669 */
3670 if (dopt->binary_upgrade)
3671 {
3672 PGresult *lo_res;
3673 PQExpBuffer loFrozenQry = createPQExpBuffer();
3674 PQExpBuffer loOutQry = createPQExpBuffer();
3675 PQExpBuffer lomOutQry = createPQExpBuffer();
3676 PQExpBuffer loHorizonQry = createPQExpBuffer();
3677 PQExpBuffer lomHorizonQry = createPQExpBuffer();
3678 int ii_relfrozenxid,
3679 ii_relfilenode,
3680 ii_oid,
3681 ii_relminmxid;
3682
3683 if (fout->remoteVersion >= 90300)
3684 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3685 "FROM pg_catalog.pg_class\n"
3686 "WHERE oid IN (%u, %u, %u, %u);\n",
3687 LargeObjectRelationId, LargeObjectLOidPNIndexId,
3688 LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3689 else
3690 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3691 "FROM pg_catalog.pg_class\n"
3692 "WHERE oid IN (%u, %u);\n",
3693 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3694
3695 lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3696
3697 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3698 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3699 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3700 ii_oid = PQfnumber(lo_res, "oid");
3701
3702 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3703 appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3704 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3705 appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3706 for (int i = 0; i < PQntuples(lo_res); ++i)
3707 {
3708 Oid oid;
3709 RelFileNumber relfilenumber;
3710 PQExpBuffer horizonQry;
3711 PQExpBuffer outQry;
3712
3713 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3714 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3715
3716 if (oid == LargeObjectRelationId ||
3717 oid == LargeObjectLOidPNIndexId)
3718 {
3719 horizonQry = loHorizonQry;
3720 outQry = loOutQry;
3721 }
3722 else
3723 {
3724 horizonQry = lomHorizonQry;
3725 outQry = lomOutQry;
3726 }
3727
3728 appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3729 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3730 "WHERE oid = %u;\n",
3731 atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3732 atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3733 atooid(PQgetvalue(lo_res, i, ii_oid)));
3734
3735 if (oid == LargeObjectRelationId ||
3736 oid == LargeObjectMetadataRelationId)
3737 appendPQExpBuffer(outQry,
3738 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3739 relfilenumber);
3740 else if (oid == LargeObjectLOidPNIndexId ||
3741 oid == LargeObjectMetadataOidIndexId)
3742 appendPQExpBuffer(outQry,
3743 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3744 relfilenumber);
3745 }
3746
3747 appendPQExpBufferStr(loOutQry,
3748 "TRUNCATE pg_catalog.pg_largeobject;\n");
3749 appendPQExpBufferStr(lomOutQry,
3750 "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3751
3752 appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3753 appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3754
3756 ARCHIVE_OPTS(.tag = "pg_largeobject",
3757 .description = "pg_largeobject",
3758 .section = SECTION_PRE_DATA,
3759 .createStmt = loOutQry->data));
3760
3761 if (fout->remoteVersion >= 160000)
3763 ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3764 .description = "pg_largeobject_metadata",
3765 .section = SECTION_PRE_DATA,
3766 .createStmt = lomOutQry->data));
3767
3768 PQclear(lo_res);
3769
3770 destroyPQExpBuffer(loFrozenQry);
3771 destroyPQExpBuffer(loHorizonQry);
3772 destroyPQExpBuffer(lomHorizonQry);
3773 destroyPQExpBuffer(loOutQry);
3774 destroyPQExpBuffer(lomOutQry);
3775 }
3776
3777 PQclear(res);
3778
3779 free(qdatname);
3780 destroyPQExpBuffer(dbQry);
3781 destroyPQExpBuffer(delQry);
3782 destroyPQExpBuffer(creaQry);
3783 destroyPQExpBuffer(labelq);
3784}
3785
3786/*
3787 * Collect any database-specific or role-and-database-specific SET options
3788 * for this database, and append them to outbuf.
3789 */
3790static void
3792 const char *dbname, Oid dboid)
3793{
3794 PGconn *conn = GetConnection(AH);
3796 PGresult *res;
3797
3798 /* First collect database-specific options */
3799 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3800 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3801 dboid);
3802
3803 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3804
3805 for (int i = 0; i < PQntuples(res); i++)
3807 "DATABASE", dbname, NULL, NULL,
3808 outbuf);
3809
3810 PQclear(res);
3811
3812 /* Now look for role-and-database-specific options */
3813 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3814 "FROM pg_db_role_setting s, pg_roles r "
3815 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3816 dboid);
3817
3818 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3819
3820 for (int i = 0; i < PQntuples(res); i++)
3822 "ROLE", PQgetvalue(res, i, 0),
3823 "DATABASE", dbname,
3824 outbuf);
3825
3826 PQclear(res);
3827
3829}
3830
3831/*
3832 * dumpEncoding: put the correct encoding into the archive
3833 */
3834static void
3836{
3837 const char *encname = pg_encoding_to_char(AH->encoding);
3839
3840 pg_log_info("saving encoding = %s", encname);
3841
3842 appendPQExpBufferStr(qry, "SET client_encoding = ");
3843 appendStringLiteralAH(qry, encname, AH);
3844 appendPQExpBufferStr(qry, ";\n");
3845
3847 ARCHIVE_OPTS(.tag = "ENCODING",
3848 .description = "ENCODING",
3849 .section = SECTION_PRE_DATA,
3850 .createStmt = qry->data));
3851
3852 destroyPQExpBuffer(qry);
3853}
3854
3855
3856/*
3857 * dumpStdStrings: put the correct escape string behavior into the archive
3858 */
3859static void
3861{
3862 const char *stdstrings = AH->std_strings ? "on" : "off";
3864
3865 pg_log_info("saving \"standard_conforming_strings = %s\"",
3866 stdstrings);
3867
3868 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3869 stdstrings);
3870
3872 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3873 .description = "STDSTRINGS",
3874 .section = SECTION_PRE_DATA,
3875 .createStmt = qry->data));
3876
3877 destroyPQExpBuffer(qry);
3878}
3879
3880/*
3881 * dumpSearchPath: record the active search_path in the archive
3882 */
3883static void
3885{
3888 PGresult *res;
3889 char **schemanames = NULL;
3890 int nschemanames = 0;
3891 int i;
3892
3893 /*
3894 * We use the result of current_schemas(), not the search_path GUC,
3895 * because that might contain wildcards such as "$user", which won't
3896 * necessarily have the same value during restore. Also, this way avoids
3897 * listing schemas that may appear in search_path but not actually exist,
3898 * which seems like a prudent exclusion.
3899 */
3901 "SELECT pg_catalog.current_schemas(false)");
3902
3903 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3904 pg_fatal("could not parse result of current_schemas()");
3905
3906 /*
3907 * We use set_config(), not a simple "SET search_path" command, because
3908 * the latter has less-clean behavior if the search path is empty. While
3909 * that's likely to get fixed at some point, it seems like a good idea to
3910 * be as backwards-compatible as possible in what we put into archives.
3911 */
3912 for (i = 0; i < nschemanames; i++)
3913 {
3914 if (i > 0)
3915 appendPQExpBufferStr(path, ", ");
3916 appendPQExpBufferStr(path, fmtId(schemanames[i]));
3917 }
3918
3919 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3920 appendStringLiteralAH(qry, path->data, AH);
3921 appendPQExpBufferStr(qry, ", false);\n");
3922
3923 pg_log_info("saving \"search_path = %s\"", path->data);
3924
3926 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3927 .description = "SEARCHPATH",
3928 .section = SECTION_PRE_DATA,
3929 .createStmt = qry->data));
3930
3931 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3932 AH->searchpath = pg_strdup(qry->data);
3933
3934 free(schemanames);
3935 PQclear(res);
3936 destroyPQExpBuffer(qry);
3937 destroyPQExpBuffer(path);
3938}
3939
3940
3941/*
3942 * getLOs:
3943 * Collect schema-level data about large objects
3944 */
3945static void
3947{
3948 DumpOptions *dopt = fout->dopt;
3950 PGresult *res;
3951 int ntups;
3952 int i;
3953 int n;
3954 int i_oid;
3955 int i_lomowner;
3956 int i_lomacl;
3957 int i_acldefault;
3958
3959 pg_log_info("reading large objects");
3960
3961 /*
3962 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3963 * with the same owner/ACL appear together.
3964 */
3966 "SELECT oid, lomowner, lomacl, "
3967 "acldefault('L', lomowner) AS acldefault "
3968 "FROM pg_largeobject_metadata "
3969 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3970
3971 res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3972
3973 i_oid = PQfnumber(res, "oid");
3974 i_lomowner = PQfnumber(res, "lomowner");
3975 i_lomacl = PQfnumber(res, "lomacl");
3976 i_acldefault = PQfnumber(res, "acldefault");
3977
3978 ntups = PQntuples(res);
3979
3980 /*
3981 * Group the blobs into suitably-sized groups that have the same owner and
3982 * ACL setting, and build a metadata and a data DumpableObject for each
3983 * group. (If we supported initprivs for blobs, we'd have to insist that
3984 * groups also share initprivs settings, since the DumpableObject only has
3985 * room for one.) i is the index of the first tuple in the current group,
3986 * and n is the number of tuples we include in the group.
3987 */
3988 for (i = 0; i < ntups; i += n)
3989 {
3990 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3991 char *thisowner = PQgetvalue(res, i, i_lomowner);
3992 char *thisacl = PQgetvalue(res, i, i_lomacl);
3993 LoInfo *loinfo;
3994 DumpableObject *lodata;
3995 char namebuf[64];
3996
3997 /* Scan to find first tuple not to be included in group */
3998 n = 1;
3999 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4000 {
4001 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4002 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4003 break;
4004 n++;
4005 }
4006
4007 /* Build the metadata DumpableObject */
4008 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4009
4010 loinfo->dobj.objType = DO_LARGE_OBJECT;
4011 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4012 loinfo->dobj.catId.oid = thisoid;
4013 AssignDumpId(&loinfo->dobj);
4014
4015 if (n > 1)
4016 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4017 atooid(PQgetvalue(res, i + n - 1, i_oid)));
4018 else
4019 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4020 loinfo->dobj.name = pg_strdup(namebuf);
4021 loinfo->dacl.acl = pg_strdup(thisacl);
4022 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4023 loinfo->dacl.privtype = 0;
4024 loinfo->dacl.initprivs = NULL;
4025 loinfo->rolname = getRoleName(thisowner);
4026 loinfo->numlos = n;
4027 loinfo->looids[0] = thisoid;
4028 /* Collect OIDs of the remaining blobs in this group */
4029 for (int k = 1; k < n; k++)
4030 {
4031 CatalogId extraID;
4032
4033 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4034
4035 /* Make sure we can look up loinfo by any of the blobs' OIDs */
4036 extraID.tableoid = LargeObjectRelationId;
4037 extraID.oid = loinfo->looids[k];
4038 recordAdditionalCatalogID(extraID, &loinfo->dobj);
4039 }
4040
4041 /* LOs have data */
4043
4044 /* Mark whether LO group has a non-empty ACL */
4045 if (!PQgetisnull(res, i, i_lomacl))
4047
4048 /*
4049 * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4050 * as it will be copied by pg_upgrade, which simply copies the
4051 * pg_largeobject table. We *do* however dump out anything but the
4052 * data, as pg_upgrade copies just pg_largeobject, but not
4053 * pg_largeobject_metadata, after the dump is restored. In versions
4054 * before v12, this is done via proper large object commands. In
4055 * newer versions, we dump the content of pg_largeobject_metadata and
4056 * any associated pg_shdepend rows, which is faster to restore. (On
4057 * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4058 * column is hidden and won't be dumped.)
4059 */
4060 if (dopt->binary_upgrade)
4061 {
4062 if (fout->remoteVersion >= 120000)
4063 {
4064 /*
4065 * We should've saved pg_largeobject_metadata's dump ID before
4066 * this point.
4067 */
4069
4071
4072 /*
4073 * Mark the large object as dependent on
4074 * pg_largeobject_metadata so that any large object
4075 * comments/seclables are dumped after it.
4076 */
4077 loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4079 loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4080 }
4081 else
4082 loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4083 }
4084
4085 /*
4086 * Create a "BLOBS" data item for the group, too. This is just a
4087 * placeholder for sorting; it carries no data now.
4088 */
4089 lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4090 lodata->objType = DO_LARGE_OBJECT_DATA;
4091 lodata->catId = nilCatalogId;
4092 AssignDumpId(lodata);
4093 lodata->name = pg_strdup(namebuf);
4095 /* Set up explicit dependency from data to metadata */
4096 lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4097 lodata->dependencies[0] = loinfo->dobj.dumpId;
4098 lodata->nDeps = lodata->allocDeps = 1;
4099 }
4100
4101 PQclear(res);
4102 destroyPQExpBuffer(loQry);
4103}
4104
4105/*
4106 * dumpLO
4107 *
4108 * dump the definition (metadata) of the given large object group
4109 */
4110static void
4111dumpLO(Archive *fout, const LoInfo *loinfo)
4112{
4113 PQExpBuffer cquery = createPQExpBuffer();
4114
4115 /*
4116 * The "definition" is just a newline-separated list of OIDs. We need to
4117 * put something into the dropStmt too, but it can just be a comment.
4118 */
4119 for (int i = 0; i < loinfo->numlos; i++)
4120 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4121
4122 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4123 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4124 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4125 .owner = loinfo->rolname,
4126 .description = "BLOB METADATA",
4127 .section = SECTION_DATA,
4128 .createStmt = cquery->data,
4129 .dropStmt = "-- dummy"));
4130
4131 /*
4132 * Dump per-blob comments and seclabels if any. We assume these are rare
4133 * enough that it's okay to generate retail TOC entries for them.
4134 */
4135 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4137 {
4138 for (int i = 0; i < loinfo->numlos; i++)
4139 {
4140 CatalogId catId;
4141 char namebuf[32];
4142
4143 /* Build identifying info for this blob */
4144 catId.tableoid = loinfo->dobj.catId.tableoid;
4145 catId.oid = loinfo->looids[i];
4146 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4147
4148 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4149 dumpComment(fout, "LARGE OBJECT", namebuf,
4150 NULL, loinfo->rolname,
4151 catId, 0, loinfo->dobj.dumpId);
4152
4153 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4154 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4155 NULL, loinfo->rolname,
4156 catId, 0, loinfo->dobj.dumpId);
4157 }
4158 }
4159
4160 /*
4161 * Dump the ACLs if any (remember that all blobs in the group will have
4162 * the same ACL). If there's just one blob, dump a simple ACL entry; if
4163 * there's more, make a "LARGE OBJECTS" entry that really contains only
4164 * the ACL for the first blob. _printTocEntry() will be cued by the tag
4165 * string to emit a mutated version for each blob.
4166 */
4167 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4168 {
4169 char namebuf[32];
4170
4171 /* Build identifying info for the first blob */
4172 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4173
4174 if (loinfo->numlos > 1)
4175 {
4176 char tagbuf[64];
4177
4178 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4179 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4180
4181 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4182 "LARGE OBJECT", namebuf, NULL, NULL,
4183 tagbuf, loinfo->rolname, &loinfo->dacl);
4184 }
4185 else
4186 {
4187 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4188 "LARGE OBJECT", namebuf, NULL, NULL,
4189 NULL, loinfo->rolname, &loinfo->dacl);
4190 }
4191 }
4192
4193 destroyPQExpBuffer(cquery);
4194}
4195
4196/*
4197 * dumpLOs:
4198 * dump the data contents of the large objects in the given group
4199 */
4200static int
4201dumpLOs(Archive *fout, const void *arg)
4202{
4203 const LoInfo *loinfo = (const LoInfo *) arg;
4204 PGconn *conn = GetConnection(fout);
4205 char buf[LOBBUFSIZE];
4206
4207 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4208
4209 for (int i = 0; i < loinfo->numlos; i++)
4210 {
4211 Oid loOid = loinfo->looids[i];
4212 int loFd;
4213 int cnt;
4214
4215 /* Open the LO */
4216 loFd = lo_open(conn, loOid, INV_READ);
4217 if (loFd == -1)
4218 pg_fatal("could not open large object %u: %s",
4219 loOid, PQerrorMessage(conn));
4220
4221 StartLO(fout, loOid);
4222
4223 /* Now read it in chunks, sending data to archive */
4224 do
4225 {
4226 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4227 if (cnt < 0)
4228 pg_fatal("error reading large object %u: %s",
4229 loOid, PQerrorMessage(conn));
4230
4231 WriteData(fout, buf, cnt);
4232 } while (cnt > 0);
4233
4234 lo_close(conn, loFd);
4235
4236 EndLO(fout, loOid);
4237 }
4238
4239 return 1;
4240}
4241
4242/*
4243 * getPolicies
4244 * get information about all RLS policies on dumpable tables.
4245 */
4246void
4247getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4248{
4249 DumpOptions *dopt = fout->dopt;
4250 PQExpBuffer query;
4251 PQExpBuffer tbloids;
4252 PGresult *res;
4253 PolicyInfo *polinfo;
4254 int i_oid;
4255 int i_tableoid;
4256 int i_polrelid;
4257 int i_polname;
4258 int i_polcmd;
4259 int i_polpermissive;
4260 int i_polroles;
4261 int i_polqual;
4262 int i_polwithcheck;
4263 int i,
4264 j,
4265 ntups;
4266
4267 /* No policies before 9.5 */
4268 if (fout->remoteVersion < 90500)
4269 return;
4270
4271 /* Skip if --no-policies was specified */
4272 if (dopt->no_policies)
4273 return;
4274
4275 query = createPQExpBuffer();
4276 tbloids = createPQExpBuffer();
4277
4278 /*
4279 * Identify tables of interest, and check which ones have RLS enabled.
4280 */
4281 appendPQExpBufferChar(tbloids, '{');
4282 for (i = 0; i < numTables; i++)
4283 {
4284 TableInfo *tbinfo = &tblinfo[i];
4285
4286 /* Ignore row security on tables not to be dumped */
4287 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4288 continue;
4289
4290 /* It can't have RLS or policies if it's not a table */
4291 if (tbinfo->relkind != RELKIND_RELATION &&
4292 tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4293 continue;
4294
4295 /* Add it to the list of table OIDs to be probed below */
4296 if (tbloids->len > 1) /* do we have more than the '{'? */
4297 appendPQExpBufferChar(tbloids, ',');
4298 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4299
4300 /* Is RLS enabled? (That's separate from whether it has policies) */
4301 if (tbinfo->rowsec)
4302 {
4304
4305 /*
4306 * We represent RLS being enabled on a table by creating a
4307 * PolicyInfo object with null polname.
4308 *
4309 * Note: use tableoid 0 so that this object won't be mistaken for
4310 * something that pg_depend entries apply to.
4311 */
4312 polinfo = pg_malloc(sizeof(PolicyInfo));
4313 polinfo->dobj.objType = DO_POLICY;
4314 polinfo->dobj.catId.tableoid = 0;
4315 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4316 AssignDumpId(&polinfo->dobj);
4317 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4318 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4319 polinfo->poltable = tbinfo;
4320 polinfo->polname = NULL;
4321 polinfo->polcmd = '\0';
4322 polinfo->polpermissive = 0;
4323 polinfo->polroles = NULL;
4324 polinfo->polqual = NULL;
4325 polinfo->polwithcheck = NULL;
4326 }
4327 }
4328 appendPQExpBufferChar(tbloids, '}');
4329
4330 /*
4331 * Now, read all RLS policies belonging to the tables of interest, and
4332 * create PolicyInfo objects for them. (Note that we must filter the
4333 * results server-side not locally, because we dare not apply pg_get_expr
4334 * to tables we don't have lock on.)
4335 */
4336 pg_log_info("reading row-level security policies");
4337
4338 printfPQExpBuffer(query,
4339 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4340 if (fout->remoteVersion >= 100000)
4341 appendPQExpBufferStr(query, "pol.polpermissive, ");
4342 else
4343 appendPQExpBufferStr(query, "'t' as polpermissive, ");
4344 appendPQExpBuffer(query,
4345 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4346 " 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, "
4347 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4348 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4349 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4350 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4351 tbloids->data);
4352
4353 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4354
4355 ntups = PQntuples(res);
4356 if (ntups > 0)
4357 {
4358 i_oid = PQfnumber(res, "oid");
4359 i_tableoid = PQfnumber(res, "tableoid");
4360 i_polrelid = PQfnumber(res, "polrelid");
4361 i_polname = PQfnumber(res, "polname");
4362 i_polcmd = PQfnumber(res, "polcmd");
4363 i_polpermissive = PQfnumber(res, "polpermissive");
4364 i_polroles = PQfnumber(res, "polroles");
4365 i_polqual = PQfnumber(res, "polqual");
4366 i_polwithcheck = PQfnumber(res, "polwithcheck");
4367
4368 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4369
4370 for (j = 0; j < ntups; j++)
4371 {
4372 Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4373 TableInfo *tbinfo = findTableByOid(polrelid);
4374
4376
4377 polinfo[j].dobj.objType = DO_POLICY;
4378 polinfo[j].dobj.catId.tableoid =
4379 atooid(PQgetvalue(res, j, i_tableoid));
4380 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4381 AssignDumpId(&polinfo[j].dobj);
4382 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4383 polinfo[j].poltable = tbinfo;
4384 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4385 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4386
4387 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4388 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4389
4390 if (PQgetisnull(res, j, i_polroles))
4391 polinfo[j].polroles = NULL;
4392 else
4393 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4394
4395 if (PQgetisnull(res, j, i_polqual))
4396 polinfo[j].polqual = NULL;
4397 else
4398 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4399
4400 if (PQgetisnull(res, j, i_polwithcheck))
4401 polinfo[j].polwithcheck = NULL;
4402 else
4403 polinfo[j].polwithcheck
4404 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4405 }
4406 }
4407
4408 PQclear(res);
4409
4410 destroyPQExpBuffer(query);
4411 destroyPQExpBuffer(tbloids);
4412}
4413
4414/*
4415 * dumpPolicy
4416 * dump the definition of the given policy
4417 */
4418static void
4419dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4420{
4421 DumpOptions *dopt = fout->dopt;
4422 TableInfo *tbinfo = polinfo->poltable;
4423 PQExpBuffer query;
4424 PQExpBuffer delqry;
4425 PQExpBuffer polprefix;
4426 char *qtabname;
4427 const char *cmd;
4428 char *tag;
4429
4430 /* Do nothing if not dumping schema */
4431 if (!dopt->dumpSchema)
4432 return;
4433
4434 /*
4435 * If polname is NULL, then this record is just indicating that ROW LEVEL
4436 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4437 * ROW LEVEL SECURITY.
4438 */
4439 if (polinfo->polname == NULL)
4440 {
4441 query = createPQExpBuffer();
4442
4443 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4444 fmtQualifiedDumpable(tbinfo));
4445
4446 /*
4447 * We must emit the ROW SECURITY object's dependency on its table
4448 * explicitly, because it will not match anything in pg_depend (unlike
4449 * the case for other PolicyInfo objects).
4450 */
4451 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4452 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4453 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4454 .namespace = polinfo->dobj.namespace->dobj.name,
4455 .owner = tbinfo->rolname,
4456 .description = "ROW SECURITY",
4457 .section = SECTION_POST_DATA,
4458 .createStmt = query->data,
4459 .deps = &(tbinfo->dobj.dumpId),
4460 .nDeps = 1));
4461
4462 destroyPQExpBuffer(query);
4463 return;
4464 }
4465
4466 if (polinfo->polcmd == '*')
4467 cmd = "";
4468 else if (polinfo->polcmd == 'r')
4469 cmd = " FOR SELECT";
4470 else if (polinfo->polcmd == 'a')
4471 cmd = " FOR INSERT";
4472 else if (polinfo->polcmd == 'w')
4473 cmd = " FOR UPDATE";
4474 else if (polinfo->polcmd == 'd')
4475 cmd = " FOR DELETE";
4476 else
4477 pg_fatal("unexpected policy command type: %c",
4478 polinfo->polcmd);
4479
4480 query = createPQExpBuffer();
4481 delqry = createPQExpBuffer();
4482 polprefix = createPQExpBuffer();
4483
4484 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4485
4486 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4487
4488 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4489 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4490
4491 if (polinfo->polroles != NULL)
4492 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4493
4494 if (polinfo->polqual != NULL)
4495 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4496
4497 if (polinfo->polwithcheck != NULL)
4498 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4499
4500 appendPQExpBufferStr(query, ";\n");
4501
4502 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4503 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4504
4505 appendPQExpBuffer(polprefix, "POLICY %s ON",
4506 fmtId(polinfo->polname));
4507
4508 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4509
4510 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4511 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4512 ARCHIVE_OPTS(.tag = tag,
4513 .namespace = polinfo->dobj.namespace->dobj.name,
4514 .owner = tbinfo->rolname,
4515 .description = "POLICY",
4516 .section = SECTION_POST_DATA,
4517 .createStmt = query->data,
4518 .dropStmt = delqry->data));
4519
4520 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4521 dumpComment(fout, polprefix->data, qtabname,
4522 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4523 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4524
4525 free(tag);
4526 destroyPQExpBuffer(query);
4527 destroyPQExpBuffer(delqry);
4528 destroyPQExpBuffer(polprefix);
4529 free(qtabname);
4530}
4531
4532/*
4533 * getPublications
4534 * get information about publications
4535 */
4536void
4538{
4539 DumpOptions *dopt = fout->dopt;
4540 PQExpBuffer query;
4541 PGresult *res;
4542 PublicationInfo *pubinfo;
4543 int i_tableoid;
4544 int i_oid;
4545 int i_pubname;
4546 int i_pubowner;
4547 int i_puballtables;
4548 int i_puballsequences;
4549 int i_pubinsert;
4550 int i_pubupdate;
4551 int i_pubdelete;
4552 int i_pubtruncate;
4553 int i_pubviaroot;
4554 int i_pubgencols;
4555 int i,
4556 ntups;
4557
4558 if (dopt->no_publications || fout->remoteVersion < 100000)
4559 return;
4560
4561 query = createPQExpBuffer();
4562
4563 /* Get the publications. */
4564 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4565 "p.pubowner, p.puballtables, p.pubinsert, "
4566 "p.pubupdate, p.pubdelete, ");
4567
4568 if (fout->remoteVersion >= 110000)
4569 appendPQExpBufferStr(query, "p.pubtruncate, ");
4570 else
4571 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4572
4573 if (fout->remoteVersion >= 130000)
4574 appendPQExpBufferStr(query, "p.pubviaroot, ");
4575 else
4576 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4577
4578 if (fout->remoteVersion >= 180000)
4579 appendPQExpBufferStr(query, "p.pubgencols, ");
4580 else
4581 appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4582
4583 if (fout->remoteVersion >= 190000)
4584 appendPQExpBufferStr(query, "p.puballsequences ");
4585 else
4586 appendPQExpBufferStr(query, "false AS puballsequences ");
4587
4588 appendPQExpBufferStr(query, "FROM pg_publication p");
4589
4590 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4591
4592 ntups = PQntuples(res);
4593
4594 if (ntups == 0)
4595 goto cleanup;
4596
4597 i_tableoid = PQfnumber(res, "tableoid");
4598 i_oid = PQfnumber(res, "oid");
4599 i_pubname = PQfnumber(res, "pubname");
4600 i_pubowner = PQfnumber(res, "pubowner");
4601 i_puballtables = PQfnumber(res, "puballtables");
4602 i_puballsequences = PQfnumber(res, "puballsequences");
4603 i_pubinsert = PQfnumber(res, "pubinsert");
4604 i_pubupdate = PQfnumber(res, "pubupdate");
4605 i_pubdelete = PQfnumber(res, "pubdelete");
4606 i_pubtruncate = PQfnumber(res, "pubtruncate");
4607 i_pubviaroot = PQfnumber(res, "pubviaroot");
4608 i_pubgencols = PQfnumber(res, "pubgencols");
4609
4610 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4611
4612 for (i = 0; i < ntups; i++)
4613 {
4614 pubinfo[i].dobj.objType = DO_PUBLICATION;
4615 pubinfo[i].dobj.catId.tableoid =
4616 atooid(PQgetvalue(res, i, i_tableoid));
4617 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4618 AssignDumpId(&pubinfo[i].dobj);
4619 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4620 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4621 pubinfo[i].puballtables =
4622 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4623 pubinfo[i].puballsequences =
4624 (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4625 pubinfo[i].pubinsert =
4626 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4627 pubinfo[i].pubupdate =
4628 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4629 pubinfo[i].pubdelete =
4630 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4631 pubinfo[i].pubtruncate =
4632 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4633 pubinfo[i].pubviaroot =
4634 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4635 pubinfo[i].pubgencols_type =
4636 *(PQgetvalue(res, i, i_pubgencols));
4637
4638 /* Decide whether we want to dump it */
4639 selectDumpableObject(&(pubinfo[i].dobj), fout);
4640 }
4641
4642cleanup:
4643 PQclear(res);
4644
4645 destroyPQExpBuffer(query);
4646}
4647
4648/*
4649 * dumpPublication
4650 * dump the definition of the given publication
4651 */
4652static void
4654{
4655 DumpOptions *dopt = fout->dopt;
4656 PQExpBuffer delq;
4657 PQExpBuffer query;
4658 char *qpubname;
4659 bool first = true;
4660
4661 /* Do nothing if not dumping schema */
4662 if (!dopt->dumpSchema)
4663 return;
4664
4665 delq = createPQExpBuffer();
4666 query = createPQExpBuffer();
4667
4668 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4669
4670 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4671 qpubname);
4672
4673 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4674 qpubname);
4675
4676 if (pubinfo->puballtables && pubinfo->puballsequences)
4677 appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4678 else if (pubinfo->puballtables)
4679 appendPQExpBufferStr(query, " FOR ALL TABLES");
4680 else if (pubinfo->puballsequences)
4681 appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4682
4683 appendPQExpBufferStr(query, " WITH (publish = '");
4684 if (pubinfo->pubinsert)
4685 {
4686 appendPQExpBufferStr(query, "insert");
4687 first = false;
4688 }
4689
4690 if (pubinfo->pubupdate)
4691 {
4692 if (!first)
4693 appendPQExpBufferStr(query, ", ");
4694
4695 appendPQExpBufferStr(query, "update");
4696 first = false;
4697 }
4698
4699 if (pubinfo->pubdelete)
4700 {
4701 if (!first)
4702 appendPQExpBufferStr(query, ", ");
4703
4704 appendPQExpBufferStr(query, "delete");
4705 first = false;
4706 }
4707
4708 if (pubinfo->pubtruncate)
4709 {
4710 if (!first)
4711 appendPQExpBufferStr(query, ", ");
4712
4713 appendPQExpBufferStr(query, "truncate");
4714 first = false;
4715 }
4716
4717 appendPQExpBufferChar(query, '\'');
4718
4719 if (pubinfo->pubviaroot)
4720 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4721
4722 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4723 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4724
4725 appendPQExpBufferStr(query, ");\n");
4726
4727 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4728 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4729 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4730 .owner = pubinfo->rolname,
4731 .description = "PUBLICATION",
4732 .section = SECTION_POST_DATA,
4733 .createStmt = query->data,
4734 .dropStmt = delq->data));
4735
4736 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4737 dumpComment(fout, "PUBLICATION", qpubname,
4738 NULL, pubinfo->rolname,
4739 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4740
4741 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4742 dumpSecLabel(fout, "PUBLICATION", qpubname,
4743 NULL, pubinfo->rolname,
4744 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4745
4746 destroyPQExpBuffer(delq);
4747 destroyPQExpBuffer(query);
4748 free(qpubname);
4749}
4750
4751/*
4752 * getPublicationNamespaces
4753 * get information about publication membership for dumpable schemas.
4754 */
4755void
4757{
4758 PQExpBuffer query;
4759 PGresult *res;
4760 PublicationSchemaInfo *pubsinfo;
4761 DumpOptions *dopt = fout->dopt;
4762 int i_tableoid;
4763 int i_oid;
4764 int i_pnpubid;
4765 int i_pnnspid;
4766 int i,
4767 j,
4768 ntups;
4769
4770 if (dopt->no_publications || fout->remoteVersion < 150000)
4771 return;
4772
4773 query = createPQExpBuffer();
4774
4775 /* Collect all publication membership info. */
4777 "SELECT tableoid, oid, pnpubid, pnnspid "
4778 "FROM pg_catalog.pg_publication_namespace");
4779 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4780
4781 ntups = PQntuples(res);
4782
4783 i_tableoid = PQfnumber(res, "tableoid");
4784 i_oid = PQfnumber(res, "oid");
4785 i_pnpubid = PQfnumber(res, "pnpubid");
4786 i_pnnspid = PQfnumber(res, "pnnspid");
4787
4788 /* this allocation may be more than we need */
4789 pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4790 j = 0;
4791
4792 for (i = 0; i < ntups; i++)
4793 {
4794 Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4795 Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4796 PublicationInfo *pubinfo;
4797 NamespaceInfo *nspinfo;
4798
4799 /*
4800 * Ignore any entries for which we aren't interested in either the
4801 * publication or the rel.
4802 */
4803 pubinfo = findPublicationByOid(pnpubid);
4804 if (pubinfo == NULL)
4805 continue;
4806 nspinfo = findNamespaceByOid(pnnspid);
4807 if (nspinfo == NULL)
4808 continue;
4809
4810 /* OK, make a DumpableObject for this relationship */
4812 pubsinfo[j].dobj.catId.tableoid =
4813 atooid(PQgetvalue(res, i, i_tableoid));
4814 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4815 AssignDumpId(&pubsinfo[j].dobj);
4816 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4817 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4818 pubsinfo[j].publication = pubinfo;
4819 pubsinfo[j].pubschema = nspinfo;
4820
4821 /* Decide whether we want to dump it */
4822 selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4823
4824 j++;
4825 }
4826
4827 PQclear(res);
4828 destroyPQExpBuffer(query);
4829}
4830
4831/*
4832 * getPublicationTables
4833 * get information about publication membership for dumpable tables.
4834 */
4835void
4836getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4837{
4838 PQExpBuffer query;
4839 PGresult *res;
4840 PublicationRelInfo *pubrinfo;
4841 DumpOptions *dopt = fout->dopt;
4842 int i_tableoid;
4843 int i_oid;
4844 int i_prpubid;
4845 int i_prrelid;
4846 int i_prrelqual;
4847 int i_prattrs;
4848 int i,
4849 j,
4850 ntups;
4851
4852 if (dopt->no_publications || fout->remoteVersion < 100000)
4853 return;
4854
4855 query = createPQExpBuffer();
4856
4857 /* Collect all publication membership info. */
4858 if (fout->remoteVersion >= 150000)
4860 "SELECT tableoid, oid, prpubid, prrelid, "
4861 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4862 "(CASE\n"
4863 " WHEN pr.prattrs IS NOT NULL THEN\n"
4864 " (SELECT array_agg(attname)\n"
4865 " FROM\n"
4866 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4867 " pg_catalog.pg_attribute\n"
4868 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4869 " ELSE NULL END) prattrs "
4870 "FROM pg_catalog.pg_publication_rel pr");
4871 else
4873 "SELECT tableoid, oid, prpubid, prrelid, "
4874 "NULL AS prrelqual, NULL AS prattrs "
4875 "FROM pg_catalog.pg_publication_rel");
4876 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4877
4878 ntups = PQntuples(res);
4879
4880 i_tableoid = PQfnumber(res, "tableoid");
4881 i_oid = PQfnumber(res, "oid");
4882 i_prpubid = PQfnumber(res, "prpubid");
4883 i_prrelid = PQfnumber(res, "prrelid");
4884 i_prrelqual = PQfnumber(res, "prrelqual");
4885 i_prattrs = PQfnumber(res, "prattrs");
4886
4887 /* this allocation may be more than we need */
4888 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4889 j = 0;
4890
4891 for (i = 0; i < ntups; i++)
4892 {
4893 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4894 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4895 PublicationInfo *pubinfo;
4896 TableInfo *tbinfo;
4897
4898 /*
4899 * Ignore any entries for which we aren't interested in either the
4900 * publication or the rel.
4901 */
4902 pubinfo = findPublicationByOid(prpubid);
4903 if (pubinfo == NULL)
4904 continue;
4905 tbinfo = findTableByOid(prrelid);
4906 if (tbinfo == NULL)
4907 continue;
4908
4909 /* OK, make a DumpableObject for this relationship */
4910 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4911 pubrinfo[j].dobj.catId.tableoid =
4912 atooid(PQgetvalue(res, i, i_tableoid));
4913 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4914 AssignDumpId(&pubrinfo[j].dobj);
4915 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4916 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4917 pubrinfo[j].publication = pubinfo;
4918 pubrinfo[j].pubtable = tbinfo;
4919 if (PQgetisnull(res, i, i_prrelqual))
4920 pubrinfo[j].pubrelqual = NULL;
4921 else
4922 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4923
4924 if (!PQgetisnull(res, i, i_prattrs))
4925 {
4926 char **attnames;
4927 int nattnames;
4928 PQExpBuffer attribs;
4929
4930 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4931 &attnames, &nattnames))
4932 pg_fatal("could not parse %s array", "prattrs");
4933 attribs = createPQExpBuffer();
4934 for (int k = 0; k < nattnames; k++)
4935 {
4936 if (k > 0)
4937 appendPQExpBufferStr(attribs, ", ");
4938
4939 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4940 }
4941 pubrinfo[j].pubrattrs = attribs->data;
4942 free(attribs); /* but not attribs->data */
4943 free(attnames);
4944 }
4945 else
4946 pubrinfo[j].pubrattrs = NULL;
4947
4948 /* Decide whether we want to dump it */
4949 selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4950
4951 j++;
4952 }
4953
4954 PQclear(res);
4955 destroyPQExpBuffer(query);
4956}
4957
4958/*
4959 * dumpPublicationNamespace
4960 * dump the definition of the given publication schema mapping.
4961 */
4962static void
4964{
4965 DumpOptions *dopt = fout->dopt;
4966 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4967 PublicationInfo *pubinfo = pubsinfo->publication;
4968 PQExpBuffer query;
4969 char *tag;
4970
4971 /* Do nothing if not dumping schema */
4972 if (!dopt->dumpSchema)
4973 return;
4974
4975 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4976
4977 query = createPQExpBuffer();
4978
4979 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4980 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4981
4982 /*
4983 * There is no point in creating drop query as the drop is done by schema
4984 * drop.
4985 */
4986 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4987 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4988 ARCHIVE_OPTS(.tag = tag,
4989 .namespace = schemainfo->dobj.name,
4990 .owner = pubinfo->rolname,
4991 .description = "PUBLICATION TABLES IN SCHEMA",
4992 .section = SECTION_POST_DATA,
4993 .createStmt = query->data));
4994
4995 /* These objects can't currently have comments or seclabels */
4996
4997 free(tag);
4998 destroyPQExpBuffer(query);
4999}
5000
5001/*
5002 * dumpPublicationTable
5003 * dump the definition of the given publication table mapping
5004 */
5005static void
5007{
5008 DumpOptions *dopt = fout->dopt;
5009 PublicationInfo *pubinfo = pubrinfo->publication;
5010 TableInfo *tbinfo = pubrinfo->pubtable;
5011 PQExpBuffer query;
5012 char *tag;
5013
5014 /* Do nothing if not dumping schema */
5015 if (!dopt->dumpSchema)
5016 return;
5017
5018 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5019
5020 query = createPQExpBuffer();
5021
5022 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5023 fmtId(pubinfo->dobj.name));
5024 appendPQExpBuffer(query, " %s",
5025 fmtQualifiedDumpable(tbinfo));
5026
5027 if (pubrinfo->pubrattrs)
5028 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5029
5030 if (pubrinfo->pubrelqual)
5031 {
5032 /*
5033 * It's necessary to add parentheses around the expression because
5034 * pg_get_expr won't supply the parentheses for things like WHERE
5035 * TRUE.
5036 */
5037 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5038 }
5039 appendPQExpBufferStr(query, ";\n");
5040
5041 /*
5042 * There is no point in creating a drop query as the drop is done by table
5043 * drop. (If you think to change this, see also _printTocEntry().)
5044 * Although this object doesn't really have ownership as such, set the
5045 * owner field anyway to ensure that the command is run by the correct
5046 * role at restore time.
5047 */
5048 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5049 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5050 ARCHIVE_OPTS(.tag = tag,
5051 .namespace = tbinfo->dobj.namespace->dobj.name,
5052 .owner = pubinfo->rolname,
5053 .description = "PUBLICATION TABLE",
5054 .section = SECTION_POST_DATA,
5055 .createStmt = query->data));
5056
5057 /* These objects can't currently have comments or seclabels */
5058
5059 free(tag);
5060 destroyPQExpBuffer(query);
5061}
5062
5063/*
5064 * Is the currently connected user a superuser?
5065 */
5066static bool
5068{
5069 ArchiveHandle *AH = (ArchiveHandle *) fout;
5070 const char *val;
5071
5072 val = PQparameterStatus(AH->connection, "is_superuser");
5073
5074 if (val && strcmp(val, "on") == 0)
5075 return true;
5076
5077 return false;
5078}
5079
5080/*
5081 * Set the given value to restrict_nonsystem_relation_kind value. Since
5082 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5083 * the setting query is effective only where available.
5084 */
5085static void
5087{
5089 PGresult *res;
5090
5091 appendPQExpBuffer(query,
5092 "SELECT set_config(name, '%s', false) "
5093 "FROM pg_settings "
5094 "WHERE name = 'restrict_nonsystem_relation_kind'",
5095 value);
5096 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5097
5098 PQclear(res);
5099 destroyPQExpBuffer(query);
5100}
5101
5102/*
5103 * getSubscriptions
5104 * get information about subscriptions
5105 */
5106void
5108{
5109 DumpOptions *dopt = fout->dopt;
5110 PQExpBuffer query;
5111 PGresult *res;
5112 SubscriptionInfo *subinfo;
5113 int i_tableoid;
5114 int i_oid;
5115 int i_subname;
5116 int i_subowner;
5117 int i_subbinary;
5118 int i_substream;
5119 int i_subtwophasestate;
5120 int i_subdisableonerr;
5121 int i_subpasswordrequired;
5122 int i_subrunasowner;
5123 int i_subconninfo;
5124 int i_subslotname;
5125 int i_subsynccommit;
5126 int i_subpublications;
5127 int i_suborigin;
5128 int i_suboriginremotelsn;
5129 int i_subenabled;
5130 int i_subfailover;
5131 int i_subretaindeadtuples;
5132 int i_submaxretention;
5133 int i,
5134 ntups;
5135
5136 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5137 return;
5138
5139 if (!is_superuser(fout))
5140 {
5141 int n;
5142
5143 res = ExecuteSqlQuery(fout,
5144 "SELECT count(*) FROM pg_subscription "
5145 "WHERE subdbid = (SELECT oid FROM pg_database"
5146 " WHERE datname = current_database())",
5148 n = atoi(PQgetvalue(res, 0, 0));
5149 if (n > 0)
5150 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5151 PQclear(res);
5152 return;
5153 }
5154
5155 query = createPQExpBuffer();
5156
5157 /* Get the subscriptions in current database. */
5159 "SELECT s.tableoid, s.oid, s.subname,\n"
5160 " s.subowner,\n"
5161 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5162 " s.subpublications,\n");
5163
5164 if (fout->remoteVersion >= 140000)
5165 appendPQExpBufferStr(query, " s.subbinary,\n");
5166 else
5167 appendPQExpBufferStr(query, " false AS subbinary,\n");
5168
5169 if (fout->remoteVersion >= 140000)
5170 appendPQExpBufferStr(query, " s.substream,\n");
5171 else
5172 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5173
5174 if (fout->remoteVersion >= 150000)
5176 " s.subtwophasestate,\n"
5177 " s.subdisableonerr,\n");
5178 else
5179 appendPQExpBuffer(query,
5180 " '%c' AS subtwophasestate,\n"
5181 " false AS subdisableonerr,\n",
5182 LOGICALREP_TWOPHASE_STATE_DISABLED);
5183
5184 if (fout->remoteVersion >= 160000)
5186 " s.subpasswordrequired,\n"
5187 " s.subrunasowner,\n"
5188 " s.suborigin,\n");
5189 else
5190 appendPQExpBuffer(query,
5191 " 't' AS subpasswordrequired,\n"
5192 " 't' AS subrunasowner,\n"
5193 " '%s' AS suborigin,\n",
5194 LOGICALREP_ORIGIN_ANY);
5195
5196 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5197 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5198 " s.subenabled,\n");
5199 else
5200 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5201 " false AS subenabled,\n");
5202
5203 if (fout->remoteVersion >= 170000)
5205 " s.subfailover,\n");
5206 else
5208 " false AS subfailover,\n");
5209
5210 if (fout->remoteVersion >= 190000)
5212 " s.subretaindeadtuples,\n");
5213 else
5215 " false AS subretaindeadtuples,\n");
5216
5217 if (fout->remoteVersion >= 190000)
5219 " s.submaxretention\n");
5220 else
5221 appendPQExpBuffer(query,
5222 " 0 AS submaxretention\n");
5223
5225 "FROM pg_subscription s\n");
5226
5227 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5229 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5230 " ON o.external_id = 'pg_' || s.oid::text \n");
5231
5233 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5234 " WHERE datname = current_database())");
5235
5236 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5237
5238 ntups = PQntuples(res);
5239
5240 /*
5241 * Get subscription fields. We don't include subskiplsn in the dump as
5242 * after restoring the dump this value may no longer be relevant.
5243 */
5244 i_tableoid = PQfnumber(res, "tableoid");
5245 i_oid = PQfnumber(res, "oid");
5246 i_subname = PQfnumber(res, "subname");
5247 i_subowner = PQfnumber(res, "subowner");
5248 i_subenabled = PQfnumber(res, "subenabled");
5249 i_subbinary = PQfnumber(res, "subbinary");
5250 i_substream = PQfnumber(res, "substream");
5251 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5252 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5253 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5254 i_subrunasowner = PQfnumber(res, "subrunasowner");
5255 i_subfailover = PQfnumber(res, "subfailover");
5256 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5257 i_submaxretention = PQfnumber(res, "submaxretention");
5258 i_subconninfo = PQfnumber(res, "subconninfo");
5259 i_subslotname = PQfnumber(res, "subslotname");
5260 i_subsynccommit = PQfnumber(res, "subsynccommit");
5261 i_subpublications = PQfnumber(res, "subpublications");
5262 i_suborigin = PQfnumber(res, "suborigin");
5263 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5264
5265 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5266
5267 for (i = 0; i < ntups; i++)
5268 {
5269 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5270 subinfo[i].dobj.catId.tableoid =
5271 atooid(PQgetvalue(res, i, i_tableoid));
5272 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5273 AssignDumpId(&subinfo[i].dobj);
5274 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5275 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5276
5277 subinfo[i].subenabled =
5278 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5279 subinfo[i].subbinary =
5280 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5281 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5282 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5283 subinfo[i].subdisableonerr =
5284 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5285 subinfo[i].subpasswordrequired =
5286 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5287 subinfo[i].subrunasowner =
5288 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5289 subinfo[i].subfailover =
5290 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5291 subinfo[i].subretaindeadtuples =
5292 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5293 subinfo[i].submaxretention =
5294 atoi(PQgetvalue(res, i, i_submaxretention));
5295 subinfo[i].subconninfo =
5296 pg_strdup(PQgetvalue(res, i, i_subconninfo));
5297 if (PQgetisnull(res, i, i_subslotname))
5298 subinfo[i].subslotname = NULL;
5299 else
5300 subinfo[i].subslotname =
5301 pg_strdup(PQgetvalue(res, i, i_subslotname));
5302 subinfo[i].subsynccommit =
5303 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5304 subinfo[i].subpublications =
5305 pg_strdup(PQgetvalue(res, i, i_subpublications));
5306 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5307 if (PQgetisnull(res, i, i_suboriginremotelsn))
5308 subinfo[i].suboriginremotelsn = NULL;
5309 else
5310 subinfo[i].suboriginremotelsn =
5311 pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5312
5313 /* Decide whether we want to dump it */
5314 selectDumpableObject(&(subinfo[i].dobj), fout);
5315 }
5316 PQclear(res);
5317
5318 destroyPQExpBuffer(query);
5319}
5320
5321/*
5322 * getSubscriptionRelations
5323 * Get information about subscription membership for dumpable relations. This
5324 * will be used only in binary-upgrade mode for PG17 or later versions.
5325 */
5326void
5328{
5329 DumpOptions *dopt = fout->dopt;
5330 SubscriptionInfo *subinfo = NULL;
5331 SubRelInfo *subrinfo;
5332 PGresult *res;
5333 int i_srsubid;
5334 int i_srrelid;
5335 int i_srsubstate;
5336 int i_srsublsn;
5337 int ntups;
5338 Oid last_srsubid = InvalidOid;
5339
5340 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5341 fout->remoteVersion < 170000)
5342 return;
5343
5344 res = ExecuteSqlQuery(fout,
5345 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5346 "FROM pg_catalog.pg_subscription_rel "
5347 "ORDER BY srsubid",
5349 ntups = PQntuples(res);
5350 if (ntups == 0)
5351 goto cleanup;
5352
5353 /* Get pg_subscription_rel attributes */
5354 i_srsubid = PQfnumber(res, "srsubid");
5355 i_srrelid = PQfnumber(res, "srrelid");
5356 i_srsubstate = PQfnumber(res, "srsubstate");
5357 i_srsublsn = PQfnumber(res, "srsublsn");
5358
5359 subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5360 for (int i = 0; i < ntups; i++)
5361 {
5362 Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5363 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5364 TableInfo *tblinfo;
5365
5366 /*
5367 * If we switched to a new subscription, check if the subscription
5368 * exists.
5369 */
5370 if (cur_srsubid != last_srsubid)
5371 {
5372 subinfo = findSubscriptionByOid(cur_srsubid);
5373 if (subinfo == NULL)
5374 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5375
5376 last_srsubid = cur_srsubid;
5377 }
5378
5379 tblinfo = findTableByOid(relid);
5380 if (tblinfo == NULL)
5381 pg_fatal("failed sanity check, relation with OID %u not found",
5382 relid);
5383
5384 /* OK, make a DumpableObject for this relationship */
5385 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5386 subrinfo[i].dobj.catId.tableoid = relid;
5387 subrinfo[i].dobj.catId.oid = cur_srsubid;
5388 AssignDumpId(&subrinfo[i].dobj);
5389 subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5390 subrinfo[i].tblinfo = tblinfo;
5391 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5392 if (PQgetisnull(res, i, i_srsublsn))
5393 subrinfo[i].srsublsn = NULL;
5394 else
5395 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5396
5397 subrinfo[i].subinfo = subinfo;
5398
5399 /* Decide whether we want to dump it */
5400 selectDumpableObject(&(subrinfo[i].dobj), fout);
5401 }
5402
5403cleanup:
5404 PQclear(res);
5405}
5406
5407/*
5408 * dumpSubscriptionTable
5409 * Dump the definition of the given subscription table mapping. This will be
5410 * used only in binary-upgrade mode for PG17 or later versions.
5411 */
5412static void
5414{
5415 DumpOptions *dopt = fout->dopt;
5416 SubscriptionInfo *subinfo = subrinfo->subinfo;
5417 PQExpBuffer query;
5418 char *tag;
5419
5420 /* Do nothing if not dumping schema */
5421 if (!dopt->dumpSchema)
5422 return;
5423
5424 Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5425
5426 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5427
5428 query = createPQExpBuffer();
5429
5430 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5431 {
5432 /*
5433 * binary_upgrade_add_sub_rel_state will add the subscription relation
5434 * to pg_subscription_rel table. This will be used only in
5435 * binary-upgrade mode.
5436 */
5438 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5440 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5441 appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5442 appendPQExpBuffer(query,
5443 ", %u, '%c'",
5444 subrinfo->tblinfo->dobj.catId.oid,
5445 subrinfo->srsubstate);
5446
5447 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5448 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5449 else
5450 appendPQExpBufferStr(query, ", NULL");
5451
5452 appendPQExpBufferStr(query, ");\n");
5453 }
5454
5455 /*
5456 * There is no point in creating a drop query as the drop is done by table
5457 * drop. (If you think to change this, see also _printTocEntry().)
5458 * Although this object doesn't really have ownership as such, set the
5459 * owner field anyway to ensure that the command is run by the correct
5460 * role at restore time.
5461 */
5462 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5463 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5464 ARCHIVE_OPTS(.tag = tag,
5465 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5466 .owner = subinfo->rolname,
5467 .description = "SUBSCRIPTION TABLE",
5468 .section = SECTION_POST_DATA,
5469 .createStmt = query->data));
5470
5471 /* These objects can't currently have comments or seclabels */
5472
5473 free(tag);
5474 destroyPQExpBuffer(query);
5475}
5476
5477/*
5478 * dumpSubscription
5479 * dump the definition of the given subscription
5480 */
5481static void
5483{
5484 DumpOptions *dopt = fout->dopt;
5485 PQExpBuffer delq;
5486 PQExpBuffer query;
5487 PQExpBuffer publications;
5488 char *qsubname;
5489 char **pubnames = NULL;
5490 int npubnames = 0;
5491 int i;
5492
5493 /* Do nothing if not dumping schema */
5494 if (!dopt->dumpSchema)
5495 return;
5496
5497 delq = createPQExpBuffer();
5498 query = createPQExpBuffer();
5499
5500 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5501
5502 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5503 qsubname);
5504
5505 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5506 qsubname);
5507 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5508
5509 /* Build list of quoted publications and append them to query. */
5510 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5511 pg_fatal("could not parse %s array", "subpublications");
5512
5513 publications = createPQExpBuffer();
5514 for (i = 0; i < npubnames; i++)
5515 {
5516 if (i > 0)
5517 appendPQExpBufferStr(publications, ", ");
5518
5519 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5520 }
5521
5522 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5523 if (subinfo->subslotname)
5524 appendStringLiteralAH(query, subinfo->subslotname, fout);
5525 else
5526 appendPQExpBufferStr(query, "NONE");
5527
5528 if (subinfo->subbinary)
5529 appendPQExpBufferStr(query, ", binary = true");
5530
5531 if (subinfo->substream == LOGICALREP_STREAM_ON)
5532 appendPQExpBufferStr(query, ", streaming = on");
5533 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5534 appendPQExpBufferStr(query, ", streaming = parallel");
5535 else
5536 appendPQExpBufferStr(query, ", streaming = off");
5537
5538 if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5539 appendPQExpBufferStr(query, ", two_phase = on");
5540
5541 if (subinfo->subdisableonerr)
5542 appendPQExpBufferStr(query, ", disable_on_error = true");
5543
5544 if (!subinfo->subpasswordrequired)
5545 appendPQExpBufferStr(query, ", password_required = false");
5546
5547 if (subinfo->subrunasowner)
5548 appendPQExpBufferStr(query, ", run_as_owner = true");
5549
5550 if (subinfo->subfailover)
5551 appendPQExpBufferStr(query, ", failover = true");
5552
5553 if (subinfo->subretaindeadtuples)
5554 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5555
5556 if (subinfo->submaxretention)
5557 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5558
5559 if (strcmp(subinfo->subsynccommit, "off") != 0)
5560 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5561
5562 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5563 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5564
5565 appendPQExpBufferStr(query, ");\n");
5566
5567 /*
5568 * In binary-upgrade mode, we allow the replication to continue after the
5569 * upgrade.
5570 */
5571 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5572 {
5573 if (subinfo->suboriginremotelsn)
5574 {
5575 /*
5576 * Preserve the remote_lsn for the subscriber's replication
5577 * origin. This value is required to start the replication from
5578 * the position before the upgrade. This value will be stale if
5579 * the publisher gets upgraded before the subscriber node.
5580 * However, this shouldn't be a problem as the upgrade of the
5581 * publisher ensures that all the transactions were replicated
5582 * before upgrading it.
5583 */
5585 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5587 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5588 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5589 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5590 }
5591
5592 if (subinfo->subenabled)
5593 {
5594 /*
5595 * Enable the subscription to allow the replication to continue
5596 * after the upgrade.
5597 */
5599 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5600 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5601 }
5602 }
5603
5604 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5605 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5606 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5607 .owner = subinfo->rolname,
5608 .description = "SUBSCRIPTION",
5609 .section = SECTION_POST_DATA,
5610 .createStmt = query->data,
5611 .dropStmt = delq->data));
5612
5613 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5614 dumpComment(fout, "SUBSCRIPTION", qsubname,
5615 NULL, subinfo->rolname,
5616 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5617
5618 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5619 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5620 NULL, subinfo->rolname,
5621 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5622
5623 destroyPQExpBuffer(publications);
5624 free(pubnames);
5625
5626 destroyPQExpBuffer(delq);
5627 destroyPQExpBuffer(query);
5628 free(qsubname);
5629}
5630
5631/*
5632 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5633 * the object needs.
5634 */
5635static void
5637 PQExpBuffer create,
5638 const DumpableObject *dobj,
5639 const char *catalog,
5640 const char *keyword,
5641 const char *objname)
5642{
5643 if (dobj->depends_on_ext)
5644 {
5645 char *nm;
5646 PGresult *res;
5647 PQExpBuffer query;
5648 int ntups;
5649 int i_extname;
5650 int i;
5651
5652 /* dodge fmtId() non-reentrancy */
5653 nm = pg_strdup(objname);
5654
5655 query = createPQExpBuffer();
5656 appendPQExpBuffer(query,
5657 "SELECT e.extname "
5658 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5659 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5660 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5661 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5662 catalog,
5663 dobj->catId.oid);
5664 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5665 ntups = PQntuples(res);
5666 i_extname = PQfnumber(res, "extname");
5667 for (i = 0; i < ntups; i++)
5668 {
5669 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5670 keyword, nm,
5671 fmtId(PQgetvalue(res, i, i_extname)));
5672 }
5673
5674 PQclear(res);
5675 destroyPQExpBuffer(query);
5676 pg_free(nm);
5677 }
5678}
5679
5680static Oid
5682{
5683 /*
5684 * If the old version didn't assign an array type, but the new version
5685 * does, we must select an unused type OID to assign. This currently only
5686 * happens for domains, when upgrading pre-v11 to v11 and up.
5687 *
5688 * Note: local state here is kind of ugly, but we must have some, since we
5689 * mustn't choose the same unused OID more than once.
5690 */
5691 static Oid next_possible_free_oid = FirstNormalObjectId;
5692 PGresult *res;
5693 bool is_dup;
5694
5695 do
5696 {
5697 ++next_possible_free_oid;
5698 printfPQExpBuffer(upgrade_query,
5699 "SELECT EXISTS(SELECT 1 "
5700 "FROM pg_catalog.pg_type "
5701 "WHERE oid = '%u'::pg_catalog.oid);",
5702 next_possible_free_oid);
5703 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5704 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5705 PQclear(res);
5706 } while (is_dup);
5707
5708 return next_possible_free_oid;
5709}
5710
5711static void
5713 PQExpBuffer upgrade_buffer,
5714 Oid pg_type_oid,
5715 bool force_array_type,
5716 bool include_multirange_type)
5717{
5718 PQExpBuffer upgrade_query = createPQExpBuffer();
5719 PGresult *res;
5720 Oid pg_type_array_oid;
5721 Oid pg_type_multirange_oid;
5722 Oid pg_type_multirange_array_oid;
5723 TypeInfo *tinfo;
5724
5725 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5726 appendPQExpBuffer(upgrade_buffer,
5727 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5728 pg_type_oid);
5729
5730 tinfo = findTypeByOid(pg_type_oid);
5731 if (tinfo)
5732 pg_type_array_oid = tinfo->typarray;
5733 else
5734 pg_type_array_oid = InvalidOid;
5735
5736 if (!OidIsValid(pg_type_array_oid) && force_array_type)
5737 pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5738
5739 if (OidIsValid(pg_type_array_oid))
5740 {
5741 appendPQExpBufferStr(upgrade_buffer,
5742 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5743 appendPQExpBuffer(upgrade_buffer,
5744 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5745 pg_type_array_oid);
5746 }
5747
5748 /*
5749 * Pre-set the multirange type oid and its own array type oid.
5750 */
5751 if (include_multirange_type)
5752 {
5753 if (fout->remoteVersion >= 140000)
5754 {
5755 printfPQExpBuffer(upgrade_query,
5756 "SELECT t.oid, t.typarray "
5757 "FROM pg_catalog.pg_type t "
5758 "JOIN pg_catalog.pg_range r "
5759 "ON t.oid = r.rngmultitypid "
5760 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5761 pg_type_oid);
5762
5763 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5764
5765 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5766 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5767
5768 PQclear(res);
5769 }
5770 else
5771 {
5772 pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5773 pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5774 }
5775
5776 appendPQExpBufferStr(upgrade_buffer,
5777 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5778 appendPQExpBuffer(upgrade_buffer,
5779 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5780 pg_type_multirange_oid);
5781 appendPQExpBufferStr(upgrade_buffer,
5782 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5783 appendPQExpBuffer(upgrade_buffer,
5784 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5785 pg_type_multirange_array_oid);
5786 }
5787
5788 destroyPQExpBuffer(upgrade_query);
5789}
5790
5791static void
5793 PQExpBuffer upgrade_buffer,
5794 const TableInfo *tbinfo)
5795{
5796 Oid pg_type_oid = tbinfo->reltype;
5797
5798 if (OidIsValid(pg_type_oid))
5799 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5800 pg_type_oid, false, false);
5801}
5802
5803/*
5804 * bsearch() comparator for BinaryUpgradeClassOidItem
5805 */
5806static int
5807BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5808{
5811
5812 return pg_cmp_u32(v1.oid, v2.oid);
5813}
5814
5815/*
5816 * collectBinaryUpgradeClassOids
5817 *
5818 * Construct a table of pg_class information required for
5819 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5820 * lookup.
5821 */
5822static void
5824{
5825 PGresult *res;
5826 const char *query;
5827
5828 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5829 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5830 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5831 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5832 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5833 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5834 "ORDER BY c.oid;";
5835
5836 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5837
5841
5842 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5843 {
5851 }
5852
5853 PQclear(res);
5854}
5855
5856static void
5858 PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5859{
5862
5864
5865 /*
5866 * Preserve the OID and relfilenumber of the table, table's index, table's
5867 * toast table and toast table's index if any.
5868 *
5869 * One complexity is that the current table definition might not require
5870 * the creation of a TOAST table, but the old database might have a TOAST
5871 * table that was created earlier, before some wide columns were dropped.
5872 * By setting the TOAST oid we force creation of the TOAST heap and index
5873 * by the new backend, so we can copy the files during binary upgrade
5874 * without worrying about this case.
5875 */
5876 key.oid = pg_class_oid;
5880
5881 appendPQExpBufferStr(upgrade_buffer,
5882 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5883
5884 if (entry->relkind != RELKIND_INDEX &&
5885 entry->relkind != RELKIND_PARTITIONED_INDEX)
5886 {
5887 appendPQExpBuffer(upgrade_buffer,
5888 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5889 pg_class_oid);
5890
5891 /*
5892 * Not every relation has storage. Also, in a pre-v12 database,
5893 * partitioned tables have a relfilenumber, which should not be
5894 * preserved when upgrading.
5895 */
5896 if (RelFileNumberIsValid(entry->relfilenumber) &&
5897 entry->relkind != RELKIND_PARTITIONED_TABLE)
5898 appendPQExpBuffer(upgrade_buffer,
5899 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5900 entry->relfilenumber);
5901
5902 /*
5903 * In a pre-v12 database, partitioned tables might be marked as having
5904 * toast tables, but we should ignore them if so.
5905 */
5906 if (OidIsValid(entry->toast_oid) &&
5907 entry->relkind != RELKIND_PARTITIONED_TABLE)
5908 {
5909 appendPQExpBuffer(upgrade_buffer,
5910 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5911 entry->toast_oid);
5912 appendPQExpBuffer(upgrade_buffer,
5913 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5914 entry->toast_relfilenumber);
5915
5916 /* every toast table has an index */
5917 appendPQExpBuffer(upgrade_buffer,
5918 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5919 entry->toast_index_oid);
5920 appendPQExpBuffer(upgrade_buffer,
5921 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5923 }
5924 }
5925 else
5926 {
5927 /* Preserve the OID and relfilenumber of the index */
5928 appendPQExpBuffer(upgrade_buffer,
5929 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5930 pg_class_oid);
5931 appendPQExpBuffer(upgrade_buffer,
5932 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5933 entry->relfilenumber);
5934 }
5935
5936 appendPQExpBufferChar(upgrade_buffer, '\n');
5937}
5938
5939/*
5940 * If the DumpableObject is a member of an extension, add a suitable
5941 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5942 *
5943 * For somewhat historical reasons, objname should already be quoted,
5944 * but not objnamespace (if any).
5945 */
5946static void
5948 const DumpableObject *dobj,
5949 const char *objtype,
5950 const char *objname,
5951 const char *objnamespace)
5952{
5953 DumpableObject *extobj = NULL;
5954 int i;
5955
5956 if (!dobj->ext_member)
5957 return;
5958
5959 /*
5960 * Find the parent extension. We could avoid this search if we wanted to
5961 * add a link field to DumpableObject, but the space costs of that would
5962 * be considerable. We assume that member objects could only have a
5963 * direct dependency on their own extension, not any others.
5964 */
5965 for (i = 0; i < dobj->nDeps; i++)
5966 {
5967 extobj = findObjectByDumpId(dobj->dependencies[i]);
5968 if (extobj && extobj->objType == DO_EXTENSION)
5969 break;
5970 extobj = NULL;
5971 }
5972 if (extobj == NULL)
5973 pg_fatal("could not find parent extension for %s %s",
5974 objtype, objname);
5975
5976 appendPQExpBufferStr(upgrade_buffer,
5977 "\n-- For binary upgrade, handle extension membership the hard way\n");
5978 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5979 fmtId(extobj->name),
5980 objtype);
5981 if (objnamespace && *objnamespace)
5982 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5983 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5984}
5985
5986/*
5987 * getNamespaces:
5988 * get information about all namespaces in the system catalogs
5989 */
5990void
5992{
5993 PGresult *res;
5994 int ntups;
5995 int i;
5996 PQExpBuffer query;
5997 NamespaceInfo *nsinfo;
5998 int i_tableoid;
5999 int i_oid;
6000 int i_nspname;
6001 int i_nspowner;
6002 int i_nspacl;
6003 int i_acldefault;
6004
6005 query = createPQExpBuffer();
6006
6007 /*
6008 * we fetch all namespaces including system ones, so that every object we
6009 * read in can be linked to a containing namespace.
6010 */
6011 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6012 "n.nspowner, "
6013 "n.nspacl, "
6014 "acldefault('n', n.nspowner) AS acldefault "
6015 "FROM pg_namespace n");
6016
6017 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6018
6019 ntups = PQntuples(res);
6020
6021 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6022
6023 i_tableoid = PQfnumber(res, "tableoid");
6024 i_oid = PQfnumber(res, "oid");
6025 i_nspname = PQfnumber(res, "nspname");
6026 i_nspowner = PQfnumber(res, "nspowner");
6027 i_nspacl = PQfnumber(res, "nspacl");
6028 i_acldefault = PQfnumber(res, "acldefault");
6029
6030 for (i = 0; i < ntups; i++)
6031 {
6032 const char *nspowner;
6033
6034 nsinfo[i].dobj.objType = DO_NAMESPACE;
6035 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6036 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6037 AssignDumpId(&nsinfo[i].dobj);
6038 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6039 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6040 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6041 nsinfo[i].dacl.privtype = 0;
6042 nsinfo[i].dacl.initprivs = NULL;
6043 nspowner = PQgetvalue(res, i, i_nspowner);
6044 nsinfo[i].nspowner = atooid(nspowner);
6045 nsinfo[i].rolname = getRoleName(nspowner);
6046
6047 /* Decide whether to dump this namespace */
6048 selectDumpableNamespace(&nsinfo[i], fout);
6049
6050 /* Mark whether namespace has an ACL */
6051 if (!PQgetisnull(res, i, i_nspacl))
6053
6054 /*
6055 * We ignore any pg_init_privs.initprivs entry for the public schema
6056 * and assume a predetermined default, for several reasons. First,
6057 * dropping and recreating the schema removes its pg_init_privs entry,
6058 * but an empty destination database starts with this ACL nonetheless.
6059 * Second, we support dump/reload of public schema ownership changes.
6060 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6061 * initprivs continues to reflect the initial owner. Hence,
6062 * synthesize the value that nspacl will have after the restore's
6063 * ALTER SCHEMA OWNER. Third, this makes the destination database
6064 * match the source's ACL, even if the latter was an initdb-default
6065 * ACL, which changed in v15. An upgrade pulls in changes to most
6066 * system object ACLs that the DBA had not customized. We've made the
6067 * public schema depart from that, because changing its ACL so easily
6068 * breaks applications.
6069 */
6070 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6071 {
6072 PQExpBuffer aclarray = createPQExpBuffer();
6073 PQExpBuffer aclitem = createPQExpBuffer();
6074
6075 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6076 appendPQExpBufferChar(aclarray, '{');
6077 quoteAclUserName(aclitem, nsinfo[i].rolname);
6078 appendPQExpBufferStr(aclitem, "=UC/");
6079 quoteAclUserName(aclitem, nsinfo[i].rolname);
6080 appendPGArray(aclarray, aclitem->data);
6081 resetPQExpBuffer(aclitem);
6082 appendPQExpBufferStr(aclitem, "=U/");
6083 quoteAclUserName(aclitem, nsinfo[i].rolname);
6084 appendPGArray(aclarray, aclitem->data);
6085 appendPQExpBufferChar(aclarray, '}');
6086
6087 nsinfo[i].dacl.privtype = 'i';
6088 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6090
6091 destroyPQExpBuffer(aclarray);
6092 destroyPQExpBuffer(aclitem);
6093 }
6094 }
6095
6096 PQclear(res);
6097 destroyPQExpBuffer(query);
6098}
6099
6100/*
6101 * findNamespace:
6102 * given a namespace OID, look up the info read by getNamespaces
6103 */
6104static NamespaceInfo *
6106{
6107 NamespaceInfo *nsinfo;
6108
6109 nsinfo = findNamespaceByOid(nsoid);
6110 if (nsinfo == NULL)
6111 pg_fatal("schema with OID %u does not exist", nsoid);
6112 return nsinfo;
6113}
6114
6115/*
6116 * getExtensions:
6117 * read all extensions in the system catalogs and return them in the
6118 * ExtensionInfo* structure
6119 *
6120 * numExtensions is set to the number of extensions read in
6121 */
6123getExtensions(Archive *fout, int *numExtensions)
6124{
6125 DumpOptions *dopt = fout->dopt;
6126 PGresult *res;
6127 int ntups;
6128 int i;
6129 PQExpBuffer query;
6130 ExtensionInfo *extinfo = NULL;
6131 int i_tableoid;
6132 int i_oid;
6133 int i_extname;
6134 int i_nspname;
6135 int i_extrelocatable;
6136 int i_extversion;
6137 int i_extconfig;
6138 int i_extcondition;
6139
6140 query = createPQExpBuffer();
6141
6142 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6143 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6144 "FROM pg_extension x "
6145 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6146
6147 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6148
6149 ntups = PQntuples(res);
6150 if (ntups == 0)
6151 goto cleanup;
6152
6153 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6154
6155 i_tableoid = PQfnumber(res, "tableoid");
6156 i_oid = PQfnumber(res, "oid");
6157 i_extname = PQfnumber(res, "extname");
6158 i_nspname = PQfnumber(res, "nspname");
6159 i_extrelocatable = PQfnumber(res, "extrelocatable");
6160 i_extversion = PQfnumber(res, "extversion");
6161 i_extconfig = PQfnumber(res, "extconfig");
6162 i_extcondition = PQfnumber(res, "extcondition");
6163
6164 for (i = 0; i < ntups; i++)
6165 {
6166 extinfo[i].dobj.objType = DO_EXTENSION;
6167 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6168 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6169 AssignDumpId(&extinfo[i].dobj);
6170 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6171 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6172 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6173 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6174 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6175 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6176
6177 /* Decide whether we want to dump it */
6178 selectDumpableExtension(&(extinfo[i]), dopt);
6179 }
6180
6181cleanup:
6182 PQclear(res);
6183 destroyPQExpBuffer(query);
6184
6185 *numExtensions = ntups;
6186
6187 return extinfo;
6188}
6189
6190/*
6191 * getTypes:
6192 * get information about all types in the system catalogs
6193 *
6194 * NB: this must run after getFuncs() because we assume we can do
6195 * findFuncByOid().
6196 */
6197void
6199{
6200 PGresult *res;
6201 int ntups;
6202 int i;
6204 TypeInfo *tyinfo;
6205 ShellTypeInfo *stinfo;
6206 int i_tableoid;
6207 int i_oid;
6208 int i_typname;
6209 int i_typnamespace;
6210 int i_typacl;
6211 int i_acldefault;
6212 int i_typowner;
6213 int i_typelem;
6214 int i_typrelid;
6215 int i_typrelkind;
6216 int i_typtype;
6217 int i_typisdefined;
6218 int i_isarray;
6219 int i_typarray;
6220
6221 /*
6222 * we include even the built-in types because those may be used as array
6223 * elements by user-defined types
6224 *
6225 * we filter out the built-in types when we dump out the types
6226 *
6227 * same approach for undefined (shell) types and array types
6228 *
6229 * Note: as of 8.3 we can reliably detect whether a type is an
6230 * auto-generated array type by checking the element type's typarray.
6231 * (Before that the test is capable of generating false positives.) We
6232 * still check for name beginning with '_', though, so as to avoid the
6233 * cost of the subselect probe for all standard types. This would have to
6234 * be revisited if the backend ever allows renaming of array types.
6235 */
6236 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6237 "typnamespace, typacl, "
6238 "acldefault('T', typowner) AS acldefault, "
6239 "typowner, "
6240 "typelem, typrelid, typarray, "
6241 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6242 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6243 "typtype, typisdefined, "
6244 "typname[0] = '_' AND typelem != 0 AND "
6245 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6246 "FROM pg_type");
6247
6248 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6249
6250 ntups = PQntuples(res);
6251
6252 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6253
6254 i_tableoid = PQfnumber(res, "tableoid");
6255 i_oid = PQfnumber(res, "oid");
6256 i_typname = PQfnumber(res, "typname");
6257 i_typnamespace = PQfnumber(res, "typnamespace");
6258 i_typacl = PQfnumber(res, "typacl");
6259 i_acldefault = PQfnumber(res, "acldefault");
6260 i_typowner = PQfnumber(res, "typowner");
6261 i_typelem = PQfnumber(res, "typelem");
6262 i_typrelid = PQfnumber(res, "typrelid");
6263 i_typrelkind = PQfnumber(res, "typrelkind");
6264 i_typtype = PQfnumber(res, "typtype");
6265 i_typisdefined = PQfnumber(res, "typisdefined");
6266 i_isarray = PQfnumber(res, "isarray");
6267 i_typarray = PQfnumber(res, "typarray");
6268
6269 for (i = 0; i < ntups; i++)
6270 {
6271 tyinfo[i].dobj.objType = DO_TYPE;
6272 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6273 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6274 AssignDumpId(&tyinfo[i].dobj);
6275 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6276 tyinfo[i].dobj.namespace =
6277 findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6278 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6279 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6280 tyinfo[i].dacl.privtype = 0;
6281 tyinfo[i].dacl.initprivs = NULL;
6282 tyinfo[i].ftypname = NULL; /* may get filled later */
6283 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6284 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6285 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6286 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6287 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6288 tyinfo[i].shellType = NULL;
6289
6290 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6291 tyinfo[i].isDefined = true;
6292 else
6293 tyinfo[i].isDefined = false;
6294
6295 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6296 tyinfo[i].isArray = true;
6297 else
6298 tyinfo[i].isArray = false;
6299
6300 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6301
6302 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6303 tyinfo[i].isMultirange = true;
6304 else
6305 tyinfo[i].isMultirange = false;
6306
6307 /* Decide whether we want to dump it */
6308 selectDumpableType(&tyinfo[i], fout);
6309
6310 /* Mark whether type has an ACL */
6311 if (!PQgetisnull(res, i, i_typacl))
6313
6314 /*
6315 * If it's a domain, fetch info about its constraints, if any
6316 */
6317 tyinfo[i].nDomChecks = 0;
6318 tyinfo[i].domChecks = NULL;
6319 tyinfo[i].notnull = NULL;
6320 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6321 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6322 getDomainConstraints(fout, &(tyinfo[i]));
6323
6324 /*
6325 * If it's a base type, make a DumpableObject representing a shell
6326 * definition of the type. We will need to dump that ahead of the I/O
6327 * functions for the type. Similarly, range types need a shell
6328 * definition in case they have a canonicalize function.
6329 *
6330 * Note: the shell type doesn't have a catId. You might think it
6331 * should copy the base type's catId, but then it might capture the
6332 * pg_depend entries for the type, which we don't want.
6333 */
6334 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6335 (tyinfo[i].typtype == TYPTYPE_BASE ||
6336 tyinfo[i].typtype == TYPTYPE_RANGE))
6337 {
6338 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6339 stinfo->dobj.objType = DO_SHELL_TYPE;
6340 stinfo->dobj.catId = nilCatalogId;
6341 AssignDumpId(&stinfo->dobj);
6342 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6343 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6344 stinfo->baseType = &(tyinfo[i]);
6345 tyinfo[i].shellType = stinfo;
6346
6347 /*
6348 * Initially mark the shell type as not to be dumped. We'll only
6349 * dump it if the I/O or canonicalize functions need to be dumped;
6350 * this is taken care of while sorting dependencies.
6351 */
6352 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6353 }
6354 }
6355
6356 PQclear(res);
6357
6358 destroyPQExpBuffer(query);
6359}
6360
6361/*
6362 * getOperators:
6363 * get information about all operators in the system catalogs
6364 */
6365void
6367{
6368 PGresult *res;
6369 int ntups;
6370 int i;
6372 OprInfo *oprinfo;
6373 int i_tableoid;
6374 int i_oid;
6375 int i_oprname;
6376 int i_oprnamespace;
6377 int i_oprowner;
6378 int i_oprkind;
6379 int i_oprleft;
6380 int i_oprright;
6381 int i_oprcode;
6382
6383 /*
6384 * find all operators, including builtin operators; we filter out
6385 * system-defined operators at dump-out time.
6386 */
6387
6388 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6389 "oprnamespace, "
6390 "oprowner, "
6391 "oprkind, "
6392 "oprleft, "
6393 "oprright, "
6394 "oprcode::oid AS oprcode "
6395 "FROM pg_operator");
6396
6397 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6398
6399 ntups = PQntuples(res);
6400
6401 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6402
6403 i_tableoid = PQfnumber(res, "tableoid");
6404 i_oid = PQfnumber(res, "oid");
6405 i_oprname = PQfnumber(res, "oprname");
6406 i_oprnamespace = PQfnumber(res, "oprnamespace");
6407 i_oprowner = PQfnumber(res, "oprowner");
6408 i_oprkind = PQfnumber(res, "oprkind");
6409 i_oprleft = PQfnumber(res, "oprleft");
6410 i_oprright = PQfnumber(res, "oprright");
6411 i_oprcode = PQfnumber(res, "oprcode");
6412
6413 for (i = 0; i < ntups; i++)
6414 {
6415 oprinfo[i].dobj.objType = DO_OPERATOR;
6416 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6417 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6418 AssignDumpId(&oprinfo[i].dobj);
6419 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6420 oprinfo[i].dobj.namespace =
6421 findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6422 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6423 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6424 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6425 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6426 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6427
6428 /* Decide whether we want to dump it */
6429 selectDumpableObject(&(oprinfo[i].dobj), fout);
6430 }
6431
6432 PQclear(res);
6433
6434 destroyPQExpBuffer(query);
6435}
6436
6437/*
6438 * getCollations:
6439 * get information about all collations in the system catalogs
6440 */
6441void
6443{
6444 PGresult *res;
6445 int ntups;
6446 int i;
6447 PQExpBuffer query;
6448 CollInfo *collinfo;
6449 int i_tableoid;
6450 int i_oid;
6451 int i_collname;
6452 int i_collnamespace;
6453 int i_collowner;
6454 int i_collencoding;
6455
6456 query = createPQExpBuffer();
6457
6458 /*
6459 * find all collations, including builtin collations; we filter out
6460 * system-defined collations at dump-out time.
6461 */
6462
6463 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6464 "collnamespace, "
6465 "collowner, "
6466 "collencoding "
6467 "FROM pg_collation");
6468
6469 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6470
6471 ntups = PQntuples(res);
6472
6473 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6474
6475 i_tableoid = PQfnumber(res, "tableoid");
6476 i_oid = PQfnumber(res, "oid");
6477 i_collname = PQfnumber(res, "collname");
6478 i_collnamespace = PQfnumber(res, "collnamespace");
6479 i_collowner = PQfnumber(res, "collowner");
6480 i_collencoding = PQfnumber(res, "collencoding");
6481
6482 for (i = 0; i < ntups; i++)
6483 {
6484 collinfo[i].dobj.objType = DO_COLLATION;
6485 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6486 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6487 AssignDumpId(&collinfo[i].dobj);
6488 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6489 collinfo[i].dobj.namespace =
6490 findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6491 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6492 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6493
6494 /* Decide whether we want to dump it */
6495 selectDumpableObject(&(collinfo[i].dobj), fout);
6496 }
6497
6498 PQclear(res);
6499
6500 destroyPQExpBuffer(query);
6501}
6502
6503/*
6504 * getConversions:
6505 * get information about all conversions in the system catalogs
6506 */
6507void
6509{
6510 PGresult *res;
6511 int ntups;
6512 int i;
6513 PQExpBuffer query;
6514 ConvInfo *convinfo;
6515 int i_tableoid;
6516 int i_oid;
6517 int i_conname;
6518 int i_connamespace;
6519 int i_conowner;
6520
6521 query = createPQExpBuffer();
6522
6523 /*
6524 * find all conversions, including builtin conversions; we filter out
6525 * system-defined conversions at dump-out time.
6526 */
6527
6528 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6529 "connamespace, "
6530 "conowner "
6531 "FROM pg_conversion");
6532
6533 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6534
6535 ntups = PQntuples(res);
6536
6537 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6538
6539 i_tableoid = PQfnumber(res, "tableoid");
6540 i_oid = PQfnumber(res, "oid");
6541 i_conname = PQfnumber(res, "conname");
6542 i_connamespace = PQfnumber(res, "connamespace");
6543 i_conowner = PQfnumber(res, "conowner");
6544
6545 for (i = 0; i < ntups; i++)
6546 {
6547 convinfo[i].dobj.objType = DO_CONVERSION;
6548 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6549 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6550 AssignDumpId(&convinfo[i].dobj);
6551 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6552 convinfo[i].dobj.namespace =
6553 findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6554 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6555
6556 /* Decide whether we want to dump it */
6557 selectDumpableObject(&(convinfo[i].dobj), fout);
6558 }
6559
6560 PQclear(res);
6561
6562 destroyPQExpBuffer(query);
6563}
6564
6565/*
6566 * getAccessMethods:
6567 * get information about all user-defined access methods
6568 */
6569void
6571{
6572 PGresult *res;
6573 int ntups;
6574 int i;
6575 PQExpBuffer query;
6576 AccessMethodInfo *aminfo;
6577 int i_tableoid;
6578 int i_oid;
6579 int i_amname;
6580 int i_amhandler;
6581 int i_amtype;
6582
6583 query = createPQExpBuffer();
6584
6585 /*
6586 * Select all access methods from pg_am table. v9.6 introduced CREATE
6587 * ACCESS METHOD, so earlier versions usually have only built-in access
6588 * methods. v9.6 also changed the access method API, replacing dozens of
6589 * pg_am columns with amhandler. Even if a user created an access method
6590 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6591 * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6592 * pg_am just to facilitate findAccessMethodByOid() providing the
6593 * OID-to-name mapping.
6594 */
6595 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6596 if (fout->remoteVersion >= 90600)
6598 "amtype, "
6599 "amhandler::pg_catalog.regproc AS amhandler ");
6600 else
6602 "'i'::pg_catalog.\"char\" AS amtype, "
6603 "'-'::pg_catalog.regproc AS amhandler ");
6604 appendPQExpBufferStr(query, "FROM pg_am");
6605
6606 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6607
6608 ntups = PQntuples(res);
6609
6610 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6611
6612 i_tableoid = PQfnumber(res, "tableoid");
6613 i_oid = PQfnumber(res, "oid");
6614 i_amname = PQfnumber(res, "amname");
6615 i_amhandler = PQfnumber(res, "amhandler");
6616 i_amtype = PQfnumber(res, "amtype");
6617
6618 for (i = 0; i < ntups; i++)
6619 {
6620 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6621 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6622 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6623 AssignDumpId(&aminfo[i].dobj);
6624 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6625 aminfo[i].dobj.namespace = NULL;
6626 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6627 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6628
6629 /* Decide whether we want to dump it */
6630 selectDumpableAccessMethod(&(aminfo[i]), fout);
6631 }
6632
6633 PQclear(res);
6634
6635 destroyPQExpBuffer(query);
6636}
6637
6638
6639/*
6640 * getOpclasses:
6641 * get information about all opclasses in the system catalogs
6642 */
6643void
6645{
6646 PGresult *res;
6647 int ntups;
6648 int i;
6650 OpclassInfo *opcinfo;
6651 int i_tableoid;
6652 int i_oid;
6653 int i_opcmethod;
6654 int i_opcname;
6655 int i_opcnamespace;
6656 int i_opcowner;
6657
6658 /*
6659 * find all opclasses, including builtin opclasses; we filter out
6660 * system-defined opclasses at dump-out time.
6661 */
6662
6663 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6664 "opcnamespace, "
6665 "opcowner "
6666 "FROM pg_opclass");
6667
6668 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6669
6670 ntups = PQntuples(res);
6671
6672 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6673
6674 i_tableoid = PQfnumber(res, "tableoid");
6675 i_oid = PQfnumber(res, "oid");
6676 i_opcmethod = PQfnumber(res, "opcmethod");
6677 i_opcname = PQfnumber(res, "opcname");
6678 i_opcnamespace = PQfnumber(res, "opcnamespace");
6679 i_opcowner = PQfnumber(res, "opcowner");
6680
6681 for (i = 0; i < ntups; i++)
6682 {
6683 opcinfo[i].dobj.objType = DO_OPCLASS;
6684 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6685 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6686 AssignDumpId(&opcinfo[i].dobj);
6687 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6688 opcinfo[i].dobj.namespace =
6689 findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6690 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6691 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6692
6693 /* Decide whether we want to dump it */
6694 selectDumpableObject(&(opcinfo[i].dobj), fout);
6695 }
6696
6697 PQclear(res);
6698
6699 destroyPQExpBuffer(query);
6700}
6701
6702/*
6703 * getOpfamilies:
6704 * get information about all opfamilies in the system catalogs
6705 */
6706void
6708{
6709 PGresult *res;
6710 int ntups;
6711 int i;
6712 PQExpBuffer query;
6713 OpfamilyInfo *opfinfo;
6714 int i_tableoid;
6715 int i_oid;
6716 int i_opfmethod;
6717 int i_opfname;
6718 int i_opfnamespace;
6719 int i_opfowner;
6720
6721 query = createPQExpBuffer();
6722
6723 /*
6724 * find all opfamilies, including builtin opfamilies; we filter out
6725 * system-defined opfamilies at dump-out time.
6726 */
6727
6728 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6729 "opfnamespace, "
6730 "opfowner "
6731 "FROM pg_opfamily");
6732
6733 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6734
6735 ntups = PQntuples(res);
6736
6737 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6738
6739 i_tableoid = PQfnumber(res, "tableoid");
6740 i_oid = PQfnumber(res, "oid");
6741 i_opfname = PQfnumber(res, "opfname");
6742 i_opfmethod = PQfnumber(res, "opfmethod");
6743 i_opfnamespace = PQfnumber(res, "opfnamespace");
6744 i_opfowner = PQfnumber(res, "opfowner");
6745
6746 for (i = 0; i < ntups; i++)
6747 {
6748 opfinfo[i].dobj.objType = DO_OPFAMILY;
6749 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6750 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6751 AssignDumpId(&opfinfo[i].dobj);
6752 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6753 opfinfo[i].dobj.namespace =
6754 findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6755 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6756 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6757
6758 /* Decide whether we want to dump it */
6759 selectDumpableObject(&(opfinfo[i].dobj), fout);
6760 }
6761
6762 PQclear(res);
6763
6764 destroyPQExpBuffer(query);
6765}
6766
6767/*
6768 * getAggregates:
6769 * get information about all user-defined aggregates in the system catalogs
6770 */
6771void
6773{
6774 DumpOptions *dopt = fout->dopt;
6775 PGresult *res;
6776 int ntups;
6777 int i;
6779 AggInfo *agginfo;
6780 int i_tableoid;
6781 int i_oid;
6782 int i_aggname;
6783 int i_aggnamespace;
6784 int i_pronargs;
6785 int i_proargtypes;
6786 int i_proowner;
6787 int i_aggacl;
6788 int i_acldefault;
6789
6790 /*
6791 * Find all interesting aggregates. See comment in getFuncs() for the
6792 * rationale behind the filtering logic.
6793 */
6794 if (fout->remoteVersion >= 90600)
6795 {
6796 const char *agg_check;
6797
6798 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6799 : "p.proisagg");
6800
6801 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6802 "p.proname AS aggname, "
6803 "p.pronamespace AS aggnamespace, "
6804 "p.pronargs, p.proargtypes, "
6805 "p.proowner, "
6806 "p.proacl AS aggacl, "
6807 "acldefault('f', p.proowner) AS acldefault "
6808 "FROM pg_proc p "
6809 "LEFT JOIN pg_init_privs pip ON "
6810 "(p.oid = pip.objoid "
6811 "AND pip.classoid = 'pg_proc'::regclass "
6812 "AND pip.objsubid = 0) "
6813 "WHERE %s AND ("
6814 "p.pronamespace != "
6815 "(SELECT oid FROM pg_namespace "
6816 "WHERE nspname = 'pg_catalog') OR "
6817 "p.proacl IS DISTINCT FROM pip.initprivs",
6818 agg_check);
6819 if (dopt->binary_upgrade)
6821 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6822 "classid = 'pg_proc'::regclass AND "
6823 "objid = p.oid AND "
6824 "refclassid = 'pg_extension'::regclass AND "
6825 "deptype = 'e')");
6826 appendPQExpBufferChar(query, ')');
6827 }
6828 else
6829 {
6830 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6831 "pronamespace AS aggnamespace, "
6832 "pronargs, proargtypes, "
6833 "proowner, "
6834 "proacl AS aggacl, "
6835 "acldefault('f', proowner) AS acldefault "
6836 "FROM pg_proc p "
6837 "WHERE proisagg AND ("
6838 "pronamespace != "
6839 "(SELECT oid FROM pg_namespace "
6840 "WHERE nspname = 'pg_catalog')");
6841 if (dopt->binary_upgrade)
6843 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6844 "classid = 'pg_proc'::regclass AND "
6845 "objid = p.oid AND "
6846 "refclassid = 'pg_extension'::regclass AND "
6847 "deptype = 'e')");
6848 appendPQExpBufferChar(query, ')');
6849 }
6850
6851 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6852
6853 ntups = PQntuples(res);
6854
6855 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6856
6857 i_tableoid = PQfnumber(res, "tableoid");
6858 i_oid = PQfnumber(res, "oid");
6859 i_aggname = PQfnumber(res, "aggname");
6860 i_aggnamespace = PQfnumber(res, "aggnamespace");
6861 i_pronargs = PQfnumber(res, "pronargs");
6862 i_proargtypes = PQfnumber(res, "proargtypes");
6863 i_proowner = PQfnumber(res, "proowner");
6864 i_aggacl = PQfnumber(res, "aggacl");
6865 i_acldefault = PQfnumber(res, "acldefault");
6866
6867 for (i = 0; i < ntups; i++)
6868 {
6869 agginfo[i].aggfn.dobj.objType = DO_AGG;
6870 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6871 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6872 AssignDumpId(&agginfo[i].aggfn.dobj);
6873 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6874 agginfo[i].aggfn.dobj.namespace =
6875 findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6876 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6877 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6878 agginfo[i].aggfn.dacl.privtype = 0;
6879 agginfo[i].aggfn.dacl.initprivs = NULL;
6880 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6881 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6882 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6883 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6884 if (agginfo[i].aggfn.nargs == 0)
6885 agginfo[i].aggfn.argtypes = NULL;
6886 else
6887 {
6888 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6889 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6890 agginfo[i].aggfn.argtypes,
6891 agginfo[i].aggfn.nargs);
6892 }
6893 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6894
6895 /* Decide whether we want to dump it */
6896 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6897
6898 /* Mark whether aggregate has an ACL */
6899 if (!PQgetisnull(res, i, i_aggacl))
6900 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6901 }
6902
6903 PQclear(res);
6904
6905 destroyPQExpBuffer(query);
6906}
6907
6908/*
6909 * getFuncs:
6910 * get information about all user-defined functions in the system catalogs
6911 */
6912void
6914{
6915 DumpOptions *dopt = fout->dopt;
6916 PGresult *res;
6917 int ntups;
6918 int i;
6920 FuncInfo *finfo;
6921 int i_tableoid;
6922 int i_oid;
6923 int i_proname;
6924 int i_pronamespace;
6925 int i_proowner;
6926 int i_prolang;
6927 int i_pronargs;
6928 int i_proargtypes;
6929 int i_prorettype;
6930 int i_proacl;
6931 int i_acldefault;
6932
6933 /*
6934 * Find all interesting functions. This is a bit complicated:
6935 *
6936 * 1. Always exclude aggregates; those are handled elsewhere.
6937 *
6938 * 2. Always exclude functions that are internally dependent on something
6939 * else, since presumably those will be created as a result of creating
6940 * the something else. This currently acts only to suppress constructor
6941 * functions for range types. Note this is OK only because the
6942 * constructors don't have any dependencies the range type doesn't have;
6943 * otherwise we might not get creation ordering correct.
6944 *
6945 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6946 * they're members of extensions and we are in binary-upgrade mode then
6947 * include them, since we want to dump extension members individually in
6948 * that mode. Also, if they are used by casts or transforms then we need
6949 * to gather the information about them, though they won't be dumped if
6950 * they are built-in. Also, in 9.6 and up, include functions in
6951 * pg_catalog if they have an ACL different from what's shown in
6952 * pg_init_privs (so we have to join to pg_init_privs; annoying).
6953 */
6954 if (fout->remoteVersion >= 90600)
6955 {
6956 const char *not_agg_check;
6957
6958 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6959 : "NOT p.proisagg");
6960
6961 appendPQExpBuffer(query,
6962 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6963 "p.pronargs, p.proargtypes, p.prorettype, "
6964 "p.proacl, "
6965 "acldefault('f', p.proowner) AS acldefault, "
6966 "p.pronamespace, "
6967 "p.proowner "
6968 "FROM pg_proc p "
6969 "LEFT JOIN pg_init_privs pip ON "
6970 "(p.oid = pip.objoid "
6971 "AND pip.classoid = 'pg_proc'::regclass "
6972 "AND pip.objsubid = 0) "
6973 "WHERE %s"
6974 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6975 "WHERE classid = 'pg_proc'::regclass AND "
6976 "objid = p.oid AND deptype = 'i')"
6977 "\n AND ("
6978 "\n pronamespace != "
6979 "(SELECT oid FROM pg_namespace "
6980 "WHERE nspname = 'pg_catalog')"
6981 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6982 "\n WHERE pg_cast.oid > %u "
6983 "\n AND p.oid = pg_cast.castfunc)"
6984 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6985 "\n WHERE pg_transform.oid > %u AND "
6986 "\n (p.oid = pg_transform.trffromsql"
6987 "\n OR p.oid = pg_transform.trftosql))",
6988 not_agg_check,
6991 if (dopt->binary_upgrade)
6993 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6994 "classid = 'pg_proc'::regclass AND "
6995 "objid = p.oid AND "
6996 "refclassid = 'pg_extension'::regclass AND "
6997 "deptype = 'e')");
6999 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7000 appendPQExpBufferChar(query, ')');
7001 }
7002 else
7003 {
7004 appendPQExpBuffer(query,
7005 "SELECT tableoid, oid, proname, prolang, "
7006 "pronargs, proargtypes, prorettype, proacl, "
7007 "acldefault('f', proowner) AS acldefault, "
7008 "pronamespace, "
7009 "proowner "
7010 "FROM pg_proc p "
7011 "WHERE NOT proisagg"
7012 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7013 "WHERE classid = 'pg_proc'::regclass AND "
7014 "objid = p.oid AND deptype = 'i')"
7015 "\n AND ("
7016 "\n pronamespace != "
7017 "(SELECT oid FROM pg_namespace "
7018 "WHERE nspname = 'pg_catalog')"
7019 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7020 "\n WHERE pg_cast.oid > '%u'::oid"
7021 "\n AND p.oid = pg_cast.castfunc)",
7023
7024 if (fout->remoteVersion >= 90500)
7025 appendPQExpBuffer(query,
7026 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7027 "\n WHERE pg_transform.oid > '%u'::oid"
7028 "\n AND (p.oid = pg_transform.trffromsql"
7029 "\n OR p.oid = pg_transform.trftosql))",
7031
7032 if (dopt->binary_upgrade)
7034 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7035 "classid = 'pg_proc'::regclass AND "
7036 "objid = p.oid AND "
7037 "refclassid = 'pg_extension'::regclass AND "
7038 "deptype = 'e')");
7039 appendPQExpBufferChar(query, ')');
7040 }
7041
7042 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7043
7044 ntups = PQntuples(res);
7045
7046 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7047
7048 i_tableoid = PQfnumber(res, "tableoid");
7049 i_oid = PQfnumber(res, "oid");
7050 i_proname = PQfnumber(res, "proname");
7051 i_pronamespace = PQfnumber(res, "pronamespace");
7052 i_proowner = PQfnumber(res, "proowner");
7053 i_prolang = PQfnumber(res, "prolang");
7054 i_pronargs = PQfnumber(res, "pronargs");
7055 i_proargtypes = PQfnumber(res, "proargtypes");
7056 i_prorettype = PQfnumber(res, "prorettype");
7057 i_proacl = PQfnumber(res, "proacl");
7058 i_acldefault = PQfnumber(res, "acldefault");
7059
7060 for (i = 0; i < ntups; i++)
7061 {
7062 finfo[i].dobj.objType = DO_FUNC;
7063 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7064 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7065 AssignDumpId(&finfo[i].dobj);
7066 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7067 finfo[i].dobj.namespace =
7068 findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7069 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7070 finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7071 finfo[i].dacl.privtype = 0;
7072 finfo[i].dacl.initprivs = NULL;
7073 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7074 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7075 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7076 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7077 if (finfo[i].nargs == 0)
7078 finfo[i].argtypes = NULL;
7079 else
7080 {
7081 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7082 parseOidArray(PQgetvalue(res, i, i_proargtypes),
7083 finfo[i].argtypes, finfo[i].nargs);
7084 }
7085 finfo[i].postponed_def = false; /* might get set during sort */
7086
7087 /* Decide whether we want to dump it */
7088 selectDumpableObject(&(finfo[i].dobj), fout);
7089
7090 /* Mark whether function has an ACL */
7091 if (!PQgetisnull(res, i, i_proacl))
7093 }
7094
7095 PQclear(res);
7096
7097 destroyPQExpBuffer(query);
7098}
7099
7100/*
7101 * getRelationStatistics
7102 * register the statistics object as a dependent of the relation.
7103 *
7104 * reltuples is passed as a string to avoid complexities in converting from/to
7105 * floating point.
7106 */
7107static RelStatsInfo *
7109 char *reltuples, int32 relallvisible,
7110 int32 relallfrozen, char relkind,
7111 char **indAttNames, int nindAttNames)
7112{
7113 if (!fout->dopt->dumpStatistics)
7114 return NULL;
7115
7116 if ((relkind == RELKIND_RELATION) ||
7117 (relkind == RELKIND_PARTITIONED_TABLE) ||
7118 (relkind == RELKIND_INDEX) ||
7119 (relkind == RELKIND_PARTITIONED_INDEX) ||
7120 (relkind == RELKIND_MATVIEW ||
7121 relkind == RELKIND_FOREIGN_TABLE))
7122 {
7123 RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7124 DumpableObject *dobj = &info->dobj;
7125
7126 dobj->objType = DO_REL_STATS;
7127 dobj->catId.tableoid = 0;
7128 dobj->catId.oid = 0;
7129 AssignDumpId(dobj);
7130 dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7131 dobj->dependencies[0] = rel->dumpId;
7132 dobj->nDeps = 1;
7133 dobj->allocDeps = 1;
7135 dobj->name = pg_strdup(rel->name);
7136 dobj->namespace = rel->namespace;
7137 info->relpages = relpages;
7138 info->reltuples = pstrdup(reltuples);
7139 info->relallvisible = relallvisible;
7140 info->relallfrozen = relallfrozen;
7141 info->relkind = relkind;
7142 info->indAttNames = indAttNames;
7143 info->nindAttNames = nindAttNames;
7144
7145 /*
7146 * Ordinarily, stats go in SECTION_DATA for tables and
7147 * SECTION_POST_DATA for indexes.
7148 *
7149 * However, the section may be updated later for materialized view
7150 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7151 * the stats, so the stats must be restored after the data. Also, the
7152 * materialized view definition may be postponed to SECTION_POST_DATA
7153 * (see repairMatViewBoundaryMultiLoop()).
7154 */
7155 switch (info->relkind)
7156 {
7157 case RELKIND_RELATION:
7158 case RELKIND_PARTITIONED_TABLE:
7159 case RELKIND_MATVIEW:
7160 case RELKIND_FOREIGN_TABLE:
7161 info->section = SECTION_DATA;
7162 break;
7163 case RELKIND_INDEX:
7164 case RELKIND_PARTITIONED_INDEX:
7165 info->section = SECTION_POST_DATA;
7166 break;
7167 default:
7168 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7169 info->relkind);
7170 }
7171
7172 return info;
7173 }
7174 return NULL;
7175}
7176
7177/*
7178 * getTables
7179 * read all the tables (no indexes) in the system catalogs,
7180 * and return them as an array of TableInfo structures
7181 *
7182 * *numTables is set to the number of tables read in
7183 */
7184TableInfo *
7185getTables(Archive *fout, int *numTables)
7186{
7187 DumpOptions *dopt = fout->dopt;
7188 PGresult *res;
7189 int ntups;
7190 int i;
7192 TableInfo *tblinfo;
7193 int i_reltableoid;
7194 int i_reloid;
7195 int i_relname;
7196 int i_relnamespace;
7197 int i_relkind;
7198 int i_reltype;
7199 int i_relowner;
7200 int i_relchecks;
7201 int i_relhasindex;
7202 int i_relhasrules;
7203 int i_relpages;
7204 int i_reltuples;
7205 int i_relallvisible;
7206 int i_relallfrozen;
7207 int i_toastpages;
7208 int i_owning_tab;
7209 int i_owning_col;
7210 int i_reltablespace;
7211 int i_relhasoids;
7212 int i_relhastriggers;
7213 int i_relpersistence;
7214 int i_relispopulated;
7215 int i_relreplident;
7216 int i_relrowsec;
7217 int i_relforcerowsec;
7218 int i_relfrozenxid;
7219 int i_toastfrozenxid;
7220 int i_toastoid;
7221 int i_relminmxid;
7222 int i_toastminmxid;
7223 int i_reloptions;
7224 int i_checkoption;
7225 int i_toastreloptions;
7226 int i_reloftype;
7227 int i_foreignserver;
7228 int i_amname;
7229 int i_is_identity_sequence;
7230 int i_relacl;
7231 int i_acldefault;
7232 int i_ispartition;
7233
7234 /*
7235 * Find all the tables and table-like objects.
7236 *
7237 * We must fetch all tables in this phase because otherwise we cannot
7238 * correctly identify inherited columns, owned sequences, etc.
7239 *
7240 * We include system catalogs, so that we can work if a user table is
7241 * defined to inherit from a system catalog (pretty weird, but...)
7242 *
7243 * Note: in this phase we should collect only a minimal amount of
7244 * information about each table, basically just enough to decide if it is
7245 * interesting. In particular, since we do not yet have lock on any user
7246 * table, we MUST NOT invoke any server-side data collection functions
7247 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7248 * wrong answers if any concurrent DDL is happening.
7249 */
7250
7252 "SELECT c.tableoid, c.oid, c.relname, "
7253 "c.relnamespace, c.relkind, c.reltype, "
7254 "c.relowner, "
7255 "c.relchecks, "
7256 "c.relhasindex, c.relhasrules, c.relpages, "
7257 "c.reltuples, c.relallvisible, ");
7258
7259 if (fout->remoteVersion >= 180000)
7260 appendPQExpBufferStr(query, "c.relallfrozen, ");
7261 else
7262 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7263
7265 "c.relhastriggers, c.relpersistence, "
7266 "c.reloftype, "
7267 "c.relacl, "
7268 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7269 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7270 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7271 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7272 "ELSE 0 END AS foreignserver, "
7273 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7274 "tc.oid AS toid, "
7275 "tc.relpages AS toastpages, "
7276 "tc.reloptions AS toast_reloptions, "
7277 "d.refobjid AS owning_tab, "
7278 "d.refobjsubid AS owning_col, "
7279 "tsp.spcname AS reltablespace, ");
7280
7281 if (fout->remoteVersion >= 120000)
7283 "false AS relhasoids, ");
7284 else
7286 "c.relhasoids, ");
7287
7288 if (fout->remoteVersion >= 90300)
7290 "c.relispopulated, ");
7291 else
7293 "'t' as relispopulated, ");
7294
7295 if (fout->remoteVersion >= 90400)
7297 "c.relreplident, ");
7298 else
7300 "'d' AS relreplident, ");
7301
7302 if (fout->remoteVersion >= 90500)
7304 "c.relrowsecurity, c.relforcerowsecurity, ");
7305 else
7307 "false AS relrowsecurity, "
7308 "false AS relforcerowsecurity, ");
7309
7310 if (fout->remoteVersion >= 90300)
7312 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7313 else
7315 "0 AS relminmxid, 0 AS tminmxid, ");
7316
7317 if (fout->remoteVersion >= 90300)
7319 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7320 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7321 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7322 else
7324 "c.reloptions, NULL AS checkoption, ");
7325
7326 if (fout->remoteVersion >= 90600)
7328 "am.amname, ");
7329 else
7331 "NULL AS amname, ");
7332
7333 if (fout->remoteVersion >= 90600)
7335 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7336 else
7338 "false AS is_identity_sequence, ");
7339
7340 if (fout->remoteVersion >= 100000)
7342 "c.relispartition AS ispartition ");
7343 else
7345 "false AS ispartition ");
7346
7347 /*
7348 * Left join to pg_depend to pick up dependency info linking sequences to
7349 * their owning column, if any (note this dependency is AUTO except for
7350 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7351 * collect the spcname.
7352 */
7354 "\nFROM pg_class c\n"
7355 "LEFT JOIN pg_depend d ON "
7356 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7357 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7358 "d.objsubid = 0 AND "
7359 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7360 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7361
7362 /*
7363 * In 9.6 and up, left join to pg_am to pick up the amname.
7364 */
7365 if (fout->remoteVersion >= 90600)
7367 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7368
7369 /*
7370 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7371 * that versions 10 and 11 have them, but later versions do not, so
7372 * emitting them causes the upgrade to fail.
7373 */
7375 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7376 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7377 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7378
7379 /*
7380 * Restrict to interesting relkinds (in particular, not indexes). Not all
7381 * relkinds are possible in older servers, but it's not worth the trouble
7382 * to emit a version-dependent list.
7383 *
7384 * Composite-type table entries won't be dumped as such, but we have to
7385 * make a DumpableObject for them so that we can track dependencies of the
7386 * composite type (pg_depend entries for columns of the composite type
7387 * link to the pg_class entry not the pg_type entry).
7388 */
7390 "WHERE c.relkind IN ("
7391 CppAsString2(RELKIND_RELATION) ", "
7392 CppAsString2(RELKIND_SEQUENCE) ", "
7393 CppAsString2(RELKIND_VIEW) ", "
7394 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7395 CppAsString2(RELKIND_MATVIEW) ", "
7396 CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7397 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7398 "ORDER BY c.oid");
7399
7400 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7401
7402 ntups = PQntuples(res);
7403
7404 *numTables = ntups;
7405
7406 /*
7407 * Extract data from result and lock dumpable tables. We do the locking
7408 * before anything else, to minimize the window wherein a table could
7409 * disappear under us.
7410 *
7411 * Note that we have to save info about all tables here, even when dumping
7412 * only one, because we don't yet know which tables might be inheritance
7413 * ancestors of the target table.
7414 */
7415 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7416
7417 i_reltableoid = PQfnumber(res, "tableoid");
7418 i_reloid = PQfnumber(res, "oid");
7419 i_relname = PQfnumber(res, "relname");
7420 i_relnamespace = PQfnumber(res, "relnamespace");
7421 i_relkind = PQfnumber(res, "relkind");
7422 i_reltype = PQfnumber(res, "reltype");
7423 i_relowner = PQfnumber(res, "relowner");
7424 i_relchecks = PQfnumber(res, "relchecks");
7425 i_relhasindex = PQfnumber(res, "relhasindex");
7426 i_relhasrules = PQfnumber(res, "relhasrules");
7427 i_relpages = PQfnumber(res, "relpages");
7428 i_reltuples = PQfnumber(res, "reltuples");
7429 i_relallvisible = PQfnumber(res, "relallvisible");
7430 i_relallfrozen = PQfnumber(res, "relallfrozen");
7431 i_toastpages = PQfnumber(res, "toastpages");
7432 i_owning_tab = PQfnumber(res, "owning_tab");
7433 i_owning_col = PQfnumber(res, "owning_col");
7434 i_reltablespace = PQfnumber(res, "reltablespace");
7435 i_relhasoids = PQfnumber(res, "relhasoids");
7436 i_relhastriggers = PQfnumber(res, "relhastriggers");
7437 i_relpersistence = PQfnumber(res, "relpersistence");
7438 i_relispopulated = PQfnumber(res, "relispopulated");
7439 i_relreplident = PQfnumber(res, "relreplident");
7440 i_relrowsec = PQfnumber(res, "relrowsecurity");
7441 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7442 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7443 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7444 i_toastoid = PQfnumber(res, "toid");
7445 i_relminmxid = PQfnumber(res, "relminmxid");
7446 i_toastminmxid = PQfnumber(res, "tminmxid");
7447 i_reloptions = PQfnumber(res, "reloptions");
7448 i_checkoption = PQfnumber(res, "checkoption");
7449 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7450 i_reloftype = PQfnumber(res, "reloftype");
7451 i_foreignserver = PQfnumber(res, "foreignserver");
7452 i_amname = PQfnumber(res, "amname");
7453 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7454 i_relacl = PQfnumber(res, "relacl");
7455 i_acldefault = PQfnumber(res, "acldefault");
7456 i_ispartition = PQfnumber(res, "ispartition");
7457
7458 if (dopt->lockWaitTimeout)
7459 {
7460 /*
7461 * Arrange to fail instead of waiting forever for a table lock.
7462 *
7463 * NB: this coding assumes that the only queries issued within the
7464 * following loop are LOCK TABLEs; else the timeout may be undesirably
7465 * applied to other things too.
7466 */
7467 resetPQExpBuffer(query);
7468 appendPQExpBufferStr(query, "SET statement_timeout = ");
7470 ExecuteSqlStatement(fout, query->data);
7471 }
7472
7473 resetPQExpBuffer(query);
7474
7475 for (i = 0; i < ntups; i++)
7476 {
7477 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7478 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7479
7480 tblinfo[i].dobj.objType = DO_TABLE;
7481 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7482 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7483 AssignDumpId(&tblinfo[i].dobj);
7484 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7485 tblinfo[i].dobj.namespace =
7486 findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7487 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7488 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7489 tblinfo[i].dacl.privtype = 0;
7490 tblinfo[i].dacl.initprivs = NULL;
7491 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7492 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7493 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7494 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7495 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7496 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7497 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7498 if (PQgetisnull(res, i, i_toastpages))
7499 tblinfo[i].toastpages = 0;
7500 else
7501 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7502 if (PQgetisnull(res, i, i_owning_tab))
7503 {
7504 tblinfo[i].owning_tab = InvalidOid;
7505 tblinfo[i].owning_col = 0;
7506 }
7507 else
7508 {
7509 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7510 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7511 }
7512 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7513 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7514 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7515 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7516 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7517 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7518 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7519 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7520 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7521 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7522 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7523 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7524 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7525 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7526 if (PQgetisnull(res, i, i_checkoption))
7527 tblinfo[i].checkoption = NULL;
7528 else
7529 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7530 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7531 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7532 tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7533 if (PQgetisnull(res, i, i_amname))
7534 tblinfo[i].amname = NULL;
7535 else
7536 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7537 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7538 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7539
7540 /* other fields were zeroed above */
7541
7542 /*
7543 * Decide whether we want to dump this table.
7544 */
7545 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7546 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7547 else
7548 selectDumpableTable(&tblinfo[i], fout);
7549
7550 /*
7551 * Now, consider the table "interesting" if we need to dump its
7552 * definition, data or its statistics. Later on, we'll skip a lot of
7553 * data collection for uninteresting tables.
7554 *
7555 * Note: the "interesting" flag will also be set by flagInhTables for
7556 * parents of interesting tables, so that we collect necessary
7557 * inheritance info even when the parents are not themselves being
7558 * dumped. This is the main reason why we need an "interesting" flag
7559 * that's separate from the components-to-dump bitmask.
7560 */
7561 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7565
7566 tblinfo[i].dummy_view = false; /* might get set during sort */
7567 tblinfo[i].postponed_def = false; /* might get set during sort */
7568
7569 /* Tables have data */
7571
7572 /* Mark whether table has an ACL */
7573 if (!PQgetisnull(res, i, i_relacl))
7574 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7575 tblinfo[i].hascolumnACLs = false; /* may get set later */
7576
7577 /* Add statistics */
7578 if (tblinfo[i].interesting)
7579 {
7580 RelStatsInfo *stats;
7581
7582 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7583 tblinfo[i].relpages,
7584 PQgetvalue(res, i, i_reltuples),
7585 relallvisible, relallfrozen,
7586 tblinfo[i].relkind, NULL, 0);
7587 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7588 tblinfo[i].stats = stats;
7589 }
7590
7591 /*
7592 * Read-lock target tables to make sure they aren't DROPPED or altered
7593 * in schema before we get around to dumping them.
7594 *
7595 * Note that we don't explicitly lock parents of the target tables; we
7596 * assume our lock on the child is enough to prevent schema
7597 * alterations to parent tables.
7598 *
7599 * NOTE: it'd be kinda nice to lock other relations too, not only
7600 * plain or partitioned tables, but the backend doesn't presently
7601 * allow that.
7602 *
7603 * We only need to lock the table for certain components; see
7604 * pg_dump.h
7605 */
7606 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7607 (tblinfo[i].relkind == RELKIND_RELATION ||
7608 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7609 {
7610 /*
7611 * Tables are locked in batches. When dumping from a remote
7612 * server this can save a significant amount of time by reducing
7613 * the number of round trips.
7614 */
7615 if (query->len == 0)
7616 appendPQExpBuffer(query, "LOCK TABLE %s",
7617 fmtQualifiedDumpable(&tblinfo[i]));
7618 else
7619 {
7620 appendPQExpBuffer(query, ", %s",
7621 fmtQualifiedDumpable(&tblinfo[i]));
7622
7623 /* Arbitrarily end a batch when query length reaches 100K. */
7624 if (query->len >= 100000)
7625 {
7626 /* Lock another batch of tables. */
7627 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7628 ExecuteSqlStatement(fout, query->data);
7629 resetPQExpBuffer(query);
7630 }
7631 }
7632 }
7633 }
7634
7635 if (query->len != 0)
7636 {
7637 /* Lock the tables in the last batch. */
7638 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7639 ExecuteSqlStatement(fout, query->data);
7640 }
7641
7642 if (dopt->lockWaitTimeout)
7643 {
7644 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7645 }
7646
7647 PQclear(res);
7648
7649 destroyPQExpBuffer(query);
7650
7651 return tblinfo;
7652}
7653
7654/*
7655 * getOwnedSeqs
7656 * identify owned sequences and mark them as dumpable if owning table is
7657 *
7658 * We used to do this in getTables(), but it's better to do it after the
7659 * index used by findTableByOid() has been set up.
7660 */
7661void
7662getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7663{
7664 int i;
7665
7666 /*
7667 * Force sequences that are "owned" by table columns to be dumped whenever
7668 * their owning table is being dumped.
7669 */
7670 for (i = 0; i < numTables; i++)
7671 {
7672 TableInfo *seqinfo = &tblinfo[i];
7673 TableInfo *owning_tab;
7674
7675 if (!OidIsValid(seqinfo->owning_tab))
7676 continue; /* not an owned sequence */
7677
7678 owning_tab = findTableByOid(seqinfo->owning_tab);
7679 if (owning_tab == NULL)
7680 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7681 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7682
7683 /*
7684 * For an identity sequence, dump exactly the same components for the
7685 * sequence as for the owning table. This is important because we
7686 * treat the identity sequence as an integral part of the table. For
7687 * example, there is not any DDL command that allows creation of such
7688 * a sequence independently of the table.
7689 *
7690 * For other owned sequences such as serial sequences, we need to dump
7691 * the components that are being dumped for the table and any
7692 * components that the sequence is explicitly marked with.
7693 *
7694 * We can't simply use the set of components which are being dumped
7695 * for the table as the table might be in an extension (and only the
7696 * non-extension components, eg: ACLs if changed, security labels, and
7697 * policies, are being dumped) while the sequence is not (and
7698 * therefore the definition and other components should also be
7699 * dumped).
7700 *
7701 * If the sequence is part of the extension then it should be properly
7702 * marked by checkExtensionMembership() and this will be a no-op as
7703 * the table will be equivalently marked.
7704 */
7705 if (seqinfo->is_identity_sequence)
7706 seqinfo->dobj.dump = owning_tab->dobj.dump;
7707 else
7708 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7709
7710 /* Make sure that necessary data is available if we're dumping it */
7711 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7712 {
7713 seqinfo->interesting = true;
7714 owning_tab->interesting = true;
7715 }
7716 }
7717}
7718
7719/*
7720 * getInherits
7721 * read all the inheritance information
7722 * from the system catalogs return them in the InhInfo* structure
7723 *
7724 * numInherits is set to the number of pairs read in
7725 */
7726InhInfo *
7727getInherits(Archive *fout, int *numInherits)
7728{
7729 PGresult *res;
7730 int ntups;
7731 int i;
7733 InhInfo *inhinfo;
7734
7735 int i_inhrelid;
7736 int i_inhparent;
7737
7738 /* find all the inheritance information */
7739 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7740
7741 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7742
7743 ntups = PQntuples(res);
7744
7745 *numInherits = ntups;
7746
7747 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7748
7749 i_inhrelid = PQfnumber(res, "inhrelid");
7750 i_inhparent = PQfnumber(res, "inhparent");
7751
7752 for (i = 0; i < ntups; i++)
7753 {
7754 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7755 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7756 }
7757
7758 PQclear(res);
7759
7760 destroyPQExpBuffer(query);
7761
7762 return inhinfo;
7763}
7764
7765/*
7766 * getPartitioningInfo
7767 * get information about partitioning
7768 *
7769 * For the most part, we only collect partitioning info about tables we
7770 * intend to dump. However, this function has to consider all partitioned
7771 * tables in the database, because we need to know about parents of partitions
7772 * we are going to dump even if the parents themselves won't be dumped.
7773 *
7774 * Specifically, what we need to know is whether each partitioned table
7775 * has an "unsafe" partitioning scheme that requires us to force
7776 * load-via-partition-root mode for its children. Currently the only case
7777 * for which we force that is hash partitioning on enum columns, since the
7778 * hash codes depend on enum value OIDs which won't be replicated across
7779 * dump-and-reload. There are other cases in which load-via-partition-root
7780 * might be necessary, but we expect users to cope with them.
7781 */
7782void
7784{
7785 PQExpBuffer query;
7786 PGresult *res;
7787 int ntups;
7788
7789 /* hash partitioning didn't exist before v11 */
7790 if (fout->remoteVersion < 110000)
7791 return;
7792 /* needn't bother if not dumping data */
7793 if (!fout->dopt->dumpData)
7794 return;
7795
7796 query = createPQExpBuffer();
7797
7798 /*
7799 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7800 * appears among the partition opclasses. We needn't check partstrat.
7801 *
7802 * Note that this query may well retrieve info about tables we aren't
7803 * going to dump and hence have no lock on. That's okay since we need not
7804 * invoke any unsafe server-side functions.
7805 */
7807 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7808 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7809 "ON c.opcmethod = a.oid\n"
7810 "WHERE opcname = 'enum_ops' "
7811 "AND opcnamespace = 'pg_catalog'::regnamespace "
7812 "AND amname = 'hash') = ANY(partclass)");
7813
7814 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7815
7816 ntups = PQntuples(res);
7817
7818 for (int i = 0; i < ntups; i++)
7819 {
7820 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7821 TableInfo *tbinfo;
7822
7823 tbinfo = findTableByOid(tabrelid);
7824 if (tbinfo == NULL)
7825 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7826 tabrelid);
7827 tbinfo->unsafe_partitions = true;
7828 }
7829
7830 PQclear(res);
7831
7832 destroyPQExpBuffer(query);
7833}
7834
7835/*
7836 * getIndexes
7837 * get information about every index on a dumpable table
7838 *
7839 * Note: index data is not returned directly to the caller, but it
7840 * does get entered into the DumpableObject tables.
7841 */
7842void
7843getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7844{
7846 PQExpBuffer tbloids = createPQExpBuffer();
7847 PGresult *res;
7848 int ntups;
7849 int curtblindx;
7850 IndxInfo *indxinfo;
7851 int i_tableoid,
7852 i_oid,
7853 i_indrelid,
7854 i_indexname,
7855 i_relpages,
7856 i_reltuples,
7857 i_relallvisible,
7858 i_relallfrozen,
7859 i_parentidx,
7860 i_indexdef,
7861 i_indnkeyatts,
7862 i_indnatts,
7863 i_indkey,
7864 i_indisclustered,
7865 i_indisreplident,
7866 i_indnullsnotdistinct,
7867 i_contype,
7868 i_conname,
7869 i_condeferrable,
7870 i_condeferred,
7871 i_conperiod,
7872 i_contableoid,
7873 i_conoid,
7874 i_condef,
7875 i_indattnames,
7876 i_tablespace,
7877 i_indreloptions,
7878 i_indstatcols,
7879 i_indstatvals;
7880
7881 /*
7882 * We want to perform just one query against pg_index. However, we
7883 * mustn't try to select every row of the catalog and then sort it out on
7884 * the client side, because some of the server-side functions we need
7885 * would be unsafe to apply to tables we don't have lock on. Hence, we
7886 * build an array of the OIDs of tables we care about (and now have lock
7887 * on!), and use a WHERE clause to constrain which rows are selected.
7888 */
7889 appendPQExpBufferChar(tbloids, '{');
7890 for (int i = 0; i < numTables; i++)
7891 {
7892 TableInfo *tbinfo = &tblinfo[i];
7893
7894 if (!tbinfo->hasindex)
7895 continue;
7896
7897 /*
7898 * We can ignore indexes of uninteresting tables.
7899 */
7900 if (!tbinfo->interesting)
7901 continue;
7902
7903 /* OK, we need info for this table */
7904 if (tbloids->len > 1) /* do we have more than the '{'? */
7905 appendPQExpBufferChar(tbloids, ',');
7906 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7907 }
7908 appendPQExpBufferChar(tbloids, '}');
7909
7911 "SELECT t.tableoid, t.oid, i.indrelid, "
7912 "t.relname AS indexname, "
7913 "t.relpages, t.reltuples, t.relallvisible, ");
7914
7915 if (fout->remoteVersion >= 180000)
7916 appendPQExpBufferStr(query, "t.relallfrozen, ");
7917 else
7918 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7919
7921 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7922 "i.indkey, i.indisclustered, "
7923 "c.contype, c.conname, "
7924 "c.condeferrable, c.condeferred, "
7925 "c.tableoid AS contableoid, "
7926 "c.oid AS conoid, "
7927 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7928 "CASE WHEN i.indexprs IS NOT NULL THEN "
7929 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7930 " FROM pg_catalog.pg_attribute "
7931 " WHERE attrelid = i.indexrelid) "
7932 "ELSE NULL END AS indattnames, "
7933 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7934 "t.reloptions AS indreloptions, ");
7935
7936
7937 if (fout->remoteVersion >= 90400)
7939 "i.indisreplident, ");
7940 else
7942 "false AS indisreplident, ");
7943
7944 if (fout->remoteVersion >= 110000)
7946 "inh.inhparent AS parentidx, "
7947 "i.indnkeyatts AS indnkeyatts, "
7948 "i.indnatts AS indnatts, "
7949 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7950 " FROM pg_catalog.pg_attribute "
7951 " WHERE attrelid = i.indexrelid AND "
7952 " attstattarget >= 0) AS indstatcols, "
7953 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7954 " FROM pg_catalog.pg_attribute "
7955 " WHERE attrelid = i.indexrelid AND "
7956 " attstattarget >= 0) AS indstatvals, ");
7957 else
7959 "0 AS parentidx, "
7960 "i.indnatts AS indnkeyatts, "
7961 "i.indnatts AS indnatts, "
7962 "'' AS indstatcols, "
7963 "'' AS indstatvals, ");
7964
7965 if (fout->remoteVersion >= 150000)
7967 "i.indnullsnotdistinct, ");
7968 else
7970 "false AS indnullsnotdistinct, ");
7971
7972 if (fout->remoteVersion >= 180000)
7974 "c.conperiod ");
7975 else
7977 "NULL AS conperiod ");
7978
7979 /*
7980 * The point of the messy-looking outer join is to find a constraint that
7981 * is related by an internal dependency link to the index. If we find one,
7982 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7983 * index won't have more than one internal dependency.
7984 *
7985 * Note: the check on conrelid is redundant, but useful because that
7986 * column is indexed while conindid is not.
7987 */
7988 if (fout->remoteVersion >= 110000)
7989 {
7990 appendPQExpBuffer(query,
7991 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7992 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7993 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7994 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7995 "LEFT JOIN pg_catalog.pg_constraint c "
7996 "ON (i.indrelid = c.conrelid AND "
7997 "i.indexrelid = c.conindid AND "
7998 "c.contype IN ('p','u','x')) "
7999 "LEFT JOIN pg_catalog.pg_inherits inh "
8000 "ON (inh.inhrelid = indexrelid) "
8001 "WHERE (i.indisvalid OR t2.relkind = 'p') "
8002 "AND i.indisready "
8003 "ORDER BY i.indrelid, indexname",
8004 tbloids->data);
8005 }
8006 else
8007 {
8008 /*
8009 * the test on indisready is necessary in 9.2, and harmless in
8010 * earlier/later versions
8011 */
8012 appendPQExpBuffer(query,
8013 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8014 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8015 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8016 "LEFT JOIN pg_catalog.pg_constraint c "
8017 "ON (i.indrelid = c.conrelid AND "
8018 "i.indexrelid = c.conindid AND "
8019 "c.contype IN ('p','u','x')) "
8020 "WHERE i.indisvalid AND i.indisready "
8021 "ORDER BY i.indrelid, indexname",
8022 tbloids->data);
8023 }
8024
8025 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8026
8027 ntups = PQntuples(res);
8028
8029 i_tableoid = PQfnumber(res, "tableoid");
8030 i_oid = PQfnumber(res, "oid");
8031 i_indrelid = PQfnumber(res, "indrelid");
8032 i_indexname = PQfnumber(res, "indexname");
8033 i_relpages = PQfnumber(res, "relpages");
8034 i_reltuples = PQfnumber(res, "reltuples");
8035 i_relallvisible = PQfnumber(res, "relallvisible");
8036 i_relallfrozen = PQfnumber(res, "relallfrozen");
8037 i_parentidx = PQfnumber(res, "parentidx");
8038 i_indexdef = PQfnumber(res, "indexdef");
8039 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8040 i_indnatts = PQfnumber(res, "indnatts");
8041 i_indkey = PQfnumber(res, "indkey");
8042 i_indisclustered = PQfnumber(res, "indisclustered");
8043 i_indisreplident = PQfnumber(res, "indisreplident");
8044 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8045 i_contype = PQfnumber(res, "contype");
8046 i_conname = PQfnumber(res, "conname");
8047 i_condeferrable = PQfnumber(res, "condeferrable");
8048 i_condeferred = PQfnumber(res, "condeferred");
8049 i_conperiod = PQfnumber(res, "conperiod");
8050 i_contableoid = PQfnumber(res, "contableoid");
8051 i_conoid = PQfnumber(res, "conoid");
8052 i_condef = PQfnumber(res, "condef");
8053 i_indattnames = PQfnumber(res, "indattnames");
8054 i_tablespace = PQfnumber(res, "tablespace");
8055 i_indreloptions = PQfnumber(res, "indreloptions");
8056 i_indstatcols = PQfnumber(res, "indstatcols");
8057 i_indstatvals = PQfnumber(res, "indstatvals");
8058
8059 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8060
8061 /*
8062 * Outer loop iterates once per table, not once per row. Incrementing of
8063 * j is handled by the inner loop.
8064 */
8065 curtblindx = -1;
8066 for (int j = 0; j < ntups;)
8067 {
8068 Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8069 TableInfo *tbinfo = NULL;
8070 char **indAttNames = NULL;
8071 int nindAttNames = 0;
8072 int numinds;
8073
8074 /* Count rows for this table */
8075 for (numinds = 1; numinds < ntups - j; numinds++)
8076 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8077 break;
8078
8079 /*
8080 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8081 * order.
8082 */
8083 while (++curtblindx < numTables)
8084 {
8085 tbinfo = &tblinfo[curtblindx];
8086 if (tbinfo->dobj.catId.oid == indrelid)
8087 break;
8088 }
8089 if (curtblindx >= numTables)
8090 pg_fatal("unrecognized table OID %u", indrelid);
8091 /* cross-check that we only got requested tables */
8092 if (!tbinfo->hasindex ||
8093 !tbinfo->interesting)
8094 pg_fatal("unexpected index data for table \"%s\"",
8095 tbinfo->dobj.name);
8096
8097 /* Save data for this table */
8098 tbinfo->indexes = indxinfo + j;
8099 tbinfo->numIndexes = numinds;
8100
8101 for (int c = 0; c < numinds; c++, j++)
8102 {
8103 char contype;
8104 char indexkind;
8105 RelStatsInfo *relstats;
8106 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8107 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8108 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8109
8110 indxinfo[j].dobj.objType = DO_INDEX;
8111 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8112 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8113 AssignDumpId(&indxinfo[j].dobj);
8114 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8115 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8116 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8117 indxinfo[j].indextable = tbinfo;
8118 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8119 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8120 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8121 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8122 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8123 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8124 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8125 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8126 parseOidArray(PQgetvalue(res, j, i_indkey),
8127 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8128 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8129 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8130 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8131 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8132 indxinfo[j].partattaches = (SimplePtrList)
8133 {
8134 NULL, NULL
8135 };
8136
8137 if (indxinfo[j].parentidx == 0)
8138 indexkind = RELKIND_INDEX;
8139 else
8140 indexkind = RELKIND_PARTITIONED_INDEX;
8141
8142 if (!PQgetisnull(res, j, i_indattnames))
8143 {
8144 if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8145 &indAttNames, &nindAttNames))
8146 pg_fatal("could not parse %s array", "indattnames");
8147 }
8148
8149 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8150 PQgetvalue(res, j, i_reltuples),
8151 relallvisible, relallfrozen, indexkind,
8152 indAttNames, nindAttNames);
8153
8154 contype = *(PQgetvalue(res, j, i_contype));
8155 if (contype == 'p' || contype == 'u' || contype == 'x')
8156 {
8157 /*
8158 * If we found a constraint matching the index, create an
8159 * entry for it.
8160 */
8161 ConstraintInfo *constrinfo;
8162
8163 constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8164 constrinfo->dobj.objType = DO_CONSTRAINT;
8165 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8166 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8167 AssignDumpId(&constrinfo->dobj);
8168 constrinfo->dobj.dump = tbinfo->dobj.dump;
8169 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8170 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8171 constrinfo->contable = tbinfo;
8172 constrinfo->condomain = NULL;
8173 constrinfo->contype = contype;
8174 if (contype == 'x')
8175 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8176 else
8177 constrinfo->condef = NULL;
8178 constrinfo->confrelid = InvalidOid;
8179 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8180 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8181 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8182 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8183 constrinfo->conislocal = true;
8184 constrinfo->separate = true;
8185
8186 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8187 if (relstats != NULL)
8188 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8189 }
8190 else
8191 {
8192 /* Plain secondary index */
8193 indxinfo[j].indexconstraint = 0;
8194 }
8195 }
8196 }
8197
8198 PQclear(res);
8199
8200 destroyPQExpBuffer(query);
8201 destroyPQExpBuffer(tbloids);
8202}
8203
8204/*
8205 * getExtendedStatistics
8206 * get information about extended-statistics objects.
8207 *
8208 * Note: extended statistics data is not returned directly to the caller, but
8209 * it does get entered into the DumpableObject tables.
8210 */
8211void
8213{
8214 PQExpBuffer query;
8215 PGresult *res;
8216 StatsExtInfo *statsextinfo;
8217 int ntups;
8218 int i_tableoid;
8219 int i_oid;
8220 int i_stxname;
8221 int i_stxnamespace;
8222 int i_stxowner;
8223 int i_stxrelid;
8224 int i_stattarget;
8225 int i;
8226
8227 /* Extended statistics were new in v10 */
8228 if (fout->remoteVersion < 100000)
8229 return;
8230
8231 query = createPQExpBuffer();
8232
8233 if (fout->remoteVersion < 130000)
8234 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8235 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8236 "FROM pg_catalog.pg_statistic_ext");
8237 else
8238 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8239 "stxnamespace, stxowner, stxrelid, stxstattarget "
8240 "FROM pg_catalog.pg_statistic_ext");
8241
8242 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8243
8244 ntups = PQntuples(res);
8245
8246 i_tableoid = PQfnumber(res, "tableoid");
8247 i_oid = PQfnumber(res, "oid");
8248 i_stxname = PQfnumber(res, "stxname");
8249 i_stxnamespace = PQfnumber(res, "stxnamespace");
8250 i_stxowner = PQfnumber(res, "stxowner");
8251 i_stxrelid = PQfnumber(res, "stxrelid");
8252 i_stattarget = PQfnumber(res, "stxstattarget");
8253
8254 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8255
8256 for (i = 0; i < ntups; i++)
8257 {
8258 statsextinfo[i].dobj.objType = DO_STATSEXT;
8259 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8260 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8261 AssignDumpId(&statsextinfo[i].dobj);
8262 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8263 statsextinfo[i].dobj.namespace =
8264 findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8265 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8266 statsextinfo[i].stattable =
8267 findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8268 if (PQgetisnull(res, i, i_stattarget))
8269 statsextinfo[i].stattarget = -1;
8270 else
8271 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8272
8273 /* Decide whether we want to dump it */
8274 selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8275 }
8276
8277 PQclear(res);
8278 destroyPQExpBuffer(query);
8279}
8280
8281/*
8282 * getConstraints
8283 *
8284 * Get info about constraints on dumpable tables.
8285 *
8286 * Currently handles foreign keys only.
8287 * Unique and primary key constraints are handled with indexes,
8288 * while check constraints are processed in getTableAttrs().
8289 */
8290void
8291getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8292{
8294 PQExpBuffer tbloids = createPQExpBuffer();
8295 PGresult *res;
8296 int ntups;
8297 int curtblindx;
8298 TableInfo *tbinfo = NULL;
8299 ConstraintInfo *constrinfo;
8300 int i_contableoid,
8301 i_conoid,
8302 i_conrelid,
8303 i_conname,
8304 i_confrelid,
8305 i_conindid,
8306 i_condef;
8307
8308 /*
8309 * We want to perform just one query against pg_constraint. However, we
8310 * mustn't try to select every row of the catalog and then sort it out on
8311 * the client side, because some of the server-side functions we need
8312 * would be unsafe to apply to tables we don't have lock on. Hence, we
8313 * build an array of the OIDs of tables we care about (and now have lock
8314 * on!), and use a WHERE clause to constrain which rows are selected.
8315 */
8316 appendPQExpBufferChar(tbloids, '{');
8317 for (int i = 0; i < numTables; i++)
8318 {
8319 TableInfo *tinfo = &tblinfo[i];
8320
8321 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8322 continue;
8323
8324 /* OK, we need info for this table */
8325 if (tbloids->len > 1) /* do we have more than the '{'? */
8326 appendPQExpBufferChar(tbloids, ',');
8327 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8328 }
8329 appendPQExpBufferChar(tbloids, '}');
8330
8332 "SELECT c.tableoid, c.oid, "
8333 "conrelid, conname, confrelid, ");
8334 if (fout->remoteVersion >= 110000)
8335 appendPQExpBufferStr(query, "conindid, ");
8336 else
8337 appendPQExpBufferStr(query, "0 AS conindid, ");
8338 appendPQExpBuffer(query,
8339 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8340 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8341 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8342 "WHERE contype = 'f' ",
8343 tbloids->data);
8344 if (fout->remoteVersion >= 110000)
8346 "AND conparentid = 0 ");
8348 "ORDER BY conrelid, conname");
8349
8350 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8351
8352 ntups = PQntuples(res);
8353
8354 i_contableoid = PQfnumber(res, "tableoid");
8355 i_conoid = PQfnumber(res, "oid");
8356 i_conrelid = PQfnumber(res, "conrelid");
8357 i_conname = PQfnumber(res, "conname");
8358 i_confrelid = PQfnumber(res, "confrelid");
8359 i_conindid = PQfnumber(res, "conindid");
8360 i_condef = PQfnumber(res, "condef");
8361
8362 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8363
8364 curtblindx = -1;
8365 for (int j = 0; j < ntups; j++)
8366 {
8367 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8368 TableInfo *reftable;
8369
8370 /*
8371 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8372 * order.
8373 */
8374 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8375 {
8376 while (++curtblindx < numTables)
8377 {
8378 tbinfo = &tblinfo[curtblindx];
8379 if (tbinfo->dobj.catId.oid == conrelid)
8380 break;
8381 }
8382 if (curtblindx >= numTables)
8383 pg_fatal("unrecognized table OID %u", conrelid);
8384 }
8385
8386 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8387 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8388 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8389 AssignDumpId(&constrinfo[j].dobj);
8390 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8391 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8392 constrinfo[j].contable = tbinfo;
8393 constrinfo[j].condomain = NULL;
8394 constrinfo[j].contype = 'f';
8395 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8396 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8397 constrinfo[j].conindex = 0;
8398 constrinfo[j].condeferrable = false;
8399 constrinfo[j].condeferred = false;
8400 constrinfo[j].conislocal = true;
8401 constrinfo[j].separate = true;
8402
8403 /*
8404 * Restoring an FK that points to a partitioned table requires that
8405 * all partition indexes have been attached beforehand. Ensure that
8406 * happens by making the constraint depend on each index partition
8407 * attach object.
8408 */
8409 reftable = findTableByOid(constrinfo[j].confrelid);
8410 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8411 {
8412 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8413
8414 if (indexOid != InvalidOid)
8415 {
8416 for (int k = 0; k < reftable->numIndexes; k++)
8417 {
8418 IndxInfo *refidx;
8419
8420 /* not our index? */
8421 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8422 continue;
8423
8424 refidx = &reftable->indexes[k];
8425 addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8426 break;
8427 }
8428 }
8429 }
8430 }
8431
8432 PQclear(res);
8433
8434 destroyPQExpBuffer(query);
8435 destroyPQExpBuffer(tbloids);
8436}
8437
8438/*
8439 * addConstrChildIdxDeps
8440 *
8441 * Recursive subroutine for getConstraints
8442 *
8443 * Given an object representing a foreign key constraint and an index on the
8444 * partitioned table it references, mark the constraint object as dependent
8445 * on the DO_INDEX_ATTACH object of each index partition, recursively
8446 * drilling down to their partitions if any. This ensures that the FK is not
8447 * restored until the index is fully marked valid.
8448 */
8449static void
8451{
8452 SimplePtrListCell *cell;
8453
8455
8456 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8457 {
8458 IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8459
8460 addObjectDependency(dobj, attach->dobj.dumpId);
8461
8462 if (attach->partitionIdx->partattaches.head != NULL)
8463 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8464 }
8465}
8466
8467/*
8468 * getDomainConstraints
8469 *
8470 * Get info about constraints on a domain.
8471 */
8472static void
8474{
8475 ConstraintInfo *constrinfo;
8477 PGresult *res;
8478 int i_tableoid,
8479 i_oid,
8480 i_conname,
8481 i_consrc,
8482 i_convalidated,
8483 i_contype;
8484 int ntups;
8485
8487 {
8488 /*
8489 * Set up query for constraint-specific details. For servers 17 and
8490 * up, domains have constraints of type 'n' as well as 'c', otherwise
8491 * just the latter.
8492 */
8493 appendPQExpBuffer(query,
8494 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8495 "SELECT tableoid, oid, conname, "
8496 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8497 "convalidated, contype "
8498 "FROM pg_catalog.pg_constraint "
8499 "WHERE contypid = $1 AND contype IN (%s) "
8500 "ORDER BY conname",
8501 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8502
8503 ExecuteSqlStatement(fout, query->data);
8504
8506 }
8507
8508 printfPQExpBuffer(query,
8509 "EXECUTE getDomainConstraints('%u')",
8510 tyinfo->dobj.catId.oid);
8511
8512 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8513
8514 ntups = PQntuples(res);
8515
8516 i_tableoid = PQfnumber(res, "tableoid");
8517 i_oid = PQfnumber(res, "oid");
8518 i_conname = PQfnumber(res, "conname");
8519 i_consrc = PQfnumber(res, "consrc");
8520 i_convalidated = PQfnumber(res, "convalidated");
8521 i_contype = PQfnumber(res, "contype");
8522
8523 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8524 tyinfo->domChecks = constrinfo;
8525
8526 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8527 for (int i = 0, j = 0; i < ntups; i++)
8528 {
8529 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8530 char contype = (PQgetvalue(res, i, i_contype))[0];
8531 ConstraintInfo *constraint;
8532
8533 if (contype == CONSTRAINT_CHECK)
8534 {
8535 constraint = &constrinfo[j++];
8536 tyinfo->nDomChecks++;
8537 }
8538 else
8539 {
8540 Assert(contype == CONSTRAINT_NOTNULL);
8541 Assert(tyinfo->notnull == NULL);
8542 /* use last item in array for the not-null constraint */
8543 tyinfo->notnull = &(constrinfo[ntups - 1]);
8544 constraint = tyinfo->notnull;
8545 }
8546
8547 constraint->dobj.objType = DO_CONSTRAINT;
8548 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8549 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8550 AssignDumpId(&(constraint->dobj));
8551 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8552 constraint->dobj.namespace = tyinfo->dobj.namespace;
8553 constraint->contable = NULL;
8554 constraint->condomain = tyinfo;
8555 constraint->contype = contype;
8556 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8557 constraint->confrelid = InvalidOid;
8558 constraint->conindex = 0;
8559 constraint->condeferrable = false;
8560 constraint->condeferred = false;
8561 constraint->conislocal = true;
8562
8563 constraint->separate = !validated;
8564
8565 /*
8566 * Make the domain depend on the constraint, ensuring it won't be
8567 * output till any constraint dependencies are OK. If the constraint
8568 * has not been validated, it's going to be dumped after the domain
8569 * anyway, so this doesn't matter.
8570 */
8571 if (validated)
8572 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8573 }
8574
8575 PQclear(res);
8576
8577 destroyPQExpBuffer(query);
8578}
8579
8580/*
8581 * getRules
8582 * get basic information about every rule in the system
8583 */
8584void
8586{
8587 PGresult *res;
8588 int ntups;
8589 int i;
8591 RuleInfo *ruleinfo;
8592 int i_tableoid;
8593 int i_oid;
8594 int i_rulename;
8595 int i_ruletable;
8596 int i_ev_type;
8597 int i_is_instead;
8598 int i_ev_enabled;
8599
8600 appendPQExpBufferStr(query, "SELECT "
8601 "tableoid, oid, rulename, "
8602 "ev_class AS ruletable, ev_type, is_instead, "
8603 "ev_enabled "
8604 "FROM pg_rewrite "
8605 "ORDER BY oid");
8606
8607 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8608
8609 ntups = PQntuples(res);
8610
8611 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8612
8613 i_tableoid = PQfnumber(res, "tableoid");
8614 i_oid = PQfnumber(res, "oid");
8615 i_rulename = PQfnumber(res, "rulename");
8616 i_ruletable = PQfnumber(res, "ruletable");
8617 i_ev_type = PQfnumber(res, "ev_type");
8618 i_is_instead = PQfnumber(res, "is_instead");
8619 i_ev_enabled = PQfnumber(res, "ev_enabled");
8620
8621 for (i = 0; i < ntups; i++)
8622 {
8623 Oid ruletableoid;
8624
8625 ruleinfo[i].dobj.objType = DO_RULE;
8626 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8627 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8628 AssignDumpId(&ruleinfo[i].dobj);
8629 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8630 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8631 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8632 if (ruleinfo[i].ruletable == NULL)
8633 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8634 ruletableoid, ruleinfo[i].dobj.catId.oid);
8635 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8636 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8637 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8638 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8639 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8640 if (ruleinfo[i].ruletable)
8641 {
8642 /*
8643 * If the table is a view or materialized view, force its ON
8644 * SELECT rule to be sorted before the view itself --- this
8645 * ensures that any dependencies for the rule affect the table's
8646 * positioning. Other rules are forced to appear after their
8647 * table.
8648 */
8649 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8650 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8651 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8652 {
8653 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8654 ruleinfo[i].dobj.dumpId);
8655 /* We'll merge the rule into CREATE VIEW, if possible */
8656 ruleinfo[i].separate = false;
8657 }
8658 else
8659 {
8660 addObjectDependency(&ruleinfo[i].dobj,
8661 ruleinfo[i].ruletable->dobj.dumpId);
8662 ruleinfo[i].separate = true;
8663 }
8664 }
8665 else
8666 ruleinfo[i].separate = true;
8667 }
8668
8669 PQclear(res);
8670
8671 destroyPQExpBuffer(query);
8672}
8673
8674/*
8675 * getTriggers
8676 * get information about every trigger on a dumpable table
8677 *
8678 * Note: trigger data is not returned directly to the caller, but it
8679 * does get entered into the DumpableObject tables.
8680 */
8681void
8682getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8683{
8685 PQExpBuffer tbloids = createPQExpBuffer();
8686 PGresult *res;
8687 int ntups;
8688 int curtblindx;
8689 TriggerInfo *tginfo;
8690 int i_tableoid,
8691 i_oid,
8692 i_tgrelid,
8693 i_tgname,
8694 i_tgenabled,
8695 i_tgispartition,
8696 i_tgdef;
8697
8698 /*
8699 * We want to perform just one query against pg_trigger. However, we
8700 * mustn't try to select every row of the catalog and then sort it out on
8701 * the client side, because some of the server-side functions we need
8702 * would be unsafe to apply to tables we don't have lock on. Hence, we
8703 * build an array of the OIDs of tables we care about (and now have lock
8704 * on!), and use a WHERE clause to constrain which rows are selected.
8705 */
8706 appendPQExpBufferChar(tbloids, '{');
8707 for (int i = 0; i < numTables; i++)
8708 {
8709 TableInfo *tbinfo = &tblinfo[i];
8710
8711 if (!tbinfo->hastriggers ||
8712 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8713 continue;
8714
8715 /* OK, we need info for this table */
8716 if (tbloids->len > 1) /* do we have more than the '{'? */
8717 appendPQExpBufferChar(tbloids, ',');
8718 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8719 }
8720 appendPQExpBufferChar(tbloids, '}');
8721
8722 if (fout->remoteVersion >= 150000)
8723 {
8724 /*
8725 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8726 * result in non-forward-compatible dumps of WHEN clauses due to
8727 * under-parenthesization.
8728 *
8729 * NB: We need to see partition triggers in case the tgenabled flag
8730 * has been changed from the parent.
8731 */
8732 appendPQExpBuffer(query,
8733 "SELECT t.tgrelid, t.tgname, "
8734 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8735 "t.tgenabled, t.tableoid, t.oid, "
8736 "t.tgparentid <> 0 AS tgispartition\n"
8737 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8738 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8739 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8740 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8741 "OR t.tgenabled != u.tgenabled) "
8742 "ORDER BY t.tgrelid, t.tgname",
8743 tbloids->data);
8744 }
8745 else if (fout->remoteVersion >= 130000)
8746 {
8747 /*
8748 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8749 * result in non-forward-compatible dumps of WHEN clauses due to
8750 * under-parenthesization.
8751 *
8752 * NB: We need to see tgisinternal triggers in partitions, in case the
8753 * tgenabled flag has been changed from the parent.
8754 */
8755 appendPQExpBuffer(query,
8756 "SELECT t.tgrelid, t.tgname, "
8757 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8758 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8759 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8760 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8761 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8762 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8763 "ORDER BY t.tgrelid, t.tgname",
8764 tbloids->data);
8765 }
8766 else if (fout->remoteVersion >= 110000)
8767 {
8768 /*
8769 * NB: We need to see tgisinternal triggers in partitions, in case the
8770 * tgenabled flag has been changed from the parent. No tgparentid in
8771 * version 11-12, so we have to match them via pg_depend.
8772 *
8773 * See above about pretty=true in pg_get_triggerdef.
8774 */
8775 appendPQExpBuffer(query,
8776 "SELECT t.tgrelid, t.tgname, "
8777 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8778 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8779 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8780 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8781 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8782 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8783 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8784 " d.objid = t.oid "
8785 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8786 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8787 "ORDER BY t.tgrelid, t.tgname",
8788 tbloids->data);
8789 }
8790 else
8791 {
8792 /* See above about pretty=true in pg_get_triggerdef */
8793 appendPQExpBuffer(query,
8794 "SELECT t.tgrelid, t.tgname, "
8795 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8796 "t.tgenabled, false as tgispartition, "
8797 "t.tableoid, t.oid "
8798 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8799 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8800 "WHERE NOT tgisinternal "
8801 "ORDER BY t.tgrelid, t.tgname",
8802 tbloids->data);
8803 }
8804
8805 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8806
8807 ntups = PQntuples(res);
8808
8809 i_tableoid = PQfnumber(res, "tableoid");
8810 i_oid = PQfnumber(res, "oid");
8811 i_tgrelid = PQfnumber(res, "tgrelid");
8812 i_tgname = PQfnumber(res, "tgname");
8813 i_tgenabled = PQfnumber(res, "tgenabled");
8814 i_tgispartition = PQfnumber(res, "tgispartition");
8815 i_tgdef = PQfnumber(res, "tgdef");
8816
8817 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8818
8819 /*
8820 * Outer loop iterates once per table, not once per row. Incrementing of
8821 * j is handled by the inner loop.
8822 */
8823 curtblindx = -1;
8824 for (int j = 0; j < ntups;)
8825 {
8826 Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8827 TableInfo *tbinfo = NULL;
8828 int numtrigs;
8829
8830 /* Count rows for this table */
8831 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8832 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8833 break;
8834
8835 /*
8836 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8837 * order.
8838 */
8839 while (++curtblindx < numTables)
8840 {
8841 tbinfo = &tblinfo[curtblindx];
8842 if (tbinfo->dobj.catId.oid == tgrelid)
8843 break;
8844 }
8845 if (curtblindx >= numTables)
8846 pg_fatal("unrecognized table OID %u", tgrelid);
8847
8848 /* Save data for this table */
8849 tbinfo->triggers = tginfo + j;
8850 tbinfo->numTriggers = numtrigs;
8851
8852 for (int c = 0; c < numtrigs; c++, j++)
8853 {
8854 tginfo[j].dobj.objType = DO_TRIGGER;
8855 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8856 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8857 AssignDumpId(&tginfo[j].dobj);
8858 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8859 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8860 tginfo[j].tgtable = tbinfo;
8861 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8862 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8863 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8864 }
8865 }
8866
8867 PQclear(res);
8868
8869 destroyPQExpBuffer(query);
8870 destroyPQExpBuffer(tbloids);
8871}
8872
8873/*
8874 * getEventTriggers
8875 * get information about event triggers
8876 */
8877void
8879{
8880 int i;
8881 PQExpBuffer query;
8882 PGresult *res;
8883 EventTriggerInfo *evtinfo;
8884 int i_tableoid,
8885 i_oid,
8886 i_evtname,
8887 i_evtevent,
8888 i_evtowner,
8889 i_evttags,
8890 i_evtfname,
8891 i_evtenabled;
8892 int ntups;
8893
8894 /* Before 9.3, there are no event triggers */
8895 if (fout->remoteVersion < 90300)
8896 return;
8897
8898 query = createPQExpBuffer();
8899
8901 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8902 "evtevent, evtowner, "
8903 "array_to_string(array("
8904 "select quote_literal(x) "
8905 " from unnest(evttags) as t(x)), ', ') as evttags, "
8906 "e.evtfoid::regproc as evtfname "
8907 "FROM pg_event_trigger e "
8908 "ORDER BY e.oid");
8909
8910 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8911
8912 ntups = PQntuples(res);
8913
8914 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8915
8916 i_tableoid = PQfnumber(res, "tableoid");
8917 i_oid = PQfnumber(res, "oid");
8918 i_evtname = PQfnumber(res, "evtname");
8919 i_evtevent = PQfnumber(res, "evtevent");
8920 i_evtowner = PQfnumber(res, "evtowner");
8921 i_evttags = PQfnumber(res, "evttags");
8922 i_evtfname = PQfnumber(res, "evtfname");
8923 i_evtenabled = PQfnumber(res, "evtenabled");
8924
8925 for (i = 0; i < ntups; i++)
8926 {
8927 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8928 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8929 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8930 AssignDumpId(&evtinfo[i].dobj);
8931 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8932 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8933 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8934 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8935 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8936 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8937 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8938
8939 /* Decide whether we want to dump it */
8940 selectDumpableObject(&(evtinfo[i].dobj), fout);
8941 }
8942
8943 PQclear(res);
8944
8945 destroyPQExpBuffer(query);
8946}
8947
8948/*
8949 * getProcLangs
8950 * get basic information about every procedural language in the system
8951 *
8952 * NB: this must run after getFuncs() because we assume we can do
8953 * findFuncByOid().
8954 */
8955void
8957{
8958 PGresult *res;
8959 int ntups;
8960 int i;
8962 ProcLangInfo *planginfo;
8963 int i_tableoid;
8964 int i_oid;
8965 int i_lanname;
8966 int i_lanpltrusted;
8967 int i_lanplcallfoid;
8968 int i_laninline;
8969 int i_lanvalidator;
8970 int i_lanacl;
8971 int i_acldefault;
8972 int i_lanowner;
8973
8974 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8975 "lanname, lanpltrusted, lanplcallfoid, "
8976 "laninline, lanvalidator, "
8977 "lanacl, "
8978 "acldefault('l', lanowner) AS acldefault, "
8979 "lanowner "
8980 "FROM pg_language "
8981 "WHERE lanispl "
8982 "ORDER BY oid");
8983
8984 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8985
8986 ntups = PQntuples(res);
8987
8988 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8989
8990 i_tableoid = PQfnumber(res, "tableoid");
8991 i_oid = PQfnumber(res, "oid");
8992 i_lanname = PQfnumber(res, "lanname");
8993 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8994 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8995 i_laninline = PQfnumber(res, "laninline");
8996 i_lanvalidator = PQfnumber(res, "lanvalidator");
8997 i_lanacl = PQfnumber(res, "lanacl");
8998 i_acldefault = PQfnumber(res, "acldefault");
8999 i_lanowner = PQfnumber(res, "lanowner");
9000
9001 for (i = 0; i < ntups; i++)
9002 {
9003 planginfo[i].dobj.objType = DO_PROCLANG;
9004 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9005 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9006 AssignDumpId(&planginfo[i].dobj);
9007
9008 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9009 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9010 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9011 planginfo[i].dacl.privtype = 0;
9012 planginfo[i].dacl.initprivs = NULL;
9013 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9014 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9015 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9016 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9017 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9018
9019 /* Decide whether we want to dump it */
9020 selectDumpableProcLang(&(planginfo[i]), fout);
9021
9022 /* Mark whether language has an ACL */
9023 if (!PQgetisnull(res, i, i_lanacl))
9024 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9025 }
9026
9027 PQclear(res);
9028
9029 destroyPQExpBuffer(query);
9030}
9031
9032/*
9033 * getCasts
9034 * get basic information about most casts in the system
9035 *
9036 * Skip casts from a range to its multirange, since we'll create those
9037 * automatically.
9038 */
9039void
9041{
9042 PGresult *res;
9043 int ntups;
9044 int i;
9046 CastInfo *castinfo;
9047 int i_tableoid;
9048 int i_oid;
9049 int i_castsource;
9050 int i_casttarget;
9051 int i_castfunc;
9052 int i_castcontext;
9053 int i_castmethod;
9054
9055 if (fout->remoteVersion >= 140000)
9056 {
9057 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9058 "castsource, casttarget, castfunc, castcontext, "
9059 "castmethod "
9060 "FROM pg_cast c "
9061 "WHERE NOT EXISTS ( "
9062 "SELECT 1 FROM pg_range r "
9063 "WHERE c.castsource = r.rngtypid "
9064 "AND c.casttarget = r.rngmultitypid "
9065 ") "
9066 "ORDER BY 3,4");
9067 }
9068 else
9069 {
9070 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9071 "castsource, casttarget, castfunc, castcontext, "
9072 "castmethod "
9073 "FROM pg_cast ORDER BY 3,4");
9074 }
9075
9076 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9077
9078 ntups = PQntuples(res);
9079
9080 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9081
9082 i_tableoid = PQfnumber(res, "tableoid");
9083 i_oid = PQfnumber(res, "oid");
9084 i_castsource = PQfnumber(res, "castsource");
9085 i_casttarget = PQfnumber(res, "casttarget");
9086 i_castfunc = PQfnumber(res, "castfunc");
9087 i_castcontext = PQfnumber(res, "castcontext");
9088 i_castmethod = PQfnumber(res, "castmethod");
9089
9090 for (i = 0; i < ntups; i++)
9091 {
9092 PQExpBufferData namebuf;
9093 TypeInfo *sTypeInfo;
9094 TypeInfo *tTypeInfo;
9095
9096 castinfo[i].dobj.objType = DO_CAST;
9097 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9098 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9099 AssignDumpId(&castinfo[i].dobj);
9100 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9101 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9102 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9103 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9104 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9105
9106 /*
9107 * Try to name cast as concatenation of typnames. This is only used
9108 * for purposes of sorting. If we fail to find either type, the name
9109 * will be an empty string.
9110 */
9111 initPQExpBuffer(&namebuf);
9112 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9113 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9114 if (sTypeInfo && tTypeInfo)
9115 appendPQExpBuffer(&namebuf, "%s %s",
9116 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9117 castinfo[i].dobj.name = namebuf.data;
9118
9119 /* Decide whether we want to dump it */
9120 selectDumpableCast(&(castinfo[i]), fout);
9121 }
9122
9123 PQclear(res);
9124
9125 destroyPQExpBuffer(query);
9126}
9127
9128static char *
9130{
9131 PQExpBuffer query;
9132 PGresult *res;
9133 char *lanname;
9134
9135 query = createPQExpBuffer();
9136 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9137 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9138 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9139 destroyPQExpBuffer(query);
9140 PQclear(res);
9141
9142 return lanname;
9143}
9144
9145/*
9146 * getTransforms
9147 * get basic information about every transform in the system
9148 */
9149void
9151{
9152 PGresult *res;
9153 int ntups;
9154 int i;
9155 PQExpBuffer query;
9156 TransformInfo *transforminfo;
9157 int i_tableoid;
9158 int i_oid;
9159 int i_trftype;
9160 int i_trflang;
9161 int i_trffromsql;
9162 int i_trftosql;
9163
9164 /* Transforms didn't exist pre-9.5 */
9165 if (fout->remoteVersion < 90500)
9166 return;
9167
9168 query = createPQExpBuffer();
9169
9170 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9171 "trftype, trflang, trffromsql::oid, trftosql::oid "
9172 "FROM pg_transform "
9173 "ORDER BY 3,4");
9174
9175 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9176
9177 ntups = PQntuples(res);
9178
9179 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9180
9181 i_tableoid = PQfnumber(res, "tableoid");
9182 i_oid = PQfnumber(res, "oid");
9183 i_trftype = PQfnumber(res, "trftype");
9184 i_trflang = PQfnumber(res, "trflang");
9185 i_trffromsql = PQfnumber(res, "trffromsql");
9186 i_trftosql = PQfnumber(res, "trftosql");
9187
9188 for (i = 0; i < ntups; i++)
9189 {
9190 PQExpBufferData namebuf;
9191 TypeInfo *typeInfo;
9192 char *lanname;
9193
9194 transforminfo[i].dobj.objType = DO_TRANSFORM;
9195 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9196 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9197 AssignDumpId(&transforminfo[i].dobj);
9198 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9199 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9200 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9201 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9202
9203 /*
9204 * Try to name transform as concatenation of type and language name.
9205 * This is only used for purposes of sorting. If we fail to find
9206 * either, the name will be an empty string.
9207 */
9208 initPQExpBuffer(&namebuf);
9209 typeInfo = findTypeByOid(transforminfo[i].trftype);
9210 lanname = get_language_name(fout, transforminfo[i].trflang);
9211 if (typeInfo && lanname)
9212 appendPQExpBuffer(&namebuf, "%s %s",
9213 typeInfo->dobj.name, lanname);
9214 transforminfo[i].dobj.name = namebuf.data;
9215 free(lanname);
9216
9217 /* Decide whether we want to dump it */
9218 selectDumpableObject(&(transforminfo[i].dobj), fout);
9219 }
9220
9221 PQclear(res);
9222
9223 destroyPQExpBuffer(query);
9224}
9225
9226/*
9227 * getTableAttrs -
9228 * for each interesting table, read info about its attributes
9229 * (names, types, default values, CHECK constraints, etc)
9230 *
9231 * modifies tblinfo
9232 */
9233void
9234getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9235{
9236 DumpOptions *dopt = fout->dopt;
9238 PQExpBuffer tbloids = createPQExpBuffer();
9239 PQExpBuffer checkoids = createPQExpBuffer();
9240 PQExpBuffer invalidnotnulloids = NULL;
9241 PGresult *res;
9242 int ntups;
9243 int curtblindx;
9244 int i_attrelid;
9245 int i_attnum;
9246 int i_attname;
9247 int i_atttypname;
9248 int i_attstattarget;
9249 int i_attstorage;
9250 int i_typstorage;
9251 int i_attidentity;
9252 int i_attgenerated;
9253 int i_attisdropped;
9254 int i_attlen;
9255 int i_attalign;
9256 int i_attislocal;
9257 int i_notnull_name;
9258 int i_notnull_comment;
9259 int i_notnull_noinherit;
9260 int i_notnull_islocal;
9261 int i_notnull_invalidoid;
9262 int i_attoptions;
9263 int i_attcollation;
9264 int i_attcompression;
9265 int i_attfdwoptions;
9266 int i_attmissingval;
9267 int i_atthasdef;
9268
9269 /*
9270 * We want to perform just one query against pg_attribute, and then just
9271 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9272 * (for CHECK constraints and for NOT NULL constraints). However, we
9273 * mustn't try to select every row of those catalogs and then sort it out
9274 * on the client side, because some of the server-side functions we need
9275 * would be unsafe to apply to tables we don't have lock on. Hence, we
9276 * build an array of the OIDs of tables we care about (and now have lock
9277 * on!), and use a WHERE clause to constrain which rows are selected.
9278 */
9279 appendPQExpBufferChar(tbloids, '{');
9280 appendPQExpBufferChar(checkoids, '{');
9281 for (int i = 0; i < numTables; i++)
9282 {
9283 TableInfo *tbinfo = &tblinfo[i];
9284
9285 /* Don't bother to collect info for sequences */
9286 if (tbinfo->relkind == RELKIND_SEQUENCE)
9287 continue;
9288
9289 /*
9290 * Don't bother with uninteresting tables, either. For binary
9291 * upgrades, this is bypassed for pg_largeobject_metadata and
9292 * pg_shdepend so that the columns names are collected for the
9293 * corresponding COPY commands. Restoring the data for those catalogs
9294 * is faster than restoring the equivalent set of large object
9295 * commands. We can only do this for upgrades from v12 and newer; in
9296 * older versions, pg_largeobject_metadata was created WITH OIDS, so
9297 * the OID column is hidden and won't be dumped.
9298 */
9299 if (!tbinfo->interesting &&
9300 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9301 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9302 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9303 continue;
9304
9305 /* OK, we need info for this table */
9306 if (tbloids->len > 1) /* do we have more than the '{'? */
9307 appendPQExpBufferChar(tbloids, ',');
9308 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9309
9310 if (tbinfo->ncheck > 0)
9311 {
9312 /* Also make a list of the ones with check constraints */
9313 if (checkoids->len > 1) /* do we have more than the '{'? */
9314 appendPQExpBufferChar(checkoids, ',');
9315 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9316 }
9317 }
9318 appendPQExpBufferChar(tbloids, '}');
9319 appendPQExpBufferChar(checkoids, '}');
9320
9321 /*
9322 * Find all the user attributes and their types.
9323 *
9324 * Since we only want to dump COLLATE clauses for attributes whose
9325 * collation is different from their type's default, we use a CASE here to
9326 * suppress uninteresting attcollations cheaply.
9327 */
9329 "SELECT\n"
9330 "a.attrelid,\n"
9331 "a.attnum,\n"
9332 "a.attname,\n"
9333 "a.attstattarget,\n"
9334 "a.attstorage,\n"
9335 "t.typstorage,\n"
9336 "a.atthasdef,\n"
9337 "a.attisdropped,\n"
9338 "a.attlen,\n"
9339 "a.attalign,\n"
9340 "a.attislocal,\n"
9341 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9342 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9343 "CASE WHEN a.attcollation <> t.typcollation "
9344 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9345 "pg_catalog.array_to_string(ARRAY("
9346 "SELECT pg_catalog.quote_ident(option_name) || "
9347 "' ' || pg_catalog.quote_literal(option_value) "
9348 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9349 "ORDER BY option_name"
9350 "), E',\n ') AS attfdwoptions,\n");
9351
9352 /*
9353 * Find out any NOT NULL markings for each column. In 18 and up we read
9354 * pg_constraint to obtain the constraint name, and for valid constraints
9355 * also pg_description to obtain its comment. notnull_noinherit is set
9356 * according to the NO INHERIT property. For versions prior to 18, we
9357 * store an empty string as the name when a constraint is marked as
9358 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9359 * without a name); also, such cases are never NO INHERIT.
9360 *
9361 * For invalid constraints, we need to store their OIDs for processing
9362 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9363 * is invalid, and NULL otherwise. Their comments are handled not here
9364 * but by collectComments, because they're their own dumpable object.
9365 *
9366 * We track in notnull_islocal whether the constraint was defined directly
9367 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9368 * might modify this later.
9369 */
9370 if (fout->remoteVersion >= 180000)
9372 "co.conname AS notnull_name,\n"
9373 "CASE WHEN co.convalidated THEN pt.description"
9374 " ELSE NULL END AS notnull_comment,\n"
9375 "CASE WHEN NOT co.convalidated THEN co.oid "
9376 "ELSE NULL END AS notnull_invalidoid,\n"
9377 "co.connoinherit AS notnull_noinherit,\n"
9378 "co.conislocal AS notnull_islocal,\n");
9379 else
9381 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9382 "NULL AS notnull_comment,\n"
9383 "NULL AS notnull_invalidoid,\n"
9384 "false AS notnull_noinherit,\n"
9385 "CASE WHEN a.attislocal THEN true\n"
9386 " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9387 " ELSE false\n"
9388 "END AS notnull_islocal,\n");
9389
9390 if (fout->remoteVersion >= 140000)
9392 "a.attcompression AS attcompression,\n");
9393 else
9395 "'' AS attcompression,\n");
9396
9397 if (fout->remoteVersion >= 100000)
9399 "a.attidentity,\n");
9400 else
9402 "'' AS attidentity,\n");
9403
9404 if (fout->remoteVersion >= 110000)
9406 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9407 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9408 else
9410 "NULL AS attmissingval,\n");
9411
9412 if (fout->remoteVersion >= 120000)
9414 "a.attgenerated\n");
9415 else
9417 "'' AS attgenerated\n");
9418
9419 /* need left join to pg_type to not fail on dropped columns ... */
9421 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9422 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9423 "LEFT JOIN pg_catalog.pg_type t "
9424 "ON (a.atttypid = t.oid)\n",
9425 tbloids->data);
9426
9427 /*
9428 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9429 * entries and pg_description to get their comments.
9430 */
9431 if (fout->remoteVersion >= 180000)
9433 " LEFT JOIN pg_catalog.pg_constraint co ON "
9434 "(a.attrelid = co.conrelid\n"
9435 " AND co.contype = 'n' AND "
9436 "co.conkey = array[a.attnum])\n"
9437 " LEFT JOIN pg_catalog.pg_description pt ON "
9438 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9439
9441 "WHERE a.attnum > 0::pg_catalog.int2\n"
9442 "ORDER BY a.attrelid, a.attnum");
9443
9444 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9445
9446 ntups = PQntuples(res);
9447
9448 i_attrelid = PQfnumber(res, "attrelid");
9449 i_attnum = PQfnumber(res, "attnum");
9450 i_attname = PQfnumber(res, "attname");
9451 i_atttypname = PQfnumber(res, "atttypname");
9452 i_attstattarget = PQfnumber(res, "attstattarget");
9453 i_attstorage = PQfnumber(res, "attstorage");
9454 i_typstorage = PQfnumber(res, "typstorage");
9455 i_attidentity = PQfnumber(res, "attidentity");
9456 i_attgenerated = PQfnumber(res, "attgenerated");
9457 i_attisdropped = PQfnumber(res, "attisdropped");
9458 i_attlen = PQfnumber(res, "attlen");
9459 i_attalign = PQfnumber(res, "attalign");
9460 i_attislocal = PQfnumber(res, "attislocal");
9461 i_notnull_name = PQfnumber(res, "notnull_name");
9462 i_notnull_comment = PQfnumber(res, "notnull_comment");
9463 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9464 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9465 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9466 i_attoptions = PQfnumber(res, "attoptions");
9467 i_attcollation = PQfnumber(res, "attcollation");
9468 i_attcompression = PQfnumber(res, "attcompression");
9469 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9470 i_attmissingval = PQfnumber(res, "attmissingval");
9471 i_atthasdef = PQfnumber(res, "atthasdef");
9472
9473 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9474 resetPQExpBuffer(tbloids);
9475 appendPQExpBufferChar(tbloids, '{');
9476
9477 /*
9478 * Outer loop iterates once per table, not once per row. Incrementing of
9479 * r is handled by the inner loop.
9480 */
9481 curtblindx = -1;
9482 for (int r = 0; r < ntups;)
9483 {
9484 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9485 TableInfo *tbinfo = NULL;
9486 int numatts;
9487 bool hasdefaults;
9488
9489 /* Count rows for this table */
9490 for (numatts = 1; numatts < ntups - r; numatts++)
9491 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9492 break;
9493
9494 /*
9495 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9496 * order.
9497 */
9498 while (++curtblindx < numTables)
9499 {
9500 tbinfo = &tblinfo[curtblindx];
9501 if (tbinfo->dobj.catId.oid == attrelid)
9502 break;
9503 }
9504 if (curtblindx >= numTables)
9505 pg_fatal("unrecognized table OID %u", attrelid);
9506 /* cross-check that we only got requested tables */
9507 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9508 (!tbinfo->interesting &&
9509 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9510 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9511 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9512 pg_fatal("unexpected column data for table \"%s\"",
9513 tbinfo->dobj.name);
9514
9515 /* Save data for this table */
9516 tbinfo->numatts = numatts;
9517 tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9518 tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9519 tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9520 tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9521 tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9522 tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9523 tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9524 tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9525 tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9526 tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9527 tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9528 tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9529 tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9530 tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9531 tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9532 tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9533 tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9534 tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9535 tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9536 tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9537 tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9538 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9539 hasdefaults = false;
9540
9541 for (int j = 0; j < numatts; j++, r++)
9542 {
9543 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9544 pg_fatal("invalid column numbering in table \"%s\"",
9545 tbinfo->dobj.name);
9546 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9547 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9548 if (PQgetisnull(res, r, i_attstattarget))
9549 tbinfo->attstattarget[j] = -1;
9550 else
9551 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9552 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9553 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9554 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9555 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9556 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9557 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9558 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9559 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9560 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9561
9562 /* Handle not-null constraint name and flags */
9563 determineNotNullFlags(fout, res, r,
9564 tbinfo, j,
9565 i_notnull_name,
9566 i_notnull_comment,
9567 i_notnull_invalidoid,
9568 i_notnull_noinherit,
9569 i_notnull_islocal,
9570 &invalidnotnulloids);
9571
9572 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9573 NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9574 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9575 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9576 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9577 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9578 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9579 tbinfo->attrdefs[j] = NULL; /* fix below */
9580 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9581 hasdefaults = true;
9582 }
9583
9584 if (hasdefaults)
9585 {
9586 /* Collect OIDs of interesting tables that have defaults */
9587 if (tbloids->len > 1) /* do we have more than the '{'? */
9588 appendPQExpBufferChar(tbloids, ',');
9589 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9590 }
9591 }
9592
9593 /* If invalidnotnulloids has any data, finalize it */
9594 if (invalidnotnulloids != NULL)
9595 appendPQExpBufferChar(invalidnotnulloids, '}');
9596
9597 PQclear(res);
9598
9599 /*
9600 * Now get info about column defaults. This is skipped for a data-only
9601 * dump, as it is only needed for table schemas.
9602 */
9603 if (dopt->dumpSchema && tbloids->len > 1)
9604 {
9605 AttrDefInfo *attrdefs;
9606 int numDefaults;
9607 TableInfo *tbinfo = NULL;
9608
9609 pg_log_info("finding table default expressions");
9610
9611 appendPQExpBufferChar(tbloids, '}');
9612
9613 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9614 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9615 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9616 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9617 "ORDER BY a.adrelid, a.adnum",
9618 tbloids->data);
9619
9620 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9621
9622 numDefaults = PQntuples(res);
9623 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9624
9625 curtblindx = -1;
9626 for (int j = 0; j < numDefaults; j++)
9627 {
9628 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9629 Oid adoid = atooid(PQgetvalue(res, j, 1));
9630 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9631 int adnum = atoi(PQgetvalue(res, j, 3));
9632 char *adsrc = PQgetvalue(res, j, 4);
9633
9634 /*
9635 * Locate the associated TableInfo; we rely on tblinfo[] being in
9636 * OID order.
9637 */
9638 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9639 {
9640 while (++curtblindx < numTables)
9641 {
9642 tbinfo = &tblinfo[curtblindx];
9643 if (tbinfo->dobj.catId.oid == adrelid)
9644 break;
9645 }
9646 if (curtblindx >= numTables)
9647 pg_fatal("unrecognized table OID %u", adrelid);
9648 }
9649
9650 if (adnum <= 0 || adnum > tbinfo->numatts)
9651 pg_fatal("invalid adnum value %d for table \"%s\"",
9652 adnum, tbinfo->dobj.name);
9653
9654 /*
9655 * dropped columns shouldn't have defaults, but just in case,
9656 * ignore 'em
9657 */
9658 if (tbinfo->attisdropped[adnum - 1])
9659 continue;
9660
9661 attrdefs[j].dobj.objType = DO_ATTRDEF;
9662 attrdefs[j].dobj.catId.tableoid = adtableoid;
9663 attrdefs[j].dobj.catId.oid = adoid;
9664 AssignDumpId(&attrdefs[j].dobj);
9665 attrdefs[j].adtable = tbinfo;
9666 attrdefs[j].adnum = adnum;
9667 attrdefs[j].adef_expr = pg_strdup(adsrc);
9668
9669 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9670 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9671
9672 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9673
9674 /*
9675 * Figure out whether the default/generation expression should be
9676 * dumped as part of the main CREATE TABLE (or similar) command or
9677 * as a separate ALTER TABLE (or similar) command. The preference
9678 * is to put it into the CREATE command, but in some cases that's
9679 * not possible.
9680 */
9681 if (tbinfo->attgenerated[adnum - 1])
9682 {
9683 /*
9684 * Column generation expressions cannot be dumped separately,
9685 * because there is no syntax for it. By setting separate to
9686 * false here we prevent the "default" from being processed as
9687 * its own dumpable object. Later, flagInhAttrs() will mark
9688 * it as not to be dumped at all, if possible (that is, if it
9689 * can be inherited from a parent).
9690 */
9691 attrdefs[j].separate = false;
9692 }
9693 else if (tbinfo->relkind == RELKIND_VIEW)
9694 {
9695 /*
9696 * Defaults on a VIEW must always be dumped as separate ALTER
9697 * TABLE commands.
9698 */
9699 attrdefs[j].separate = true;
9700 }
9701 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9702 {
9703 /* column will be suppressed, print default separately */
9704 attrdefs[j].separate = true;
9705 }
9706 else
9707 {
9708 attrdefs[j].separate = false;
9709 }
9710
9711 if (!attrdefs[j].separate)
9712 {
9713 /*
9714 * Mark the default as needing to appear before the table, so
9715 * that any dependencies it has must be emitted before the
9716 * CREATE TABLE. If this is not possible, we'll change to
9717 * "separate" mode while sorting dependencies.
9718 */
9719 addObjectDependency(&tbinfo->dobj,
9720 attrdefs[j].dobj.dumpId);
9721 }
9722
9723 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9724 }
9725
9726 PQclear(res);
9727 }
9728
9729 /*
9730 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9731 * data-only dump, as it is only needed for table schemas.
9732 */
9733 if (dopt->dumpSchema && invalidnotnulloids)
9734 {
9735 ConstraintInfo *constrs;
9736 int numConstrs;
9737 int i_tableoid;
9738 int i_oid;
9739 int i_conrelid;
9740 int i_conname;
9741 int i_consrc;
9742 int i_conislocal;
9743
9744 pg_log_info("finding invalid not-null constraints");
9745
9748 "SELECT c.tableoid, c.oid, conrelid, conname, "
9749 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9750 "conislocal, convalidated "
9751 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9752 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9753 "ORDER BY c.conrelid, c.conname",
9754 invalidnotnulloids->data);
9755
9756 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9757
9758 numConstrs = PQntuples(res);
9759 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9760
9761 i_tableoid = PQfnumber(res, "tableoid");
9762 i_oid = PQfnumber(res, "oid");
9763 i_conrelid = PQfnumber(res, "conrelid");
9764 i_conname = PQfnumber(res, "conname");
9765 i_consrc = PQfnumber(res, "consrc");
9766 i_conislocal = PQfnumber(res, "conislocal");
9767
9768 /* As above, this loop iterates once per table, not once per row */
9769 curtblindx = -1;
9770 for (int j = 0; j < numConstrs;)
9771 {
9772 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9773 TableInfo *tbinfo = NULL;
9774 int numcons;
9775
9776 /* Count rows for this table */
9777 for (numcons = 1; numcons < numConstrs - j; numcons++)
9778 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9779 break;
9780
9781 /*
9782 * Locate the associated TableInfo; we rely on tblinfo[] being in
9783 * OID order.
9784 */
9785 while (++curtblindx < numTables)
9786 {
9787 tbinfo = &tblinfo[curtblindx];
9788 if (tbinfo->dobj.catId.oid == conrelid)
9789 break;
9790 }
9791 if (curtblindx >= numTables)
9792 pg_fatal("unrecognized table OID %u", conrelid);
9793
9794 for (int c = 0; c < numcons; c++, j++)
9795 {
9796 constrs[j].dobj.objType = DO_CONSTRAINT;
9797 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9798 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9799 AssignDumpId(&constrs[j].dobj);
9800 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9801 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9802 constrs[j].contable = tbinfo;
9803 constrs[j].condomain = NULL;
9804 constrs[j].contype = 'n';
9805 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9806 constrs[j].confrelid = InvalidOid;
9807 constrs[j].conindex = 0;
9808 constrs[j].condeferrable = false;
9809 constrs[j].condeferred = false;
9810 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9811
9812 /*
9813 * All invalid not-null constraints must be dumped separately,
9814 * because CREATE TABLE would not create them as invalid, and
9815 * also because they must be created after potentially
9816 * violating data has been loaded.
9817 */
9818 constrs[j].separate = true;
9819
9820 constrs[j].dobj.dump = tbinfo->dobj.dump;
9821 }
9822 }
9823 PQclear(res);
9824 }
9825
9826 /*
9827 * Get info about table CHECK constraints. This is skipped for a
9828 * data-only dump, as it is only needed for table schemas.
9829 */
9830 if (dopt->dumpSchema && checkoids->len > 2)
9831 {
9832 ConstraintInfo *constrs;
9833 int numConstrs;
9834 int i_tableoid;
9835 int i_oid;
9836 int i_conrelid;
9837 int i_conname;
9838 int i_consrc;
9839 int i_conislocal;
9840 int i_convalidated;
9841
9842 pg_log_info("finding table check constraints");
9843
9846 "SELECT c.tableoid, c.oid, conrelid, conname, "
9847 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9848 "conislocal, convalidated "
9849 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9850 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9851 "WHERE contype = 'c' "
9852 "ORDER BY c.conrelid, c.conname",
9853 checkoids->data);
9854
9855 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9856
9857 numConstrs = PQntuples(res);
9858 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9859
9860 i_tableoid = PQfnumber(res, "tableoid");
9861 i_oid = PQfnumber(res, "oid");
9862 i_conrelid = PQfnumber(res, "conrelid");
9863 i_conname = PQfnumber(res, "conname");
9864 i_consrc = PQfnumber(res, "consrc");
9865 i_conislocal = PQfnumber(res, "conislocal");
9866 i_convalidated = PQfnumber(res, "convalidated");
9867
9868 /* As above, this loop iterates once per table, not once per row */
9869 curtblindx = -1;
9870 for (int j = 0; j < numConstrs;)
9871 {
9872 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9873 TableInfo *tbinfo = NULL;
9874 int numcons;
9875
9876 /* Count rows for this table */
9877 for (numcons = 1; numcons < numConstrs - j; numcons++)
9878 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9879 break;
9880
9881 /*
9882 * Locate the associated TableInfo; we rely on tblinfo[] being in
9883 * OID order.
9884 */
9885 while (++curtblindx < numTables)
9886 {
9887 tbinfo = &tblinfo[curtblindx];
9888 if (tbinfo->dobj.catId.oid == conrelid)
9889 break;
9890 }
9891 if (curtblindx >= numTables)
9892 pg_fatal("unrecognized table OID %u", conrelid);
9893
9894 if (numcons != tbinfo->ncheck)
9895 {
9896 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9897 "expected %d check constraints on table \"%s\" but found %d",
9898 tbinfo->ncheck),
9899 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9900 pg_log_error_hint("The system catalogs might be corrupted.");
9901 exit_nicely(1);
9902 }
9903
9904 tbinfo->checkexprs = constrs + j;
9905
9906 for (int c = 0; c < numcons; c++, j++)
9907 {
9908 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9909
9910 constrs[j].dobj.objType = DO_CONSTRAINT;
9911 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9912 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9913 AssignDumpId(&constrs[j].dobj);
9914 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9915 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9916 constrs[j].contable = tbinfo;
9917 constrs[j].condomain = NULL;
9918 constrs[j].contype = 'c';
9919 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9920 constrs[j].confrelid = InvalidOid;
9921 constrs[j].conindex = 0;
9922 constrs[j].condeferrable = false;
9923 constrs[j].condeferred = false;
9924 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9925
9926 /*
9927 * An unvalidated constraint needs to be dumped separately, so
9928 * that potentially-violating existing data is loaded before
9929 * the constraint.
9930 */
9931 constrs[j].separate = !validated;
9932
9933 constrs[j].dobj.dump = tbinfo->dobj.dump;
9934
9935 /*
9936 * Mark the constraint as needing to appear before the table
9937 * --- this is so that any other dependencies of the
9938 * constraint will be emitted before we try to create the
9939 * table. If the constraint is to be dumped separately, it
9940 * will be dumped after data is loaded anyway, so don't do it.
9941 * (There's an automatic dependency in the opposite direction
9942 * anyway, so don't need to add one manually here.)
9943 */
9944 if (!constrs[j].separate)
9945 addObjectDependency(&tbinfo->dobj,
9946 constrs[j].dobj.dumpId);
9947
9948 /*
9949 * We will detect later whether the constraint must be split
9950 * out from the table definition.
9951 */
9952 }
9953 }
9954
9955 PQclear(res);
9956 }
9957
9959 destroyPQExpBuffer(tbloids);
9960 destroyPQExpBuffer(checkoids);
9961}
9962
9963/*
9964 * Based on the getTableAttrs query's row corresponding to one column, set
9965 * the name and flags to handle a not-null constraint for that column in
9966 * the tbinfo struct.
9967 *
9968 * Result row 'r' is for tbinfo's attribute 'j'.
9969 *
9970 * There are four possibilities:
9971 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9972 * (the constraint name) remains NULL.
9973 * 2) The column has a constraint with no name (this is the case when
9974 * constraints come from pre-18 servers). In this case, ->notnull_constrs
9975 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9976 * 3) The column has an invalid not-null constraint. This must be treated
9977 * as a separate object (because it must be created after the table data
9978 * is loaded). So we add its OID to invalidnotnulloids for processing
9979 * elsewhere and do nothing further with it here. We distinguish this
9980 * case because the "notnull_invalidoid" column has been set to a non-NULL
9981 * value, which is the constraint OID. Valid constraints have a null OID.
9982 * 4) The column has a constraint with a known name; in that case
9983 * notnull_constrs carries that name and dumpTableSchema will print
9984 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9985 * (table_column_not_null) and there's no comment on the constraint,
9986 * there's no need to print that name in the dump, so notnull_constrs
9987 * is set to the empty string and it behaves as case 2.
9988 *
9989 * In a child table that inherits from a parent already containing NOT NULL
9990 * constraints and the columns in the child don't have their own NOT NULL
9991 * declarations, we suppress printing constraints in the child: the
9992 * constraints are acquired at the point where the child is attached to the
9993 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9994 * set not here but in flagInhAttrs. That flag is also used when the
9995 * constraint was validated in a child but all its parent have it as NOT
9996 * VALID.
9997 *
9998 * Any of these constraints might have the NO INHERIT bit. If so we set
9999 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10000 *
10001 * In case 4 above, the name comparison is a bit of a hack; it actually fails
10002 * to do the right thing in all but the trivial case. However, the downside
10003 * of getting it wrong is simply that the name is printed rather than
10004 * suppressed, so it's not a big deal.
10005 *
10006 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10007 * constraints are found, it is initialized and filled with the array of
10008 * OIDs of such constraints, for later processing.
10009 */
10010static void
10012 TableInfo *tbinfo, int j,
10013 int i_notnull_name,
10014 int i_notnull_comment,
10015 int i_notnull_invalidoid,
10016 int i_notnull_noinherit,
10017 int i_notnull_islocal,
10018 PQExpBuffer *invalidnotnulloids)
10019{
10020 DumpOptions *dopt = fout->dopt;
10021
10022 /*
10023 * If this not-null constraint is not valid, list its OID in
10024 * invalidnotnulloids and do nothing further. It'll be processed
10025 * elsewhere later.
10026 *
10027 * Because invalid not-null constraints are rare, we don't want to malloc
10028 * invalidnotnulloids until we're sure we're going it need it, which
10029 * happens here.
10030 */
10031 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10032 {
10033 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10034
10035 if (*invalidnotnulloids == NULL)
10036 {
10037 *invalidnotnulloids = createPQExpBuffer();
10038 appendPQExpBufferChar(*invalidnotnulloids, '{');
10039 appendPQExpBufferStr(*invalidnotnulloids, constroid);
10040 }
10041 else
10042 appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10043
10044 /*
10045 * Track when a parent constraint is invalid for the cases where a
10046 * child constraint has been validated independenly.
10047 */
10048 tbinfo->notnull_invalid[j] = true;
10049
10050 /* nothing else to do */
10051 tbinfo->notnull_constrs[j] = NULL;
10052 return;
10053 }
10054
10055 /*
10056 * notnull_noinh is straight from the query result. notnull_islocal also,
10057 * though flagInhAttrs may change that one later.
10058 */
10059 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10060 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10061 tbinfo->notnull_invalid[j] = false;
10062
10063 /*
10064 * Determine a constraint name to use. If the column is not marked not-
10065 * null, we set NULL which cues ... to do nothing. An empty string says
10066 * to print an unnamed NOT NULL, and anything else is a constraint name to
10067 * use.
10068 */
10069 if (fout->remoteVersion < 180000)
10070 {
10071 /*
10072 * < 18 doesn't have not-null names, so an unnamed constraint is
10073 * sufficient.
10074 */
10075 if (PQgetisnull(res, r, i_notnull_name))
10076 tbinfo->notnull_constrs[j] = NULL;
10077 else
10078 tbinfo->notnull_constrs[j] = "";
10079 }
10080 else
10081 {
10082 if (PQgetisnull(res, r, i_notnull_name))
10083 tbinfo->notnull_constrs[j] = NULL;
10084 else
10085 {
10086 /*
10087 * In binary upgrade of inheritance child tables, must have a
10088 * constraint name that we can UPDATE later; same if there's a
10089 * comment on the constraint.
10090 */
10091 if ((dopt->binary_upgrade &&
10092 !tbinfo->ispartition &&
10093 !tbinfo->notnull_islocal) ||
10094 !PQgetisnull(res, r, i_notnull_comment))
10095 {
10096 tbinfo->notnull_constrs[j] =
10097 pstrdup(PQgetvalue(res, r, i_notnull_name));
10098 }
10099 else
10100 {
10101 char *default_name;
10102
10103 /* XXX should match ChooseConstraintName better */
10104 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10105 tbinfo->attnames[j]);
10106 if (strcmp(default_name,
10107 PQgetvalue(res, r, i_notnull_name)) == 0)
10108 tbinfo->notnull_constrs[j] = "";
10109 else
10110 {
10111 tbinfo->notnull_constrs[j] =
10112 pstrdup(PQgetvalue(res, r, i_notnull_name));
10113 }
10114 free(default_name);
10115 }
10116 }
10117 }
10118}
10119
10120/*
10121 * Test whether a column should be printed as part of table's CREATE TABLE.
10122 * Column number is zero-based.
10123 *
10124 * Normally this is always true, but it's false for dropped columns, as well
10125 * as those that were inherited without any local definition. (If we print
10126 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10127 * For partitions, it's always true, because we want the partitions to be
10128 * created independently and ATTACH PARTITION used afterwards.
10129 *
10130 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10131 * attisdropped state later, so as to keep control of the physical column
10132 * order.
10133 *
10134 * This function exists because there are scattered nonobvious places that
10135 * must be kept in sync with this decision.
10136 */
10137bool
10138shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10139{
10140 if (dopt->binary_upgrade)
10141 return true;
10142 if (tbinfo->attisdropped[colno])
10143 return false;
10144 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10145}
10146
10147
10148/*
10149 * getTSParsers:
10150 * get information about all text search parsers in the system catalogs
10151 */
10152void
10154{
10155 PGresult *res;
10156 int ntups;
10157 int i;
10158 PQExpBuffer query;
10159 TSParserInfo *prsinfo;
10160 int i_tableoid;
10161 int i_oid;
10162 int i_prsname;
10163 int i_prsnamespace;
10164 int i_prsstart;
10165 int i_prstoken;
10166 int i_prsend;
10167 int i_prsheadline;
10168 int i_prslextype;
10169
10170 query = createPQExpBuffer();
10171
10172 /*
10173 * find all text search objects, including builtin ones; we filter out
10174 * system-defined objects at dump-out time.
10175 */
10176
10177 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10178 "prsstart::oid, prstoken::oid, "
10179 "prsend::oid, prsheadline::oid, prslextype::oid "
10180 "FROM pg_ts_parser");
10181
10182 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10183
10184 ntups = PQntuples(res);
10185
10186 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10187
10188 i_tableoid = PQfnumber(res, "tableoid");
10189 i_oid = PQfnumber(res, "oid");
10190 i_prsname = PQfnumber(res, "prsname");
10191 i_prsnamespace = PQfnumber(res, "prsnamespace");
10192 i_prsstart = PQfnumber(res, "prsstart");
10193 i_prstoken = PQfnumber(res, "prstoken");
10194 i_prsend = PQfnumber(res, "prsend");
10195 i_prsheadline = PQfnumber(res, "prsheadline");
10196 i_prslextype = PQfnumber(res, "prslextype");
10197
10198 for (i = 0; i < ntups; i++)
10199 {
10200 prsinfo[i].dobj.objType = DO_TSPARSER;
10201 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10202 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10203 AssignDumpId(&prsinfo[i].dobj);
10204 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10205 prsinfo[i].dobj.namespace =
10206 findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10207 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10208 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10209 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10210 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10211 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10212
10213 /* Decide whether we want to dump it */
10214 selectDumpableObject(&(prsinfo[i].dobj), fout);
10215 }
10216
10217 PQclear(res);
10218
10219 destroyPQExpBuffer(query);
10220}
10221
10222/*
10223 * getTSDictionaries:
10224 * get information about all text search dictionaries in the system catalogs
10225 */
10226void
10228{
10229 PGresult *res;
10230 int ntups;
10231 int i;
10232 PQExpBuffer query;
10233 TSDictInfo *dictinfo;
10234 int i_tableoid;
10235 int i_oid;
10236 int i_dictname;
10237 int i_dictnamespace;
10238 int i_dictowner;
10239 int i_dicttemplate;
10240 int i_dictinitoption;
10241
10242 query = createPQExpBuffer();
10243
10244 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10245 "dictnamespace, dictowner, "
10246 "dicttemplate, dictinitoption "
10247 "FROM pg_ts_dict");
10248
10249 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10250
10251 ntups = PQntuples(res);
10252
10253 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10254
10255 i_tableoid = PQfnumber(res, "tableoid");
10256 i_oid = PQfnumber(res, "oid");
10257 i_dictname = PQfnumber(res, "dictname");
10258 i_dictnamespace = PQfnumber(res, "dictnamespace");
10259 i_dictowner = PQfnumber(res, "dictowner");
10260 i_dictinitoption = PQfnumber(res, "dictinitoption");
10261 i_dicttemplate = PQfnumber(res, "dicttemplate");
10262
10263 for (i = 0; i < ntups; i++)
10264 {
10265 dictinfo[i].dobj.objType = DO_TSDICT;
10266 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10267 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10268 AssignDumpId(&dictinfo[i].dobj);
10269 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10270 dictinfo[i].dobj.namespace =
10271 findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10272 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10273 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10274 if (PQgetisnull(res, i, i_dictinitoption))
10275 dictinfo[i].dictinitoption = NULL;
10276 else
10277 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10278
10279 /* Decide whether we want to dump it */
10280 selectDumpableObject(&(dictinfo[i].dobj), fout);
10281 }
10282
10283 PQclear(res);
10284
10285 destroyPQExpBuffer(query);
10286}
10287
10288/*
10289 * getTSTemplates:
10290 * get information about all text search templates in the system catalogs
10291 */
10292void
10294{
10295 PGresult *res;
10296 int ntups;
10297 int i;
10298 PQExpBuffer query;
10299 TSTemplateInfo *tmplinfo;
10300 int i_tableoid;
10301 int i_oid;
10302 int i_tmplname;
10303 int i_tmplnamespace;
10304 int i_tmplinit;
10305 int i_tmpllexize;
10306
10307 query = createPQExpBuffer();
10308
10309 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10310 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10311 "FROM pg_ts_template");
10312
10313 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10314
10315 ntups = PQntuples(res);
10316
10317 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10318
10319 i_tableoid = PQfnumber(res, "tableoid");
10320 i_oid = PQfnumber(res, "oid");
10321 i_tmplname = PQfnumber(res, "tmplname");
10322 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10323 i_tmplinit = PQfnumber(res, "tmplinit");
10324 i_tmpllexize = PQfnumber(res, "tmpllexize");
10325
10326 for (i = 0; i < ntups; i++)
10327 {
10328 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10329 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10330 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10331 AssignDumpId(&tmplinfo[i].dobj);
10332 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10333 tmplinfo[i].dobj.namespace =
10334 findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10335 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10336 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10337
10338 /* Decide whether we want to dump it */
10339 selectDumpableObject(&(tmplinfo[i].dobj), fout);
10340 }
10341
10342 PQclear(res);
10343
10344 destroyPQExpBuffer(query);
10345}
10346
10347/*
10348 * getTSConfigurations:
10349 * get information about all text search configurations
10350 */
10351void
10353{
10354 PGresult *res;
10355 int ntups;
10356 int i;
10357 PQExpBuffer query;
10358 TSConfigInfo *cfginfo;
10359 int i_tableoid;
10360 int i_oid;
10361 int i_cfgname;
10362 int i_cfgnamespace;
10363 int i_cfgowner;
10364 int i_cfgparser;
10365
10366 query = createPQExpBuffer();
10367
10368 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10369 "cfgnamespace, cfgowner, cfgparser "
10370 "FROM pg_ts_config");
10371
10372 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10373
10374 ntups = PQntuples(res);
10375
10376 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10377
10378 i_tableoid = PQfnumber(res, "tableoid");
10379 i_oid = PQfnumber(res, "oid");
10380 i_cfgname = PQfnumber(res, "cfgname");
10381 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10382 i_cfgowner = PQfnumber(res, "cfgowner");
10383 i_cfgparser = PQfnumber(res, "cfgparser");
10384
10385 for (i = 0; i < ntups; i++)
10386 {
10387 cfginfo[i].dobj.objType = DO_TSCONFIG;
10388 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10389 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10390 AssignDumpId(&cfginfo[i].dobj);
10391 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10392 cfginfo[i].dobj.namespace =
10393 findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10394 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10395 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10396
10397 /* Decide whether we want to dump it */
10398 selectDumpableObject(&(cfginfo[i].dobj), fout);
10399 }
10400
10401 PQclear(res);
10402
10403 destroyPQExpBuffer(query);
10404}
10405
10406/*
10407 * getForeignDataWrappers:
10408 * get information about all foreign-data wrappers in the system catalogs
10409 */
10410void
10412{
10413 PGresult *res;
10414 int ntups;
10415 int i;
10416 PQExpBuffer query;
10417 FdwInfo *fdwinfo;
10418 int i_tableoid;
10419 int i_oid;
10420 int i_fdwname;
10421 int i_fdwowner;
10422 int i_fdwhandler;
10423 int i_fdwvalidator;
10424 int i_fdwacl;
10425 int i_acldefault;
10426 int i_fdwoptions;
10427
10428 query = createPQExpBuffer();
10429
10430 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10431 "fdwowner, "
10432 "fdwhandler::pg_catalog.regproc, "
10433 "fdwvalidator::pg_catalog.regproc, "
10434 "fdwacl, "
10435 "acldefault('F', fdwowner) AS acldefault, "
10436 "array_to_string(ARRAY("
10437 "SELECT quote_ident(option_name) || ' ' || "
10438 "quote_literal(option_value) "
10439 "FROM pg_options_to_table(fdwoptions) "
10440 "ORDER BY option_name"
10441 "), E',\n ') AS fdwoptions "
10442 "FROM pg_foreign_data_wrapper");
10443
10444 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10445
10446 ntups = PQntuples(res);
10447
10448 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10449
10450 i_tableoid = PQfnumber(res, "tableoid");
10451 i_oid = PQfnumber(res, "oid");
10452 i_fdwname = PQfnumber(res, "fdwname");
10453 i_fdwowner = PQfnumber(res, "fdwowner");
10454 i_fdwhandler = PQfnumber(res, "fdwhandler");
10455 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10456 i_fdwacl = PQfnumber(res, "fdwacl");
10457 i_acldefault = PQfnumber(res, "acldefault");
10458 i_fdwoptions = PQfnumber(res, "fdwoptions");
10459
10460 for (i = 0; i < ntups; i++)
10461 {
10462 fdwinfo[i].dobj.objType = DO_FDW;
10463 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10464 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10465 AssignDumpId(&fdwinfo[i].dobj);
10466 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10467 fdwinfo[i].dobj.namespace = NULL;
10468 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10469 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10470 fdwinfo[i].dacl.privtype = 0;
10471 fdwinfo[i].dacl.initprivs = NULL;
10472 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10473 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10474 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10475 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10476
10477 /* Decide whether we want to dump it */
10478 selectDumpableObject(&(fdwinfo[i].dobj), fout);
10479
10480 /* Mark whether FDW has an ACL */
10481 if (!PQgetisnull(res, i, i_fdwacl))
10482 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10483 }
10484
10485 PQclear(res);
10486
10487 destroyPQExpBuffer(query);
10488}
10489
10490/*
10491 * getForeignServers:
10492 * get information about all foreign servers in the system catalogs
10493 */
10494void
10496{
10497 PGresult *res;
10498 int ntups;
10499 int i;
10500 PQExpBuffer query;
10501 ForeignServerInfo *srvinfo;
10502 int i_tableoid;
10503 int i_oid;
10504 int i_srvname;
10505 int i_srvowner;
10506 int i_srvfdw;
10507 int i_srvtype;
10508 int i_srvversion;
10509 int i_srvacl;
10510 int i_acldefault;
10511 int i_srvoptions;
10512
10513 query = createPQExpBuffer();
10514
10515 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10516 "srvowner, "
10517 "srvfdw, srvtype, srvversion, srvacl, "
10518 "acldefault('S', srvowner) AS acldefault, "
10519 "array_to_string(ARRAY("
10520 "SELECT quote_ident(option_name) || ' ' || "
10521 "quote_literal(option_value) "
10522 "FROM pg_options_to_table(srvoptions) "
10523 "ORDER BY option_name"
10524 "), E',\n ') AS srvoptions "
10525 "FROM pg_foreign_server");
10526
10527 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10528
10529 ntups = PQntuples(res);
10530
10531 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10532
10533 i_tableoid = PQfnumber(res, "tableoid");
10534 i_oid = PQfnumber(res, "oid");
10535 i_srvname = PQfnumber(res, "srvname");
10536 i_srvowner = PQfnumber(res, "srvowner");
10537 i_srvfdw = PQfnumber(res, "srvfdw");
10538 i_srvtype = PQfnumber(res, "srvtype");
10539 i_srvversion = PQfnumber(res, "srvversion");
10540 i_srvacl = PQfnumber(res, "srvacl");
10541 i_acldefault = PQfnumber(res, "acldefault");
10542 i_srvoptions = PQfnumber(res, "srvoptions");
10543
10544 for (i = 0; i < ntups; i++)
10545 {
10546 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10547 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10548 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10549 AssignDumpId(&srvinfo[i].dobj);
10550 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10551 srvinfo[i].dobj.namespace = NULL;
10552 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10553 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10554 srvinfo[i].dacl.privtype = 0;
10555 srvinfo[i].dacl.initprivs = NULL;
10556 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10557 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10558 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10559 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10560 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10561
10562 /* Decide whether we want to dump it */
10563 selectDumpableObject(&(srvinfo[i].dobj), fout);
10564
10565 /* Servers have user mappings */
10567
10568 /* Mark whether server has an ACL */
10569 if (!PQgetisnull(res, i, i_srvacl))
10570 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10571 }
10572
10573 PQclear(res);
10574
10575 destroyPQExpBuffer(query);
10576}
10577
10578/*
10579 * getDefaultACLs:
10580 * get information about all default ACL information in the system catalogs
10581 */
10582void
10584{
10585 DumpOptions *dopt = fout->dopt;
10586 DefaultACLInfo *daclinfo;
10587 PQExpBuffer query;
10588 PGresult *res;
10589 int i_oid;
10590 int i_tableoid;
10591 int i_defaclrole;
10592 int i_defaclnamespace;
10593 int i_defaclobjtype;
10594 int i_defaclacl;
10595 int i_acldefault;
10596 int i,
10597 ntups;
10598
10599 query = createPQExpBuffer();
10600
10601 /*
10602 * Global entries (with defaclnamespace=0) replace the hard-wired default
10603 * ACL for their object type. We should dump them as deltas from the
10604 * default ACL, since that will be used as a starting point for
10605 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10606 * non-global entries can only add privileges not revoke them. We must
10607 * dump those as-is (i.e., as deltas from an empty ACL).
10608 *
10609 * We can use defaclobjtype as the object type for acldefault(), except
10610 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10611 * 's'.
10612 */
10614 "SELECT oid, tableoid, "
10615 "defaclrole, "
10616 "defaclnamespace, "
10617 "defaclobjtype, "
10618 "defaclacl, "
10619 "CASE WHEN defaclnamespace = 0 THEN "
10620 "acldefault(CASE WHEN defaclobjtype = 'S' "
10621 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10622 "defaclrole) ELSE '{}' END AS acldefault "
10623 "FROM pg_default_acl");
10624
10625 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10626
10627 ntups = PQntuples(res);
10628
10629 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10630
10631 i_oid = PQfnumber(res, "oid");
10632 i_tableoid = PQfnumber(res, "tableoid");
10633 i_defaclrole = PQfnumber(res, "defaclrole");
10634 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10635 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10636 i_defaclacl = PQfnumber(res, "defaclacl");
10637 i_acldefault = PQfnumber(res, "acldefault");
10638
10639 for (i = 0; i < ntups; i++)
10640 {
10641 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10642
10643 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10644 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10645 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10646 AssignDumpId(&daclinfo[i].dobj);
10647 /* cheesy ... is it worth coming up with a better object name? */
10648 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10649
10650 if (nspid != InvalidOid)
10651 daclinfo[i].dobj.namespace = findNamespace(nspid);
10652 else
10653 daclinfo[i].dobj.namespace = NULL;
10654
10655 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10656 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10657 daclinfo[i].dacl.privtype = 0;
10658 daclinfo[i].dacl.initprivs = NULL;
10659 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10660 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10661
10662 /* Default ACLs are ACLs, of course */
10663 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10664
10665 /* Decide whether we want to dump it */
10666 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10667 }
10668
10669 PQclear(res);
10670
10671 destroyPQExpBuffer(query);
10672}
10673
10674/*
10675 * getRoleName -- look up the name of a role, given its OID
10676 *
10677 * In current usage, we don't expect failures, so error out for a bad OID.
10678 */
10679static const char *
10680getRoleName(const char *roleoid_str)
10681{
10682 Oid roleoid = atooid(roleoid_str);
10683
10684 /*
10685 * Do binary search to find the appropriate item.
10686 */
10687 if (nrolenames > 0)
10688 {
10689 RoleNameItem *low = &rolenames[0];
10690 RoleNameItem *high = &rolenames[nrolenames - 1];
10691
10692 while (low <= high)
10693 {
10694 RoleNameItem *middle = low + (high - low) / 2;
10695
10696 if (roleoid < middle->roleoid)
10697 high = middle - 1;
10698 else if (roleoid > middle->roleoid)
10699 low = middle + 1;
10700 else
10701 return middle->rolename; /* found a match */
10702 }
10703 }
10704
10705 pg_fatal("role with OID %u does not exist", roleoid);
10706 return NULL; /* keep compiler quiet */
10707}
10708
10709/*
10710 * collectRoleNames --
10711 *
10712 * Construct a table of all known roles.
10713 * The table is sorted by OID for speed in lookup.
10714 */
10715static void
10717{
10718 PGresult *res;
10719 const char *query;
10720 int i;
10721
10722 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10723
10724 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10725
10726 nrolenames = PQntuples(res);
10727
10729
10730 for (i = 0; i < nrolenames; i++)
10731 {
10732 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10734 }
10735
10736 PQclear(res);
10737}
10738
10739/*
10740 * getAdditionalACLs
10741 *
10742 * We have now created all the DumpableObjects, and collected the ACL data
10743 * that appears in the directly-associated catalog entries. However, there's
10744 * more ACL-related info to collect. If any of a table's columns have ACLs,
10745 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10746 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10747 * Also, in versions having the pg_init_privs catalog, read that and load the
10748 * information into the relevant DumpableObjects.
10749 */
10750static void
10752{
10754 PGresult *res;
10755 int ntups,
10756 i;
10757
10758 /* Check for per-column ACLs */
10760 "SELECT DISTINCT attrelid FROM pg_attribute "
10761 "WHERE attacl IS NOT NULL");
10762
10763 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10764
10765 ntups = PQntuples(res);
10766 for (i = 0; i < ntups; i++)
10767 {
10768 Oid relid = atooid(PQgetvalue(res, i, 0));
10769 TableInfo *tblinfo;
10770
10771 tblinfo = findTableByOid(relid);
10772 /* OK to ignore tables we haven't got a DumpableObject for */
10773 if (tblinfo)
10774 {
10776 tblinfo->hascolumnACLs = true;
10777 }
10778 }
10779 PQclear(res);
10780
10781 /* Fetch initial-privileges data */
10782 if (fout->remoteVersion >= 90600)
10783 {
10784 printfPQExpBuffer(query,
10785 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10786 "FROM pg_init_privs");
10787
10788 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10789
10790 ntups = PQntuples(res);
10791 for (i = 0; i < ntups; i++)
10792 {
10793 Oid objoid = atooid(PQgetvalue(res, i, 0));
10794 Oid classoid = atooid(PQgetvalue(res, i, 1));
10795 int objsubid = atoi(PQgetvalue(res, i, 2));
10796 char privtype = *(PQgetvalue(res, i, 3));
10797 char *initprivs = PQgetvalue(res, i, 4);
10798 CatalogId objId;
10799 DumpableObject *dobj;
10800
10801 objId.tableoid = classoid;
10802 objId.oid = objoid;
10803 dobj = findObjectByCatalogId(objId);
10804 /* OK to ignore entries we haven't got a DumpableObject for */
10805 if (dobj)
10806 {
10807 /* Cope with sub-object initprivs */
10808 if (objsubid != 0)
10809 {
10810 if (dobj->objType == DO_TABLE)
10811 {
10812 /* For a column initprivs, set the table's ACL flags */
10814 ((TableInfo *) dobj)->hascolumnACLs = true;
10815 }
10816 else
10817 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10818 classoid, objoid, objsubid);
10819 continue;
10820 }
10821
10822 /*
10823 * We ignore any pg_init_privs.initprivs entry for the public
10824 * schema, as explained in getNamespaces().
10825 */
10826 if (dobj->objType == DO_NAMESPACE &&
10827 strcmp(dobj->name, "public") == 0)
10828 continue;
10829
10830 /* Else it had better be of a type we think has ACLs */
10831 if (dobj->objType == DO_NAMESPACE ||
10832 dobj->objType == DO_TYPE ||
10833 dobj->objType == DO_FUNC ||
10834 dobj->objType == DO_AGG ||
10835 dobj->objType == DO_TABLE ||
10836 dobj->objType == DO_PROCLANG ||
10837 dobj->objType == DO_FDW ||
10838 dobj->objType == DO_FOREIGN_SERVER)
10839 {
10841
10842 daobj->dacl.privtype = privtype;
10843 daobj->dacl.initprivs = pstrdup(initprivs);
10844 }
10845 else
10846 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10847 classoid, objoid, objsubid);
10848 }
10849 }
10850 PQclear(res);
10851 }
10852
10853 destroyPQExpBuffer(query);
10854}
10855
10856/*
10857 * dumpCommentExtended --
10858 *
10859 * This routine is used to dump any comments associated with the
10860 * object handed to this routine. The routine takes the object type
10861 * and object name (ready to print, except for schema decoration), plus
10862 * the namespace and owner of the object (for labeling the ArchiveEntry),
10863 * plus catalog ID and subid which are the lookup key for pg_description,
10864 * plus the dump ID for the object (for setting a dependency).
10865 * If a matching pg_description entry is found, it is dumped.
10866 *
10867 * Note: in some cases, such as comments for triggers and rules, the "type"
10868 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10869 * but it doesn't seem worth complicating the API for all callers to make
10870 * it cleaner.
10871 *
10872 * Note: although this routine takes a dumpId for dependency purposes,
10873 * that purpose is just to mark the dependency in the emitted dump file
10874 * for possible future use by pg_restore. We do NOT use it for determining
10875 * ordering of the comment in the dump file, because this routine is called
10876 * after dependency sorting occurs. This routine should be called just after
10877 * calling ArchiveEntry() for the specified object.
10878 */
10879static void
10881 const char *name, const char *namespace,
10882 const char *owner, CatalogId catalogId,
10883 int subid, DumpId dumpId,
10884 const char *initdb_comment)
10885{
10886 DumpOptions *dopt = fout->dopt;
10888 int ncomments;
10889
10890 /* do nothing, if --no-comments is supplied */
10891 if (dopt->no_comments)
10892 return;
10893
10894 /* Comments are schema not data ... except LO comments are data */
10895 if (strcmp(type, "LARGE OBJECT") != 0)
10896 {
10897 if (!dopt->dumpSchema)
10898 return;
10899 }
10900 else
10901 {
10902 /* We do dump LO comments in binary-upgrade mode */
10903 if (!dopt->dumpData && !dopt->binary_upgrade)
10904 return;
10905 }
10906
10907 /* Search for comments associated with catalogId, using table */
10908 ncomments = findComments(catalogId.tableoid, catalogId.oid,
10909 &comments);
10910
10911 /* Is there one matching the subid? */
10912 while (ncomments > 0)
10913 {
10914 if (comments->objsubid == subid)
10915 break;
10916 comments++;
10917 ncomments--;
10918 }
10919
10920 if (initdb_comment != NULL)
10921 {
10922 static CommentItem empty_comment = {.descr = ""};
10923
10924 /*
10925 * initdb creates this object with a comment. Skip dumping the
10926 * initdb-provided comment, which would complicate matters for
10927 * non-superuser use of pg_dump. When the DBA has removed initdb's
10928 * comment, replicate that.
10929 */
10930 if (ncomments == 0)
10931 {
10932 comments = &empty_comment;
10933 ncomments = 1;
10934 }
10935 else if (strcmp(comments->descr, initdb_comment) == 0)
10936 ncomments = 0;
10937 }
10938
10939 /* If a comment exists, build COMMENT ON statement */
10940 if (ncomments > 0)
10941 {
10944
10945 appendPQExpBuffer(query, "COMMENT ON %s ", type);
10946 if (namespace && *namespace)
10947 appendPQExpBuffer(query, "%s.", fmtId(namespace));
10948 appendPQExpBuffer(query, "%s IS ", name);
10949 appendStringLiteralAH(query, comments->descr, fout);
10950 appendPQExpBufferStr(query, ";\n");
10951
10952 appendPQExpBuffer(tag, "%s %s", type, name);
10953
10954 /*
10955 * We mark comments as SECTION_NONE because they really belong in the
10956 * same section as their parent, whether that is pre-data or
10957 * post-data.
10958 */
10960 ARCHIVE_OPTS(.tag = tag->data,
10961 .namespace = namespace,
10962 .owner = owner,
10963 .description = "COMMENT",
10964 .section = SECTION_NONE,
10965 .createStmt = query->data,
10966 .deps = &dumpId,
10967 .nDeps = 1));
10968
10969 destroyPQExpBuffer(query);
10970 destroyPQExpBuffer(tag);
10971 }
10972}
10973
10974/*
10975 * dumpComment --
10976 *
10977 * Typical simplification of the above function.
10978 */
10979static inline void
10980dumpComment(Archive *fout, const char *type,
10981 const char *name, const char *namespace,
10982 const char *owner, CatalogId catalogId,
10983 int subid, DumpId dumpId)
10984{
10985 dumpCommentExtended(fout, type, name, namespace, owner,
10986 catalogId, subid, dumpId, NULL);
10987}
10988
10989/*
10990 * appendNamedArgument --
10991 *
10992 * Convenience routine for constructing parameters of the form:
10993 * 'paraname', 'value'::type
10994 */
10995static void
10996appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10997 const char *argtype, const char *argval)
10998{
10999 appendPQExpBufferStr(out, ",\n\t");
11000
11001 appendStringLiteralAH(out, argname, fout);
11002 appendPQExpBufferStr(out, ", ");
11003
11004 appendStringLiteralAH(out, argval, fout);
11005 appendPQExpBuffer(out, "::%s", argtype);
11006}
11007
11008/*
11009 * fetchAttributeStats --
11010 *
11011 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11012 */
11013static PGresult *
11015{
11016 ArchiveHandle *AH = (ArchiveHandle *) fout;
11017 PQExpBuffer nspnames = createPQExpBuffer();
11018 PQExpBuffer relnames = createPQExpBuffer();
11019 int count = 0;
11020 PGresult *res = NULL;
11021 static TocEntry *te;
11022 static bool restarted;
11023 int max_rels = MAX_ATTR_STATS_RELS;
11024
11025 /*
11026 * Our query for retrieving statistics for multiple relations uses WITH
11027 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11028 * in v9.4. For older versions, we resort to gathering statistics for a
11029 * single relation at a time.
11030 */
11031 if (fout->remoteVersion < 90400)
11032 max_rels = 1;
11033
11034 /* If we're just starting, set our TOC pointer. */
11035 if (!te)
11036 te = AH->toc->next;
11037
11038 /*
11039 * We can't easily avoid a second TOC scan for the tar format because it
11040 * writes restore.sql separately, which means we must execute the queries
11041 * twice. This feels risky, but there is no known reason it should
11042 * generate different output than the first pass. Even if it does, the
11043 * worst-case scenario is that restore.sql might have different statistics
11044 * data than the archive.
11045 */
11046 if (!restarted && te == AH->toc && AH->format == archTar)
11047 {
11048 te = AH->toc->next;
11049 restarted = true;
11050 }
11051
11052 appendPQExpBufferChar(nspnames, '{');
11053 appendPQExpBufferChar(relnames, '{');
11054
11055 /*
11056 * Scan the TOC for the next set of relevant stats entries. We assume
11057 * that statistics are dumped in the order they are listed in the TOC.
11058 * This is perhaps not the sturdiest assumption, so we verify it matches
11059 * reality in dumpRelationStats_dumper().
11060 */
11061 for (; te != AH->toc && count < max_rels; te = te->next)
11062 {
11063 if ((te->reqs & REQ_STATS) != 0 &&
11064 strcmp(te->desc, "STATISTICS DATA") == 0)
11065 {
11066 appendPGArray(nspnames, te->namespace);
11067 appendPGArray(relnames, te->tag);
11068 count++;
11069 }
11070 }
11071
11072 appendPQExpBufferChar(nspnames, '}');
11073 appendPQExpBufferChar(relnames, '}');
11074
11075 /* Execute the query for the next batch of relations. */
11076 if (count > 0)
11077 {
11079
11080 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11081 appendStringLiteralAH(query, nspnames->data, fout);
11082 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11083 appendStringLiteralAH(query, relnames->data, fout);
11084 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11085 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11086 destroyPQExpBuffer(query);
11087 }
11088
11089 destroyPQExpBuffer(nspnames);
11090 destroyPQExpBuffer(relnames);
11091 return res;
11092}
11093
11094/*
11095 * dumpRelationStats_dumper --
11096 *
11097 * Generate command to import stats into the relation on the new database.
11098 * This routine is called by the Archiver when it wants the statistics to be
11099 * dumped.
11100 */
11101static char *
11102dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11103{
11104 const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11105 static PGresult *res;
11106 static int rownum;
11107 PQExpBuffer query;
11108 PQExpBufferData out_data;
11109 PQExpBuffer out = &out_data;
11110 int i_schemaname;
11111 int i_tablename;
11112 int i_attname;
11113 int i_inherited;
11114 int i_null_frac;
11115 int i_avg_width;
11116 int i_n_distinct;
11117 int i_most_common_vals;
11118 int i_most_common_freqs;
11119 int i_histogram_bounds;
11120 int i_correlation;
11121 int i_most_common_elems;
11122 int i_most_common_elem_freqs;
11123 int i_elem_count_histogram;
11124 int i_range_length_histogram;
11125 int i_range_empty_frac;
11126 int i_range_bounds_histogram;
11127 static TocEntry *expected_te;
11128
11129 /*
11130 * fetchAttributeStats() assumes that the statistics are dumped in the
11131 * order they are listed in the TOC. We verify that here for safety.
11132 */
11133 if (!expected_te)
11134 expected_te = ((ArchiveHandle *) fout)->toc;
11135
11136 expected_te = expected_te->next;
11137 while ((expected_te->reqs & REQ_STATS) == 0 ||
11138 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11139 expected_te = expected_te->next;
11140
11141 if (te != expected_te)
11142 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11143 te->dumpId, te->desc, te->tag,
11144 expected_te->dumpId, expected_te->desc, expected_te->tag);
11145
11146 query = createPQExpBuffer();
11148 {
11150 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11151 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11152 "s.null_frac, s.avg_width, s.n_distinct, "
11153 "s.most_common_vals, s.most_common_freqs, "
11154 "s.histogram_bounds, s.correlation, "
11155 "s.most_common_elems, s.most_common_elem_freqs, "
11156 "s.elem_count_histogram, ");
11157
11158 if (fout->remoteVersion >= 170000)
11160 "s.range_length_histogram, "
11161 "s.range_empty_frac, "
11162 "s.range_bounds_histogram ");
11163 else
11165 "NULL AS range_length_histogram,"
11166 "NULL AS range_empty_frac,"
11167 "NULL AS range_bounds_histogram ");
11168
11169 /*
11170 * The results must be in the order of the relations supplied in the
11171 * parameters to ensure we remain in sync as we walk through the TOC.
11172 * The redundant filter clause on s.tablename = ANY(...) seems
11173 * sufficient to convince the planner to use
11174 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11175 * This may not work for all versions.
11176 *
11177 * Our query for retrieving statistics for multiple relations uses
11178 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11179 * introduced in v9.4. For older versions, we resort to gathering
11180 * statistics for a single relation at a time.
11181 */
11182 if (fout->remoteVersion >= 90400)
11184 "FROM pg_catalog.pg_stats s "
11185 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11186 "ON s.schemaname = u.schemaname "
11187 "AND s.tablename = u.tablename "
11188 "WHERE s.tablename = ANY($2) "
11189 "ORDER BY u.ord, s.attname, s.inherited");
11190 else
11192 "FROM pg_catalog.pg_stats s "
11193 "WHERE s.schemaname = $1[1] "
11194 "AND s.tablename = $2[1] "
11195 "ORDER BY s.attname, s.inherited");
11196
11197 ExecuteSqlStatement(fout, query->data);
11198
11200 resetPQExpBuffer(query);
11201 }
11202
11203 initPQExpBuffer(out);
11204
11205 /* restore relation stats */
11206 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11207 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11208 fout->remoteVersion);
11209 appendPQExpBufferStr(out, "\t'schemaname', ");
11210 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11211 appendPQExpBufferStr(out, ",\n");
11212 appendPQExpBufferStr(out, "\t'relname', ");
11213 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11214 appendPQExpBufferStr(out, ",\n");
11215 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11216
11217 /*
11218 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11219 * the relation is empty, or it could mean that it hadn't yet been
11220 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11221 * This ambiguity allegedly can cause the planner to choose inefficient
11222 * plans after restoring to v18 or newer. To deal with this, let's just
11223 * set reltuples to -1 in that case.
11224 */
11225 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11226 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11227 else
11228 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11229
11230 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11231 rsinfo->relallvisible);
11232
11233 if (fout->remoteVersion >= 180000)
11234 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11235
11236 appendPQExpBufferStr(out, "\n);\n");
11237
11238 /* Fetch the next batch of attribute statistics if needed. */
11239 if (rownum >= PQntuples(res))
11240 {
11241 PQclear(res);
11242 res = fetchAttributeStats(fout);
11243 rownum = 0;
11244 }
11245
11246 i_schemaname = PQfnumber(res, "schemaname");
11247 i_tablename = PQfnumber(res, "tablename");
11248 i_attname = PQfnumber(res, "attname");
11249 i_inherited = PQfnumber(res, "inherited");
11250 i_null_frac = PQfnumber(res, "null_frac");
11251 i_avg_width = PQfnumber(res, "avg_width");
11252 i_n_distinct = PQfnumber(res, "n_distinct");
11253 i_most_common_vals = PQfnumber(res, "most_common_vals");
11254 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11255 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11256 i_correlation = PQfnumber(res, "correlation");
11257 i_most_common_elems = PQfnumber(res, "most_common_elems");
11258 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11259 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11260 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11261 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11262 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11263
11264 /* restore attribute stats */
11265 for (; rownum < PQntuples(res); rownum++)
11266 {
11267 const char *attname;
11268
11269 /* Stop if the next stat row in our cache isn't for this relation. */
11270 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11271 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11272 break;
11273
11274 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11275 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11276 fout->remoteVersion);
11277 appendPQExpBufferStr(out, "\t'schemaname', ");
11278 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11279 appendPQExpBufferStr(out, ",\n\t'relname', ");
11280 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11281
11282 if (PQgetisnull(res, rownum, i_attname))
11283 pg_fatal("unexpected null attname");
11284 attname = PQgetvalue(res, rownum, i_attname);
11285
11286 /*
11287 * Indexes look up attname in indAttNames to derive attnum, all others
11288 * use attname directly. We must specify attnum for indexes, since
11289 * their attnames are not necessarily stable across dump/reload.
11290 */
11291 if (rsinfo->nindAttNames == 0)
11292 {
11293 appendPQExpBufferStr(out, ",\n\t'attname', ");
11294 appendStringLiteralAH(out, attname, fout);
11295 }
11296 else
11297 {
11298 bool found = false;
11299
11300 for (int i = 0; i < rsinfo->nindAttNames; i++)
11301 {
11302 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11303 {
11304 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11305 i + 1);
11306 found = true;
11307 break;
11308 }
11309 }
11310
11311 if (!found)
11312 pg_fatal("could not find index attname \"%s\"", attname);
11313 }
11314
11315 if (!PQgetisnull(res, rownum, i_inherited))
11316 appendNamedArgument(out, fout, "inherited", "boolean",
11317 PQgetvalue(res, rownum, i_inherited));
11318 if (!PQgetisnull(res, rownum, i_null_frac))
11319 appendNamedArgument(out, fout, "null_frac", "real",
11320 PQgetvalue(res, rownum, i_null_frac));
11321 if (!PQgetisnull(res, rownum, i_avg_width))
11322 appendNamedArgument(out, fout, "avg_width", "integer",
11323 PQgetvalue(res, rownum, i_avg_width));
11324 if (!PQgetisnull(res, rownum, i_n_distinct))
11325 appendNamedArgument(out, fout, "n_distinct", "real",
11326 PQgetvalue(res, rownum, i_n_distinct));
11327 if (!PQgetisnull(res, rownum, i_most_common_vals))
11328 appendNamedArgument(out, fout, "most_common_vals", "text",
11329 PQgetvalue(res, rownum, i_most_common_vals));
11330 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11331 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11332 PQgetvalue(res, rownum, i_most_common_freqs));
11333 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11334 appendNamedArgument(out, fout, "histogram_bounds", "text",
11335 PQgetvalue(res, rownum, i_histogram_bounds));
11336 if (!PQgetisnull(res, rownum, i_correlation))
11337 appendNamedArgument(out, fout, "correlation", "real",
11338 PQgetvalue(res, rownum, i_correlation));
11339 if (!PQgetisnull(res, rownum, i_most_common_elems))
11340 appendNamedArgument(out, fout, "most_common_elems", "text",
11341 PQgetvalue(res, rownum, i_most_common_elems));
11342 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11343 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11344 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11345 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11346 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11347 PQgetvalue(res, rownum, i_elem_count_histogram));
11348 if (fout->remoteVersion >= 170000)
11349 {
11350 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11351 appendNamedArgument(out, fout, "range_length_histogram", "text",
11352 PQgetvalue(res, rownum, i_range_length_histogram));
11353 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11354 appendNamedArgument(out, fout, "range_empty_frac", "real",
11355 PQgetvalue(res, rownum, i_range_empty_frac));
11356 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11357 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11358 PQgetvalue(res, rownum, i_range_bounds_histogram));
11359 }
11360 appendPQExpBufferStr(out, "\n);\n");
11361 }
11362
11363 destroyPQExpBuffer(query);
11364 return out->data;
11365}
11366
11367/*
11368 * dumpRelationStats --
11369 *
11370 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11371 * care of gathering the statistics and generating the restore commands when
11372 * they are needed.
11373 */
11374static void
11376{
11377 const DumpableObject *dobj = &rsinfo->dobj;
11378
11379 /* nothing to do if we are not dumping statistics */
11380 if (!fout->dopt->dumpStatistics)
11381 return;
11382
11384 ARCHIVE_OPTS(.tag = dobj->name,
11385 .namespace = dobj->namespace->dobj.name,
11386 .description = "STATISTICS DATA",
11387 .section = rsinfo->section,
11388 .defnFn = dumpRelationStats_dumper,
11389 .defnArg = rsinfo,
11390 .deps = dobj->dependencies,
11391 .nDeps = dobj->nDeps));
11392}
11393
11394/*
11395 * dumpTableComment --
11396 *
11397 * As above, but dump comments for both the specified table (or view)
11398 * and its columns.
11399 */
11400static void
11402 const char *reltypename)
11403{
11404 DumpOptions *dopt = fout->dopt;
11406 int ncomments;
11407 PQExpBuffer query;
11408 PQExpBuffer tag;
11409
11410 /* do nothing, if --no-comments is supplied */
11411 if (dopt->no_comments)
11412 return;
11413
11414 /* Comments are SCHEMA not data */
11415 if (!dopt->dumpSchema)
11416 return;
11417
11418 /* Search for comments associated with relation, using table */
11420 tbinfo->dobj.catId.oid,
11421 &comments);
11422
11423 /* If comments exist, build COMMENT ON statements */
11424 if (ncomments <= 0)
11425 return;
11426
11427 query = createPQExpBuffer();
11428 tag = createPQExpBuffer();
11429
11430 while (ncomments > 0)
11431 {
11432 const char *descr = comments->descr;
11433 int objsubid = comments->objsubid;
11434
11435 if (objsubid == 0)
11436 {
11437 resetPQExpBuffer(tag);
11438 appendPQExpBuffer(tag, "%s %s", reltypename,
11439 fmtId(tbinfo->dobj.name));
11440
11441 resetPQExpBuffer(query);
11442 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11443 fmtQualifiedDumpable(tbinfo));
11444 appendStringLiteralAH(query, descr, fout);
11445 appendPQExpBufferStr(query, ";\n");
11446
11448 ARCHIVE_OPTS(.tag = tag->data,
11449 .namespace = tbinfo->dobj.namespace->dobj.name,
11450 .owner = tbinfo->rolname,
11451 .description = "COMMENT",
11452 .section = SECTION_NONE,
11453 .createStmt = query->data,
11454 .deps = &(tbinfo->dobj.dumpId),
11455 .nDeps = 1));
11456 }
11457 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11458 {
11459 resetPQExpBuffer(tag);
11460 appendPQExpBuffer(tag, "COLUMN %s.",
11461 fmtId(tbinfo->dobj.name));
11462 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11463
11464 resetPQExpBuffer(query);
11465 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11466 fmtQualifiedDumpable(tbinfo));
11467 appendPQExpBuffer(query, "%s IS ",
11468 fmtId(tbinfo->attnames[objsubid - 1]));
11469 appendStringLiteralAH(query, descr, fout);
11470 appendPQExpBufferStr(query, ";\n");
11471
11473 ARCHIVE_OPTS(.tag = tag->data,
11474 .namespace = tbinfo->dobj.namespace->dobj.name,
11475 .owner = tbinfo->rolname,
11476 .description = "COMMENT",
11477 .section = SECTION_NONE,
11478 .createStmt = query->data,
11479 .deps = &(tbinfo->dobj.dumpId),
11480 .nDeps = 1));
11481 }
11482
11483 comments++;
11484 ncomments--;
11485 }
11486
11487 destroyPQExpBuffer(query);
11488 destroyPQExpBuffer(tag);
11489}
11490
11491/*
11492 * findComments --
11493 *
11494 * Find the comment(s), if any, associated with the given object. All the
11495 * objsubid values associated with the given classoid/objoid are found with
11496 * one search.
11497 */
11498static int
11500{
11501 CommentItem *middle = NULL;
11502 CommentItem *low;
11503 CommentItem *high;
11504 int nmatch;
11505
11506 /*
11507 * Do binary search to find some item matching the object.
11508 */
11509 low = &comments[0];
11510 high = &comments[ncomments - 1];
11511 while (low <= high)
11512 {
11513 middle = low + (high - low) / 2;
11514
11515 if (classoid < middle->classoid)
11516 high = middle - 1;
11517 else if (classoid > middle->classoid)
11518 low = middle + 1;
11519 else if (objoid < middle->objoid)
11520 high = middle - 1;
11521 else if (objoid > middle->objoid)
11522 low = middle + 1;
11523 else
11524 break; /* found a match */
11525 }
11526
11527 if (low > high) /* no matches */
11528 {
11529 *items = NULL;
11530 return 0;
11531 }
11532
11533 /*
11534 * Now determine how many items match the object. The search loop
11535 * invariant still holds: only items between low and high inclusive could
11536 * match.
11537 */
11538 nmatch = 1;
11539 while (middle > low)
11540 {
11541 if (classoid != middle[-1].classoid ||
11542 objoid != middle[-1].objoid)
11543 break;
11544 middle--;
11545 nmatch++;
11546 }
11547
11548 *items = middle;
11549
11550 middle += nmatch;
11551 while (middle <= high)
11552 {
11553 if (classoid != middle->classoid ||
11554 objoid != middle->objoid)
11555 break;
11556 middle++;
11557 nmatch++;
11558 }
11559
11560 return nmatch;
11561}
11562
11563/*
11564 * collectComments --
11565 *
11566 * Construct a table of all comments available for database objects;
11567 * also set the has-comment component flag for each relevant object.
11568 *
11569 * We used to do per-object queries for the comments, but it's much faster
11570 * to pull them all over at once, and on most databases the memory cost
11571 * isn't high.
11572 *
11573 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11574 */
11575static void
11577{
11578 PGresult *res;
11579 PQExpBuffer query;
11580 int i_description;
11581 int i_classoid;
11582 int i_objoid;
11583 int i_objsubid;
11584 int ntups;
11585 int i;
11586 DumpableObject *dobj;
11587
11588 query = createPQExpBuffer();
11589
11590 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11591 "FROM pg_catalog.pg_description "
11592 "ORDER BY classoid, objoid, objsubid");
11593
11594 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11595
11596 /* Construct lookup table containing OIDs in numeric form */
11597
11598 i_description = PQfnumber(res, "description");
11599 i_classoid = PQfnumber(res, "classoid");
11600 i_objoid = PQfnumber(res, "objoid");
11601 i_objsubid = PQfnumber(res, "objsubid");
11602
11603 ntups = PQntuples(res);
11604
11605 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11606 ncomments = 0;
11607 dobj = NULL;
11608
11609 for (i = 0; i < ntups; i++)
11610 {
11611 CatalogId objId;
11612 int subid;
11613
11614 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11615 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11616 subid = atoi(PQgetvalue(res, i, i_objsubid));
11617
11618 /* We needn't remember comments that don't match any dumpable object */
11619 if (dobj == NULL ||
11620 dobj->catId.tableoid != objId.tableoid ||
11621 dobj->catId.oid != objId.oid)
11622 dobj = findObjectByCatalogId(objId);
11623 if (dobj == NULL)
11624 continue;
11625
11626 /*
11627 * Comments on columns of composite types are linked to the type's
11628 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11629 * in the type's own DumpableObject.
11630 */
11631 if (subid != 0 && dobj->objType == DO_TABLE &&
11632 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11633 {
11634 TypeInfo *cTypeInfo;
11635
11636 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11637 if (cTypeInfo)
11639 }
11640 else
11641 dobj->components |= DUMP_COMPONENT_COMMENT;
11642
11643 comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11645 comments[ncomments].objoid = objId.oid;
11646 comments[ncomments].objsubid = subid;
11647 ncomments++;
11648 }
11649
11650 PQclear(res);
11651 destroyPQExpBuffer(query);
11652}
11653
11654/*
11655 * dumpDumpableObject
11656 *
11657 * This routine and its subsidiaries are responsible for creating
11658 * ArchiveEntries (TOC objects) for each object to be dumped.
11659 */
11660static void
11662{
11663 /*
11664 * Clear any dump-request bits for components that don't exist for this
11665 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11666 * request for every kind of object.)
11667 */
11668 dobj->dump &= dobj->components;
11669
11670 /* Now, short-circuit if there's nothing to be done here. */
11671 if (dobj->dump == 0)
11672 return;
11673
11674 switch (dobj->objType)
11675 {
11676 case DO_NAMESPACE:
11677 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11678 break;
11679 case DO_EXTENSION:
11680 dumpExtension(fout, (const ExtensionInfo *) dobj);
11681 break;
11682 case DO_TYPE:
11683 dumpType(fout, (const TypeInfo *) dobj);
11684 break;
11685 case DO_SHELL_TYPE:
11686 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11687 break;
11688 case DO_FUNC:
11689 dumpFunc(fout, (const FuncInfo *) dobj);
11690 break;
11691 case DO_AGG:
11692 dumpAgg(fout, (const AggInfo *) dobj);
11693 break;
11694 case DO_OPERATOR:
11695 dumpOpr(fout, (const OprInfo *) dobj);
11696 break;
11697 case DO_ACCESS_METHOD:
11698 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11699 break;
11700 case DO_OPCLASS:
11701 dumpOpclass(fout, (const OpclassInfo *) dobj);
11702 break;
11703 case DO_OPFAMILY:
11704 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11705 break;
11706 case DO_COLLATION:
11707 dumpCollation(fout, (const CollInfo *) dobj);
11708 break;
11709 case DO_CONVERSION:
11710 dumpConversion(fout, (const ConvInfo *) dobj);
11711 break;
11712 case DO_TABLE:
11713 dumpTable(fout, (const TableInfo *) dobj);
11714 break;
11715 case DO_TABLE_ATTACH:
11716 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11717 break;
11718 case DO_ATTRDEF:
11719 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11720 break;
11721 case DO_INDEX:
11722 dumpIndex(fout, (const IndxInfo *) dobj);
11723 break;
11724 case DO_INDEX_ATTACH:
11725 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11726 break;
11727 case DO_STATSEXT:
11728 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11729 break;
11730 case DO_REFRESH_MATVIEW:
11731 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11732 break;
11733 case DO_RULE:
11734 dumpRule(fout, (const RuleInfo *) dobj);
11735 break;
11736 case DO_TRIGGER:
11737 dumpTrigger(fout, (const TriggerInfo *) dobj);
11738 break;
11739 case DO_EVENT_TRIGGER:
11740 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11741 break;
11742 case DO_CONSTRAINT:
11743 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11744 break;
11745 case DO_FK_CONSTRAINT:
11746 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11747 break;
11748 case DO_PROCLANG:
11749 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11750 break;
11751 case DO_CAST:
11752 dumpCast(fout, (const CastInfo *) dobj);
11753 break;
11754 case DO_TRANSFORM:
11755 dumpTransform(fout, (const TransformInfo *) dobj);
11756 break;
11757 case DO_SEQUENCE_SET:
11758 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11759 break;
11760 case DO_TABLE_DATA:
11761 dumpTableData(fout, (const TableDataInfo *) dobj);
11762 break;
11763 case DO_DUMMY_TYPE:
11764 /* table rowtypes and array types are never dumped separately */
11765 break;
11766 case DO_TSPARSER:
11767 dumpTSParser(fout, (const TSParserInfo *) dobj);
11768 break;
11769 case DO_TSDICT:
11770 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11771 break;
11772 case DO_TSTEMPLATE:
11773 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11774 break;
11775 case DO_TSCONFIG:
11776 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11777 break;
11778 case DO_FDW:
11779 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11780 break;
11781 case DO_FOREIGN_SERVER:
11782 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11783 break;
11784 case DO_DEFAULT_ACL:
11785 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11786 break;
11787 case DO_LARGE_OBJECT:
11788 dumpLO(fout, (const LoInfo *) dobj);
11789 break;
11791 if (dobj->dump & DUMP_COMPONENT_DATA)
11792 {
11793 LoInfo *loinfo;
11794 TocEntry *te;
11795
11796 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11797 if (loinfo == NULL)
11798 pg_fatal("missing metadata for large objects \"%s\"",
11799 dobj->name);
11800
11801 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11802 ARCHIVE_OPTS(.tag = dobj->name,
11803 .owner = loinfo->rolname,
11804 .description = "BLOBS",
11805 .section = SECTION_DATA,
11806 .deps = dobj->dependencies,
11807 .nDeps = dobj->nDeps,
11808 .dumpFn = dumpLOs,
11809 .dumpArg = loinfo));
11810
11811 /*
11812 * Set the TocEntry's dataLength in case we are doing a
11813 * parallel dump and want to order dump jobs by table size.
11814 * (We need some size estimate for every TocEntry with a
11815 * DataDumper function.) We don't currently have any cheap
11816 * way to estimate the size of LOs, but fortunately it doesn't
11817 * matter too much as long as we get large batches of LOs
11818 * processed reasonably early. Assume 8K per blob.
11819 */
11820 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11821 }
11822 break;
11823 case DO_POLICY:
11824 dumpPolicy(fout, (const PolicyInfo *) dobj);
11825 break;
11826 case DO_PUBLICATION:
11827 dumpPublication(fout, (const PublicationInfo *) dobj);
11828 break;
11829 case DO_PUBLICATION_REL:
11830 dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11831 break;
11834 (const PublicationSchemaInfo *) dobj);
11835 break;
11836 case DO_SUBSCRIPTION:
11837 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11838 break;
11840 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11841 break;
11842 case DO_REL_STATS:
11843 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11844 break;
11847 /* never dumped, nothing to do */
11848 break;
11849 }
11850}
11851
11852/*
11853 * dumpNamespace
11854 * writes out to fout the queries to recreate a user-defined namespace
11855 */
11856static void
11858{
11859 DumpOptions *dopt = fout->dopt;
11860 PQExpBuffer q;
11861 PQExpBuffer delq;
11862 char *qnspname;
11863
11864 /* Do nothing if not dumping schema */
11865 if (!dopt->dumpSchema)
11866 return;
11867
11868 q = createPQExpBuffer();
11869 delq = createPQExpBuffer();
11870
11871 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11872
11873 if (nspinfo->create)
11874 {
11875 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11876 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11877 }
11878 else
11879 {
11880 /* see selectDumpableNamespace() */
11882 "-- *not* dropping schema, since initdb creates it\n");
11884 "-- *not* creating schema, since initdb creates it\n");
11885 }
11886
11887 if (dopt->binary_upgrade)
11889 "SCHEMA", qnspname, NULL);
11890
11891 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11892 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11893 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11894 .owner = nspinfo->rolname,
11895 .description = "SCHEMA",
11896 .section = SECTION_PRE_DATA,
11897 .createStmt = q->data,
11898 .dropStmt = delq->data));
11899
11900 /* Dump Schema Comments and Security Labels */
11901 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11902 {
11903 const char *initdb_comment = NULL;
11904
11905 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11906 initdb_comment = "standard public schema";
11907 dumpCommentExtended(fout, "SCHEMA", qnspname,
11908 NULL, nspinfo->rolname,
11909 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11910 initdb_comment);
11911 }
11912
11913 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11914 dumpSecLabel(fout, "SCHEMA", qnspname,
11915 NULL, nspinfo->rolname,
11916 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11917
11918 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11919 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11920 qnspname, NULL, NULL,
11921 NULL, nspinfo->rolname, &nspinfo->dacl);
11922
11923 free(qnspname);
11924
11926 destroyPQExpBuffer(delq);
11927}
11928
11929/*
11930 * dumpExtension
11931 * writes out to fout the queries to recreate an extension
11932 */
11933static void
11935{
11936 DumpOptions *dopt = fout->dopt;
11937 PQExpBuffer q;
11938 PQExpBuffer delq;
11939 char *qextname;
11940
11941 /* Do nothing if not dumping schema */
11942 if (!dopt->dumpSchema)
11943 return;
11944
11945 q = createPQExpBuffer();
11946 delq = createPQExpBuffer();
11947
11948 qextname = pg_strdup(fmtId(extinfo->dobj.name));
11949
11950 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11951
11952 if (!dopt->binary_upgrade)
11953 {
11954 /*
11955 * In a regular dump, we simply create the extension, intentionally
11956 * not specifying a version, so that the destination installation's
11957 * default version is used.
11958 *
11959 * Use of IF NOT EXISTS here is unlike our behavior for other object
11960 * types; but there are various scenarios in which it's convenient to
11961 * manually create the desired extension before restoring, so we
11962 * prefer to allow it to exist already.
11963 */
11964 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11965 qextname, fmtId(extinfo->namespace));
11966 }
11967 else
11968 {
11969 /*
11970 * In binary-upgrade mode, it's critical to reproduce the state of the
11971 * database exactly, so our procedure is to create an empty extension,
11972 * restore all the contained objects normally, and add them to the
11973 * extension one by one. This function performs just the first of
11974 * those steps. binary_upgrade_extension_member() takes care of
11975 * adding member objects as they're created.
11976 */
11977 int i;
11978 int n;
11979
11980 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11981
11982 /*
11983 * We unconditionally create the extension, so we must drop it if it
11984 * exists. This could happen if the user deleted 'plpgsql' and then
11985 * readded it, causing its oid to be greater than g_last_builtin_oid.
11986 */
11987 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11988
11990 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11991 appendStringLiteralAH(q, extinfo->dobj.name, fout);
11992 appendPQExpBufferStr(q, ", ");
11993 appendStringLiteralAH(q, extinfo->namespace, fout);
11994 appendPQExpBufferStr(q, ", ");
11995 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11996 appendStringLiteralAH(q, extinfo->extversion, fout);
11997 appendPQExpBufferStr(q, ", ");
11998
11999 /*
12000 * Note that we're pushing extconfig (an OID array) back into
12001 * pg_extension exactly as-is. This is OK because pg_class OIDs are
12002 * preserved in binary upgrade.
12003 */
12004 if (strlen(extinfo->extconfig) > 2)
12005 appendStringLiteralAH(q, extinfo->extconfig, fout);
12006 else
12007 appendPQExpBufferStr(q, "NULL");
12008 appendPQExpBufferStr(q, ", ");
12009 if (strlen(extinfo->extcondition) > 2)
12010 appendStringLiteralAH(q, extinfo->extcondition, fout);
12011 else
12012 appendPQExpBufferStr(q, "NULL");
12013 appendPQExpBufferStr(q, ", ");
12014 appendPQExpBufferStr(q, "ARRAY[");
12015 n = 0;
12016 for (i = 0; i < extinfo->dobj.nDeps; i++)
12017 {
12018 DumpableObject *extobj;
12019
12020 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12021 if (extobj && extobj->objType == DO_EXTENSION)
12022 {
12023 if (n++ > 0)
12024 appendPQExpBufferChar(q, ',');
12025 appendStringLiteralAH(q, extobj->name, fout);
12026 }
12027 }
12028 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12029 appendPQExpBufferStr(q, ");\n");
12030 }
12031
12032 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12033 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12034 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12035 .description = "EXTENSION",
12036 .section = SECTION_PRE_DATA,
12037 .createStmt = q->data,
12038 .dropStmt = delq->data));
12039
12040 /* Dump Extension Comments */
12041 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12042 dumpComment(fout, "EXTENSION", qextname,
12043 NULL, "",
12044 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12045
12046 free(qextname);
12047
12049 destroyPQExpBuffer(delq);
12050}
12051
12052/*
12053 * dumpType
12054 * writes out to fout the queries to recreate a user-defined type
12055 */
12056static void
12057dumpType(Archive *fout, const TypeInfo *tyinfo)
12058{
12059 DumpOptions *dopt = fout->dopt;
12060
12061 /* Do nothing if not dumping schema */
12062 if (!dopt->dumpSchema)
12063 return;
12064
12065 /* Dump out in proper style */
12066 if (tyinfo->typtype == TYPTYPE_BASE)
12067 dumpBaseType(fout, tyinfo);
12068 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12069 dumpDomain(fout, tyinfo);
12070 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12071 dumpCompositeType(fout, tyinfo);
12072 else if (tyinfo->typtype == TYPTYPE_ENUM)
12073 dumpEnumType(fout, tyinfo);
12074 else if (tyinfo->typtype == TYPTYPE_RANGE)
12075 dumpRangeType(fout, tyinfo);
12076 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12077 dumpUndefinedType(fout, tyinfo);
12078 else
12079 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12080 tyinfo->dobj.name);
12081}
12082
12083/*
12084 * dumpEnumType
12085 * writes out to fout the queries to recreate a user-defined enum type
12086 */
12087static void
12088dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12089{
12090 DumpOptions *dopt = fout->dopt;
12094 PGresult *res;
12095 int num,
12096 i;
12097 Oid enum_oid;
12098 char *qtypname;
12099 char *qualtypname;
12100 char *label;
12101 int i_enumlabel;
12102 int i_oid;
12103
12105 {
12106 /* Set up query for enum-specific details */
12108 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12109 "SELECT oid, enumlabel "
12110 "FROM pg_catalog.pg_enum "
12111 "WHERE enumtypid = $1 "
12112 "ORDER BY enumsortorder");
12113
12114 ExecuteSqlStatement(fout, query->data);
12115
12116 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12117 }
12118
12119 printfPQExpBuffer(query,
12120 "EXECUTE dumpEnumType('%u')",
12121 tyinfo->dobj.catId.oid);
12122
12123 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12124
12125 num = PQntuples(res);
12126
12127 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12128 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12129
12130 /*
12131 * CASCADE shouldn't be required here as for normal types since the I/O
12132 * functions are generic and do not get dropped.
12133 */
12134 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12135
12136 if (dopt->binary_upgrade)
12138 tyinfo->dobj.catId.oid,
12139 false, false);
12140
12141 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12142 qualtypname);
12143
12144 if (!dopt->binary_upgrade)
12145 {
12146 i_enumlabel = PQfnumber(res, "enumlabel");
12147
12148 /* Labels with server-assigned oids */
12149 for (i = 0; i < num; i++)
12150 {
12151 label = PQgetvalue(res, i, i_enumlabel);
12152 if (i > 0)
12153 appendPQExpBufferChar(q, ',');
12154 appendPQExpBufferStr(q, "\n ");
12155 appendStringLiteralAH(q, label, fout);
12156 }
12157 }
12158
12159 appendPQExpBufferStr(q, "\n);\n");
12160
12161 if (dopt->binary_upgrade)
12162 {
12163 i_oid = PQfnumber(res, "oid");
12164 i_enumlabel = PQfnumber(res, "enumlabel");
12165
12166 /* Labels with dump-assigned (preserved) oids */
12167 for (i = 0; i < num; i++)
12168 {
12169 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12170 label = PQgetvalue(res, i, i_enumlabel);
12171
12172 if (i == 0)
12173 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12175 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12176 enum_oid);
12177 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12178 appendStringLiteralAH(q, label, fout);
12179 appendPQExpBufferStr(q, ";\n\n");
12180 }
12181 }
12182
12183 if (dopt->binary_upgrade)
12185 "TYPE", qtypname,
12186 tyinfo->dobj.namespace->dobj.name);
12187
12188 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12189 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12190 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12191 .namespace = tyinfo->dobj.namespace->dobj.name,
12192 .owner = tyinfo->rolname,
12193 .description = "TYPE",
12194 .section = SECTION_PRE_DATA,
12195 .createStmt = q->data,
12196 .dropStmt = delq->data));
12197
12198 /* Dump Type Comments and Security Labels */
12199 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12200 dumpComment(fout, "TYPE", qtypname,
12201 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12202 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12203
12204 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12205 dumpSecLabel(fout, "TYPE", qtypname,
12206 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12207 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12208
12209 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12210 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12211 qtypname, NULL,
12212 tyinfo->dobj.namespace->dobj.name,
12213 NULL, tyinfo->rolname, &tyinfo->dacl);
12214
12215 PQclear(res);
12217 destroyPQExpBuffer(delq);
12218 destroyPQExpBuffer(query);
12219 free(qtypname);
12220 free(qualtypname);
12221}
12222
12223/*
12224 * dumpRangeType
12225 * writes out to fout the queries to recreate a user-defined range type
12226 */
12227static void
12228dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12229{
12230 DumpOptions *dopt = fout->dopt;
12234 PGresult *res;
12235 Oid collationOid;
12236 char *qtypname;
12237 char *qualtypname;
12238 char *procname;
12239
12241 {
12242 /* Set up query for range-specific details */
12244 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12245
12247 "SELECT ");
12248
12249 if (fout->remoteVersion >= 140000)
12251 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12252 else
12254 "NULL AS rngmultitype, ");
12255
12257 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12258 "opc.opcname AS opcname, "
12259 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12260 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12261 "opc.opcdefault, "
12262 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12263 " ELSE rngcollation END AS collation, "
12264 "rngcanonical, rngsubdiff "
12265 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12266 " pg_catalog.pg_opclass opc "
12267 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12268 "rngtypid = $1");
12269
12270 ExecuteSqlStatement(fout, query->data);
12271
12273 }
12274
12275 printfPQExpBuffer(query,
12276 "EXECUTE dumpRangeType('%u')",
12277 tyinfo->dobj.catId.oid);
12278
12279 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12280
12281 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12282 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12283
12284 /*
12285 * CASCADE shouldn't be required here as for normal types since the I/O
12286 * functions are generic and do not get dropped.
12287 */
12288 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12289
12290 if (dopt->binary_upgrade)
12292 tyinfo->dobj.catId.oid,
12293 false, true);
12294
12295 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12296 qualtypname);
12297
12298 appendPQExpBuffer(q, "\n subtype = %s",
12299 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12300
12301 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12302 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12303 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12304
12305 /* print subtype_opclass only if not default for subtype */
12306 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12307 {
12308 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12309 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12310
12311 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12312 fmtId(nspname));
12313 appendPQExpBufferStr(q, fmtId(opcname));
12314 }
12315
12316 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12317 if (OidIsValid(collationOid))
12318 {
12319 CollInfo *coll = findCollationByOid(collationOid);
12320
12321 if (coll)
12322 appendPQExpBuffer(q, ",\n collation = %s",
12323 fmtQualifiedDumpable(coll));
12324 }
12325
12326 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12327 if (strcmp(procname, "-") != 0)
12328 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12329
12330 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12331 if (strcmp(procname, "-") != 0)
12332 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12333
12334 appendPQExpBufferStr(q, "\n);\n");
12335
12336 if (dopt->binary_upgrade)
12338 "TYPE", qtypname,
12339 tyinfo->dobj.namespace->dobj.name);
12340
12341 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12342 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12343 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12344 .namespace = tyinfo->dobj.namespace->dobj.name,
12345 .owner = tyinfo->rolname,
12346 .description = "TYPE",
12347 .section = SECTION_PRE_DATA,
12348 .createStmt = q->data,
12349 .dropStmt = delq->data));
12350
12351 /* Dump Type Comments and Security Labels */
12352 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12353 dumpComment(fout, "TYPE", qtypname,
12354 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12355 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12356
12357 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12358 dumpSecLabel(fout, "TYPE", qtypname,
12359 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12360 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12361
12362 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12363 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12364 qtypname, NULL,
12365 tyinfo->dobj.namespace->dobj.name,
12366 NULL, tyinfo->rolname, &tyinfo->dacl);
12367
12368 PQclear(res);
12370 destroyPQExpBuffer(delq);
12371 destroyPQExpBuffer(query);
12372 free(qtypname);
12373 free(qualtypname);
12374}
12375
12376/*
12377 * dumpUndefinedType
12378 * writes out to fout the queries to recreate a !typisdefined type
12379 *
12380 * This is a shell type, but we use different terminology to distinguish
12381 * this case from where we have to emit a shell type definition to break
12382 * circular dependencies. An undefined type shouldn't ever have anything
12383 * depending on it.
12384 */
12385static void
12387{
12388 DumpOptions *dopt = fout->dopt;
12391 char *qtypname;
12392 char *qualtypname;
12393
12394 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12395 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12396
12397 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12398
12399 if (dopt->binary_upgrade)
12401 tyinfo->dobj.catId.oid,
12402 false, false);
12403
12404 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12405 qualtypname);
12406
12407 if (dopt->binary_upgrade)
12409 "TYPE", qtypname,
12410 tyinfo->dobj.namespace->dobj.name);
12411
12412 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12413 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12414 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12415 .namespace = tyinfo->dobj.namespace->dobj.name,
12416 .owner = tyinfo->rolname,
12417 .description = "TYPE",
12418 .section = SECTION_PRE_DATA,
12419 .createStmt = q->data,
12420 .dropStmt = delq->data));
12421
12422 /* Dump Type Comments and Security Labels */
12423 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12424 dumpComment(fout, "TYPE", qtypname,
12425 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12426 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12427
12428 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12429 dumpSecLabel(fout, "TYPE", qtypname,
12430 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12431 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12432
12433 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12434 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12435 qtypname, NULL,
12436 tyinfo->dobj.namespace->dobj.name,
12437 NULL, tyinfo->rolname, &tyinfo->dacl);
12438
12440 destroyPQExpBuffer(delq);
12441 free(qtypname);
12442 free(qualtypname);
12443}
12444
12445/*
12446 * dumpBaseType
12447 * writes out to fout the queries to recreate a user-defined base type
12448 */
12449static void
12450dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12451{
12452 DumpOptions *dopt = fout->dopt;
12456 PGresult *res;
12457 char *qtypname;
12458 char *qualtypname;
12459 char *typlen;
12460 char *typinput;
12461 char *typoutput;
12462 char *typreceive;
12463 char *typsend;
12464 char *typmodin;
12465 char *typmodout;
12466 char *typanalyze;
12467 char *typsubscript;
12468 Oid typreceiveoid;
12469 Oid typsendoid;
12470 Oid typmodinoid;
12471 Oid typmodoutoid;
12472 Oid typanalyzeoid;
12473 Oid typsubscriptoid;
12474 char *typcategory;
12475 char *typispreferred;
12476 char *typdelim;
12477 char *typbyval;
12478 char *typalign;
12479 char *typstorage;
12480 char *typcollatable;
12481 char *typdefault;
12482 bool typdefault_is_literal = false;
12483
12485 {
12486 /* Set up query for type-specific details */
12488 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12489 "SELECT typlen, "
12490 "typinput, typoutput, typreceive, typsend, "
12491 "typreceive::pg_catalog.oid AS typreceiveoid, "
12492 "typsend::pg_catalog.oid AS typsendoid, "
12493 "typanalyze, "
12494 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12495 "typdelim, typbyval, typalign, typstorage, "
12496 "typmodin, typmodout, "
12497 "typmodin::pg_catalog.oid AS typmodinoid, "
12498 "typmodout::pg_catalog.oid AS typmodoutoid, "
12499 "typcategory, typispreferred, "
12500 "(typcollation <> 0) AS typcollatable, "
12501 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12502
12503 if (fout->remoteVersion >= 140000)
12505 "typsubscript, "
12506 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12507 else
12509 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12510
12511 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12512 "WHERE oid = $1");
12513
12514 ExecuteSqlStatement(fout, query->data);
12515
12516 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12517 }
12518
12519 printfPQExpBuffer(query,
12520 "EXECUTE dumpBaseType('%u')",
12521 tyinfo->dobj.catId.oid);
12522
12523 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12524
12525 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12526 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12527 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12528 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12529 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12530 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12531 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12532 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12533 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12534 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12535 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12536 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12537 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12538 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12539 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12540 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12541 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12542 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12543 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12544 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12545 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12546 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12547 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12548 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12549 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12550 {
12551 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12552 typdefault_is_literal = true; /* it needs quotes */
12553 }
12554 else
12555 typdefault = NULL;
12556
12557 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12558 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12559
12560 /*
12561 * The reason we include CASCADE is that the circular dependency between
12562 * the type and its I/O functions makes it impossible to drop the type any
12563 * other way.
12564 */
12565 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12566
12567 /*
12568 * We might already have a shell type, but setting pg_type_oid is
12569 * harmless, and in any case we'd better set the array type OID.
12570 */
12571 if (dopt->binary_upgrade)
12573 tyinfo->dobj.catId.oid,
12574 false, false);
12575
12577 "CREATE TYPE %s (\n"
12578 " INTERNALLENGTH = %s",
12579 qualtypname,
12580 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12581
12582 /* regproc result is sufficiently quoted already */
12583 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12584 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12585 if (OidIsValid(typreceiveoid))
12586 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12587 if (OidIsValid(typsendoid))
12588 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12589 if (OidIsValid(typmodinoid))
12590 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12591 if (OidIsValid(typmodoutoid))
12592 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12593 if (OidIsValid(typanalyzeoid))
12594 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12595
12596 if (strcmp(typcollatable, "t") == 0)
12597 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12598
12599 if (typdefault != NULL)
12600 {
12601 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12602 if (typdefault_is_literal)
12603 appendStringLiteralAH(q, typdefault, fout);
12604 else
12605 appendPQExpBufferStr(q, typdefault);
12606 }
12607
12608 if (OidIsValid(typsubscriptoid))
12609 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12610
12611 if (OidIsValid(tyinfo->typelem))
12612 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12613 getFormattedTypeName(fout, tyinfo->typelem,
12614 zeroIsError));
12615
12616 if (strcmp(typcategory, "U") != 0)
12617 {
12618 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12619 appendStringLiteralAH(q, typcategory, fout);
12620 }
12621
12622 if (strcmp(typispreferred, "t") == 0)
12623 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12624
12625 if (typdelim && strcmp(typdelim, ",") != 0)
12626 {
12627 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12628 appendStringLiteralAH(q, typdelim, fout);
12629 }
12630
12631 if (*typalign == TYPALIGN_CHAR)
12632 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12633 else if (*typalign == TYPALIGN_SHORT)
12634 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12635 else if (*typalign == TYPALIGN_INT)
12636 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12637 else if (*typalign == TYPALIGN_DOUBLE)
12638 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12639
12640 if (*typstorage == TYPSTORAGE_PLAIN)
12641 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12642 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12643 appendPQExpBufferStr(q, ",\n STORAGE = external");
12644 else if (*typstorage == TYPSTORAGE_EXTENDED)
12645 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12646 else if (*typstorage == TYPSTORAGE_MAIN)
12647 appendPQExpBufferStr(q, ",\n STORAGE = main");
12648
12649 if (strcmp(typbyval, "t") == 0)
12650 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12651
12652 appendPQExpBufferStr(q, "\n);\n");
12653
12654 if (dopt->binary_upgrade)
12656 "TYPE", qtypname,
12657 tyinfo->dobj.namespace->dobj.name);
12658
12659 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12660 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12661 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12662 .namespace = tyinfo->dobj.namespace->dobj.name,
12663 .owner = tyinfo->rolname,
12664 .description = "TYPE",
12665 .section = SECTION_PRE_DATA,
12666 .createStmt = q->data,
12667 .dropStmt = delq->data));
12668
12669 /* Dump Type Comments and Security Labels */
12670 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12671 dumpComment(fout, "TYPE", qtypname,
12672 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12673 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12674
12675 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12676 dumpSecLabel(fout, "TYPE", qtypname,
12677 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12678 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12679
12680 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12681 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12682 qtypname, NULL,
12683 tyinfo->dobj.namespace->dobj.name,
12684 NULL, tyinfo->rolname, &tyinfo->dacl);
12685
12686 PQclear(res);
12688 destroyPQExpBuffer(delq);
12689 destroyPQExpBuffer(query);
12690 free(qtypname);
12691 free(qualtypname);
12692}
12693
12694/*
12695 * dumpDomain
12696 * writes out to fout the queries to recreate a user-defined domain
12697 */
12698static void
12699dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12700{
12701 DumpOptions *dopt = fout->dopt;
12705 PGresult *res;
12706 int i;
12707 char *qtypname;
12708 char *qualtypname;
12709 char *typnotnull;
12710 char *typdefn;
12711 char *typdefault;
12712 Oid typcollation;
12713 bool typdefault_is_literal = false;
12714
12716 {
12717 /* Set up query for domain-specific details */
12719 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12720
12721 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12722 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12723 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12724 "t.typdefault, "
12725 "CASE WHEN t.typcollation <> u.typcollation "
12726 "THEN t.typcollation ELSE 0 END AS typcollation "
12727 "FROM pg_catalog.pg_type t "
12728 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12729 "WHERE t.oid = $1");
12730
12731 ExecuteSqlStatement(fout, query->data);
12732
12733 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12734 }
12735
12736 printfPQExpBuffer(query,
12737 "EXECUTE dumpDomain('%u')",
12738 tyinfo->dobj.catId.oid);
12739
12740 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12741
12742 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12743 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12744 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12745 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12746 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12747 {
12748 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12749 typdefault_is_literal = true; /* it needs quotes */
12750 }
12751 else
12752 typdefault = NULL;
12753 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12754
12755 if (dopt->binary_upgrade)
12757 tyinfo->dobj.catId.oid,
12758 true, /* force array type */
12759 false); /* force multirange type */
12760
12761 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12762 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12763
12765 "CREATE DOMAIN %s AS %s",
12766 qualtypname,
12767 typdefn);
12768
12769 /* Print collation only if different from base type's collation */
12770 if (OidIsValid(typcollation))
12771 {
12772 CollInfo *coll;
12773
12774 coll = findCollationByOid(typcollation);
12775 if (coll)
12776 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12777 }
12778
12779 /*
12780 * Print a not-null constraint if there's one. In servers older than 17
12781 * these don't have names, so just print it unadorned; in newer ones they
12782 * do, but most of the time it's going to be the standard generated one,
12783 * so omit the name in that case also.
12784 */
12785 if (typnotnull[0] == 't')
12786 {
12787 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12788 appendPQExpBufferStr(q, " NOT NULL");
12789 else
12790 {
12791 ConstraintInfo *notnull = tyinfo->notnull;
12792
12793 if (!notnull->separate)
12794 {
12795 char *default_name;
12796
12797 /* XXX should match ChooseConstraintName better */
12798 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12799
12800 if (strcmp(default_name, notnull->dobj.name) == 0)
12801 appendPQExpBufferStr(q, " NOT NULL");
12802 else
12803 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12804 fmtId(notnull->dobj.name), notnull->condef);
12805 free(default_name);
12806 }
12807 }
12808 }
12809
12810 if (typdefault != NULL)
12811 {
12812 appendPQExpBufferStr(q, " DEFAULT ");
12813 if (typdefault_is_literal)
12814 appendStringLiteralAH(q, typdefault, fout);
12815 else
12816 appendPQExpBufferStr(q, typdefault);
12817 }
12818
12819 PQclear(res);
12820
12821 /*
12822 * Add any CHECK constraints for the domain
12823 */
12824 for (i = 0; i < tyinfo->nDomChecks; i++)
12825 {
12826 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12827
12828 if (!domcheck->separate && domcheck->contype == 'c')
12829 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12830 fmtId(domcheck->dobj.name), domcheck->condef);
12831 }
12832
12833 appendPQExpBufferStr(q, ";\n");
12834
12835 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12836
12837 if (dopt->binary_upgrade)
12839 "DOMAIN", qtypname,
12840 tyinfo->dobj.namespace->dobj.name);
12841
12842 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12843 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12844 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12845 .namespace = tyinfo->dobj.namespace->dobj.name,
12846 .owner = tyinfo->rolname,
12847 .description = "DOMAIN",
12848 .section = SECTION_PRE_DATA,
12849 .createStmt = q->data,
12850 .dropStmt = delq->data));
12851
12852 /* Dump Domain Comments and Security Labels */
12853 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12854 dumpComment(fout, "DOMAIN", qtypname,
12855 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12856 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12857
12858 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12859 dumpSecLabel(fout, "DOMAIN", qtypname,
12860 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12861 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12862
12863 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12864 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12865 qtypname, NULL,
12866 tyinfo->dobj.namespace->dobj.name,
12867 NULL, tyinfo->rolname, &tyinfo->dacl);
12868
12869 /* Dump any per-constraint comments */
12870 for (i = 0; i < tyinfo->nDomChecks; i++)
12871 {
12872 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12873 PQExpBuffer conprefix;
12874
12875 /* but only if the constraint itself was dumped here */
12876 if (domcheck->separate)
12877 continue;
12878
12879 conprefix = createPQExpBuffer();
12880 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12881 fmtId(domcheck->dobj.name));
12882
12883 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12884 dumpComment(fout, conprefix->data, qtypname,
12885 tyinfo->dobj.namespace->dobj.name,
12886 tyinfo->rolname,
12887 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12888
12889 destroyPQExpBuffer(conprefix);
12890 }
12891
12892 /*
12893 * And a comment on the not-null constraint, if there's one -- but only if
12894 * the constraint itself was dumped here
12895 */
12896 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12897 {
12898 PQExpBuffer conprefix = createPQExpBuffer();
12899
12900 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12901 fmtId(tyinfo->notnull->dobj.name));
12902
12903 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12904 dumpComment(fout, conprefix->data, qtypname,
12905 tyinfo->dobj.namespace->dobj.name,
12906 tyinfo->rolname,
12907 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12908 destroyPQExpBuffer(conprefix);
12909 }
12910
12912 destroyPQExpBuffer(delq);
12913 destroyPQExpBuffer(query);
12914 free(qtypname);
12915 free(qualtypname);
12916}
12917
12918/*
12919 * dumpCompositeType
12920 * writes out to fout the queries to recreate a user-defined stand-alone
12921 * composite type
12922 */
12923static void
12925{
12926 DumpOptions *dopt = fout->dopt;
12928 PQExpBuffer dropped = createPQExpBuffer();
12931 PGresult *res;
12932 char *qtypname;
12933 char *qualtypname;
12934 int ntups;
12935 int i_attname;
12936 int i_atttypdefn;
12937 int i_attlen;
12938 int i_attalign;
12939 int i_attisdropped;
12940 int i_attcollation;
12941 int i;
12942 int actual_atts;
12943
12945 {
12946 /*
12947 * Set up query for type-specific details.
12948 *
12949 * Since we only want to dump COLLATE clauses for attributes whose
12950 * collation is different from their type's default, we use a CASE
12951 * here to suppress uninteresting attcollations cheaply. atttypid
12952 * will be 0 for dropped columns; collation does not matter for those.
12953 */
12955 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12956 "SELECT a.attname, a.attnum, "
12957 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12958 "a.attlen, a.attalign, a.attisdropped, "
12959 "CASE WHEN a.attcollation <> at.typcollation "
12960 "THEN a.attcollation ELSE 0 END AS attcollation "
12961 "FROM pg_catalog.pg_type ct "
12962 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12963 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12964 "WHERE ct.oid = $1 "
12965 "ORDER BY a.attnum");
12966
12967 ExecuteSqlStatement(fout, query->data);
12968
12970 }
12971
12972 printfPQExpBuffer(query,
12973 "EXECUTE dumpCompositeType('%u')",
12974 tyinfo->dobj.catId.oid);
12975
12976 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12977
12978 ntups = PQntuples(res);
12979
12980 i_attname = PQfnumber(res, "attname");
12981 i_atttypdefn = PQfnumber(res, "atttypdefn");
12982 i_attlen = PQfnumber(res, "attlen");
12983 i_attalign = PQfnumber(res, "attalign");
12984 i_attisdropped = PQfnumber(res, "attisdropped");
12985 i_attcollation = PQfnumber(res, "attcollation");
12986
12987 if (dopt->binary_upgrade)
12988 {
12990 tyinfo->dobj.catId.oid,
12991 false, false);
12993 }
12994
12995 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12996 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12997
12998 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12999 qualtypname);
13000
13001 actual_atts = 0;
13002 for (i = 0; i < ntups; i++)
13003 {
13004 char *attname;
13005 char *atttypdefn;
13006 char *attlen;
13007 char *attalign;
13008 bool attisdropped;
13009 Oid attcollation;
13010
13011 attname = PQgetvalue(res, i, i_attname);
13012 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13013 attlen = PQgetvalue(res, i, i_attlen);
13014 attalign = PQgetvalue(res, i, i_attalign);
13015 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13016 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13017
13018 if (attisdropped && !dopt->binary_upgrade)
13019 continue;
13020
13021 /* Format properly if not first attr */
13022 if (actual_atts++ > 0)
13023 appendPQExpBufferChar(q, ',');
13024 appendPQExpBufferStr(q, "\n\t");
13025
13026 if (!attisdropped)
13027 {
13028 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13029
13030 /* Add collation if not default for the column type */
13031 if (OidIsValid(attcollation))
13032 {
13033 CollInfo *coll;
13034
13035 coll = findCollationByOid(attcollation);
13036 if (coll)
13037 appendPQExpBuffer(q, " COLLATE %s",
13038 fmtQualifiedDumpable(coll));
13039 }
13040 }
13041 else
13042 {
13043 /*
13044 * This is a dropped attribute and we're in binary_upgrade mode.
13045 * Insert a placeholder for it in the CREATE TYPE command, and set
13046 * length and alignment with direct UPDATE to the catalogs
13047 * afterwards. See similar code in dumpTableSchema().
13048 */
13049 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13050
13051 /* stash separately for insertion after the CREATE TYPE */
13052 appendPQExpBufferStr(dropped,
13053 "\n-- For binary upgrade, recreate dropped column.\n");
13054 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13055 "SET attlen = %s, "
13056 "attalign = '%s', attbyval = false\n"
13057 "WHERE attname = ", attlen, attalign);
13058 appendStringLiteralAH(dropped, attname, fout);
13059 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13060 appendStringLiteralAH(dropped, qualtypname, fout);
13061 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13062
13063 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13064 qualtypname);
13065 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13066 fmtId(attname));
13067 }
13068 }
13069 appendPQExpBufferStr(q, "\n);\n");
13070 appendPQExpBufferStr(q, dropped->data);
13071
13072 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13073
13074 if (dopt->binary_upgrade)
13076 "TYPE", qtypname,
13077 tyinfo->dobj.namespace->dobj.name);
13078
13079 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13080 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13081 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13082 .namespace = tyinfo->dobj.namespace->dobj.name,
13083 .owner = tyinfo->rolname,
13084 .description = "TYPE",
13085 .section = SECTION_PRE_DATA,
13086 .createStmt = q->data,
13087 .dropStmt = delq->data));
13088
13089
13090 /* Dump Type Comments and Security Labels */
13091 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13092 dumpComment(fout, "TYPE", qtypname,
13093 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13094 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13095
13096 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13097 dumpSecLabel(fout, "TYPE", qtypname,
13098 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13099 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13100
13101 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13102 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13103 qtypname, NULL,
13104 tyinfo->dobj.namespace->dobj.name,
13105 NULL, tyinfo->rolname, &tyinfo->dacl);
13106
13107 /* Dump any per-column comments */
13108 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13109 dumpCompositeTypeColComments(fout, tyinfo, res);
13110
13111 PQclear(res);
13113 destroyPQExpBuffer(dropped);
13114 destroyPQExpBuffer(delq);
13115 destroyPQExpBuffer(query);
13116 free(qtypname);
13117 free(qualtypname);
13118}
13119
13120/*
13121 * dumpCompositeTypeColComments
13122 * writes out to fout the queries to recreate comments on the columns of
13123 * a user-defined stand-alone composite type.
13124 *
13125 * The caller has already made a query to collect the names and attnums
13126 * of the type's columns, so we just pass that result into here rather
13127 * than reading them again.
13128 */
13129static void
13131 PGresult *res)
13132{
13134 int ncomments;
13135 PQExpBuffer query;
13136 PQExpBuffer target;
13137 int i;
13138 int ntups;
13139 int i_attname;
13140 int i_attnum;
13141 int i_attisdropped;
13142
13143 /* do nothing, if --no-comments is supplied */
13144 if (fout->dopt->no_comments)
13145 return;
13146
13147 /* Search for comments associated with type's pg_class OID */
13148 ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13149 &comments);
13150
13151 /* If no comments exist, we're done */
13152 if (ncomments <= 0)
13153 return;
13154
13155 /* Build COMMENT ON statements */
13156 query = createPQExpBuffer();
13157 target = createPQExpBuffer();
13158
13159 ntups = PQntuples(res);
13160 i_attnum = PQfnumber(res, "attnum");
13161 i_attname = PQfnumber(res, "attname");
13162 i_attisdropped = PQfnumber(res, "attisdropped");
13163 while (ncomments > 0)
13164 {
13165 const char *attname;
13166
13167 attname = NULL;
13168 for (i = 0; i < ntups; i++)
13169 {
13170 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13171 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13172 {
13173 attname = PQgetvalue(res, i, i_attname);
13174 break;
13175 }
13176 }
13177 if (attname) /* just in case we don't find it */
13178 {
13179 const char *descr = comments->descr;
13180
13181 resetPQExpBuffer(target);
13182 appendPQExpBuffer(target, "COLUMN %s.",
13183 fmtId(tyinfo->dobj.name));
13185
13186 resetPQExpBuffer(query);
13187 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13188 fmtQualifiedDumpable(tyinfo));
13189 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13190 appendStringLiteralAH(query, descr, fout);
13191 appendPQExpBufferStr(query, ";\n");
13192
13194 ARCHIVE_OPTS(.tag = target->data,
13195 .namespace = tyinfo->dobj.namespace->dobj.name,
13196 .owner = tyinfo->rolname,
13197 .description = "COMMENT",
13198 .section = SECTION_NONE,
13199 .createStmt = query->data,
13200 .deps = &(tyinfo->dobj.dumpId),
13201 .nDeps = 1));
13202 }
13203
13204 comments++;
13205 ncomments--;
13206 }
13207
13208 destroyPQExpBuffer(query);
13209 destroyPQExpBuffer(target);
13210}
13211
13212/*
13213 * dumpShellType
13214 * writes out to fout the queries to create a shell type
13215 *
13216 * We dump a shell definition in advance of the I/O functions for the type.
13217 */
13218static void
13220{
13221 DumpOptions *dopt = fout->dopt;
13222 PQExpBuffer q;
13223
13224 /* Do nothing if not dumping schema */
13225 if (!dopt->dumpSchema)
13226 return;
13227
13228 q = createPQExpBuffer();
13229
13230 /*
13231 * Note the lack of a DROP command for the shell type; any required DROP
13232 * is driven off the base type entry, instead. This interacts with
13233 * _printTocEntry()'s use of the presence of a DROP command to decide
13234 * whether an entry needs an ALTER OWNER command. We don't want to alter
13235 * the shell type's owner immediately on creation; that should happen only
13236 * after it's filled in, otherwise the backend complains.
13237 */
13238
13239 if (dopt->binary_upgrade)
13241 stinfo->baseType->dobj.catId.oid,
13242 false, false);
13243
13244 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13245 fmtQualifiedDumpable(stinfo));
13246
13247 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13248 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13249 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13250 .namespace = stinfo->dobj.namespace->dobj.name,
13251 .owner = stinfo->baseType->rolname,
13252 .description = "SHELL TYPE",
13253 .section = SECTION_PRE_DATA,
13254 .createStmt = q->data));
13255
13257}
13258
13259/*
13260 * dumpProcLang
13261 * writes out to fout the queries to recreate a user-defined
13262 * procedural language
13263 */
13264static void
13266{
13267 DumpOptions *dopt = fout->dopt;
13268 PQExpBuffer defqry;
13269 PQExpBuffer delqry;
13270 bool useParams;
13271 char *qlanname;
13272 FuncInfo *funcInfo;
13273 FuncInfo *inlineInfo = NULL;
13274 FuncInfo *validatorInfo = NULL;
13275
13276 /* Do nothing if not dumping schema */
13277 if (!dopt->dumpSchema)
13278 return;
13279
13280 /*
13281 * Try to find the support function(s). It is not an error if we don't
13282 * find them --- if the functions are in the pg_catalog schema, as is
13283 * standard in 8.1 and up, then we won't have loaded them. (In this case
13284 * we will emit a parameterless CREATE LANGUAGE command, which will
13285 * require PL template knowledge in the backend to reload.)
13286 */
13287
13288 funcInfo = findFuncByOid(plang->lanplcallfoid);
13289 if (funcInfo != NULL && !funcInfo->dobj.dump)
13290 funcInfo = NULL; /* treat not-dumped same as not-found */
13291
13292 if (OidIsValid(plang->laninline))
13293 {
13294 inlineInfo = findFuncByOid(plang->laninline);
13295 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13296 inlineInfo = NULL;
13297 }
13298
13299 if (OidIsValid(plang->lanvalidator))
13300 {
13301 validatorInfo = findFuncByOid(plang->lanvalidator);
13302 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13303 validatorInfo = NULL;
13304 }
13305
13306 /*
13307 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13308 * parameters. Otherwise, we'll write a parameterless command, which will
13309 * be interpreted as CREATE EXTENSION.
13310 */
13311 useParams = (funcInfo != NULL &&
13312 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13313 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13314
13315 defqry = createPQExpBuffer();
13316 delqry = createPQExpBuffer();
13317
13318 qlanname = pg_strdup(fmtId(plang->dobj.name));
13319
13320 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13321 qlanname);
13322
13323 if (useParams)
13324 {
13325 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13326 plang->lanpltrusted ? "TRUSTED " : "",
13327 qlanname);
13328 appendPQExpBuffer(defqry, " HANDLER %s",
13329 fmtQualifiedDumpable(funcInfo));
13330 if (OidIsValid(plang->laninline))
13331 appendPQExpBuffer(defqry, " INLINE %s",
13332 fmtQualifiedDumpable(inlineInfo));
13333 if (OidIsValid(plang->lanvalidator))
13334 appendPQExpBuffer(defqry, " VALIDATOR %s",
13335 fmtQualifiedDumpable(validatorInfo));
13336 }
13337 else
13338 {
13339 /*
13340 * If not dumping parameters, then use CREATE OR REPLACE so that the
13341 * command will not fail if the language is preinstalled in the target
13342 * database.
13343 *
13344 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13345 * EXISTS; perhaps we should emit that instead? But it might just add
13346 * confusion.
13347 */
13348 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13349 qlanname);
13350 }
13351 appendPQExpBufferStr(defqry, ";\n");
13352
13353 if (dopt->binary_upgrade)
13354 binary_upgrade_extension_member(defqry, &plang->dobj,
13355 "LANGUAGE", qlanname, NULL);
13356
13357 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13358 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13359 ARCHIVE_OPTS(.tag = plang->dobj.name,
13360 .owner = plang->lanowner,
13361 .description = "PROCEDURAL LANGUAGE",
13362 .section = SECTION_PRE_DATA,
13363 .createStmt = defqry->data,
13364 .dropStmt = delqry->data,
13365 ));
13366
13367 /* Dump Proc Lang Comments and Security Labels */
13368 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13369 dumpComment(fout, "LANGUAGE", qlanname,
13370 NULL, plang->lanowner,
13371 plang->dobj.catId, 0, plang->dobj.dumpId);
13372
13373 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13374 dumpSecLabel(fout, "LANGUAGE", qlanname,
13375 NULL, plang->lanowner,
13376 plang->dobj.catId, 0, plang->dobj.dumpId);
13377
13378 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13379 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13380 qlanname, NULL, NULL,
13381 NULL, plang->lanowner, &plang->dacl);
13382
13383 free(qlanname);
13384
13385 destroyPQExpBuffer(defqry);
13386 destroyPQExpBuffer(delqry);
13387}
13388
13389/*
13390 * format_function_arguments: generate function name and argument list
13391 *
13392 * This is used when we can rely on pg_get_function_arguments to format
13393 * the argument list. Note, however, that pg_get_function_arguments
13394 * does not special-case zero-argument aggregates.
13395 */
13396static char *
13397format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13398{
13400
13403 if (is_agg && finfo->nargs == 0)
13404 appendPQExpBufferStr(&fn, "(*)");
13405 else
13406 appendPQExpBuffer(&fn, "(%s)", funcargs);
13407 return fn.data;
13408}
13409
13410/*
13411 * format_function_signature: generate function name and argument list
13412 *
13413 * Only a minimal list of input argument types is generated; this is
13414 * sufficient to reference the function, but not to define it.
13415 *
13416 * If honor_quotes is false then the function name is never quoted.
13417 * This is appropriate for use in TOC tags, but not in SQL commands.
13418 */
13419static char *
13420format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13421{
13423 int j;
13424
13426 if (honor_quotes)
13427 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13428 else
13429 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13430 for (j = 0; j < finfo->nargs; j++)
13431 {
13432 if (j > 0)
13433 appendPQExpBufferStr(&fn, ", ");
13434
13436 getFormattedTypeName(fout, finfo->argtypes[j],
13437 zeroIsError));
13438 }
13440 return fn.data;
13441}
13442
13443
13444/*
13445 * dumpFunc:
13446 * dump out one function
13447 */
13448static void
13449dumpFunc(Archive *fout, const FuncInfo *finfo)
13450{
13451 DumpOptions *dopt = fout->dopt;
13452 PQExpBuffer query;
13453 PQExpBuffer q;
13454 PQExpBuffer delqry;
13455 PQExpBuffer asPart;
13456 PGresult *res;
13457 char *funcsig; /* identity signature */
13458 char *funcfullsig = NULL; /* full signature */
13459 char *funcsig_tag;
13460 char *qual_funcsig;
13461 char *proretset;
13462 char *prosrc;
13463 char *probin;
13464 char *prosqlbody;
13465 char *funcargs;
13466 char *funciargs;
13467 char *funcresult;
13468 char *protrftypes;
13469 char *prokind;
13470 char *provolatile;
13471 char *proisstrict;
13472 char *prosecdef;
13473 char *proleakproof;
13474 char *proconfig;
13475 char *procost;
13476 char *prorows;
13477 char *prosupport;
13478 char *proparallel;
13479 char *lanname;
13480 char **configitems = NULL;
13481 int nconfigitems = 0;
13482 const char *keyword;
13483
13484 /* Do nothing if not dumping schema */
13485 if (!dopt->dumpSchema)
13486 return;
13487
13488 query = createPQExpBuffer();
13489 q = createPQExpBuffer();
13490 delqry = createPQExpBuffer();
13491 asPart = createPQExpBuffer();
13492
13493 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13494 {
13495 /* Set up query for function-specific details */
13497 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13498
13500 "SELECT\n"
13501 "proretset,\n"
13502 "prosrc,\n"
13503 "probin,\n"
13504 "provolatile,\n"
13505 "proisstrict,\n"
13506 "prosecdef,\n"
13507 "lanname,\n"
13508 "proconfig,\n"
13509 "procost,\n"
13510 "prorows,\n"
13511 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13512 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13513 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13514 "proleakproof,\n");
13515
13516 if (fout->remoteVersion >= 90500)
13518 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13519 else
13521 "NULL AS protrftypes,\n");
13522
13523 if (fout->remoteVersion >= 90600)
13525 "proparallel,\n");
13526 else
13528 "'u' AS proparallel,\n");
13529
13530 if (fout->remoteVersion >= 110000)
13532 "prokind,\n");
13533 else
13535 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13536
13537 if (fout->remoteVersion >= 120000)
13539 "prosupport,\n");
13540 else
13542 "'-' AS prosupport,\n");
13543
13544 if (fout->remoteVersion >= 140000)
13546 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13547 else
13549 "NULL AS prosqlbody\n");
13550
13552 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13553 "WHERE p.oid = $1 "
13554 "AND l.oid = p.prolang");
13555
13556 ExecuteSqlStatement(fout, query->data);
13557
13558 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13559 }
13560
13561 printfPQExpBuffer(query,
13562 "EXECUTE dumpFunc('%u')",
13563 finfo->dobj.catId.oid);
13564
13565 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13566
13567 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13568 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13569 {
13570 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13571 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13572 prosqlbody = NULL;
13573 }
13574 else
13575 {
13576 prosrc = NULL;
13577 probin = NULL;
13578 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13579 }
13580 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13581 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13582 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13583 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13584 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13585 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13586 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13587 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13588 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13589 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13590 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13591 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13592 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13593 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13594 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13595
13596 /*
13597 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13598 * is used.
13599 */
13600 if (prosqlbody)
13601 {
13602 appendPQExpBufferStr(asPart, prosqlbody);
13603 }
13604 else if (probin[0] != '\0')
13605 {
13606 appendPQExpBufferStr(asPart, "AS ");
13607 appendStringLiteralAH(asPart, probin, fout);
13608 if (prosrc[0] != '\0')
13609 {
13610 appendPQExpBufferStr(asPart, ", ");
13611
13612 /*
13613 * where we have bin, use dollar quoting if allowed and src
13614 * contains quote or backslash; else use regular quoting.
13615 */
13616 if (dopt->disable_dollar_quoting ||
13617 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13618 appendStringLiteralAH(asPart, prosrc, fout);
13619 else
13620 appendStringLiteralDQ(asPart, prosrc, NULL);
13621 }
13622 }
13623 else
13624 {
13625 appendPQExpBufferStr(asPart, "AS ");
13626 /* with no bin, dollar quote src unconditionally if allowed */
13627 if (dopt->disable_dollar_quoting)
13628 appendStringLiteralAH(asPart, prosrc, fout);
13629 else
13630 appendStringLiteralDQ(asPart, prosrc, NULL);
13631 }
13632
13633 if (*proconfig)
13634 {
13635 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13636 pg_fatal("could not parse %s array", "proconfig");
13637 }
13638 else
13639 {
13640 configitems = NULL;
13641 nconfigitems = 0;
13642 }
13643
13644 funcfullsig = format_function_arguments(finfo, funcargs, false);
13645 funcsig = format_function_arguments(finfo, funciargs, false);
13646
13647 funcsig_tag = format_function_signature(fout, finfo, false);
13648
13649 qual_funcsig = psprintf("%s.%s",
13650 fmtId(finfo->dobj.namespace->dobj.name),
13651 funcsig);
13652
13653 if (prokind[0] == PROKIND_PROCEDURE)
13654 keyword = "PROCEDURE";
13655 else
13656 keyword = "FUNCTION"; /* works for window functions too */
13657
13658 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13659 keyword, qual_funcsig);
13660
13661 appendPQExpBuffer(q, "CREATE %s %s.%s",
13662 keyword,
13663 fmtId(finfo->dobj.namespace->dobj.name),
13664 funcfullsig ? funcfullsig :
13665 funcsig);
13666
13667 if (prokind[0] == PROKIND_PROCEDURE)
13668 /* no result type to output */ ;
13669 else if (funcresult)
13670 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13671 else
13672 appendPQExpBuffer(q, " RETURNS %s%s",
13673 (proretset[0] == 't') ? "SETOF " : "",
13674 getFormattedTypeName(fout, finfo->prorettype,
13675 zeroIsError));
13676
13677 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13678
13679 if (*protrftypes)
13680 {
13681 Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13682 int i;
13683
13684 appendPQExpBufferStr(q, " TRANSFORM ");
13685 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13686 for (i = 0; typeids[i]; i++)
13687 {
13688 if (i != 0)
13689 appendPQExpBufferStr(q, ", ");
13690 appendPQExpBuffer(q, "FOR TYPE %s",
13691 getFormattedTypeName(fout, typeids[i], zeroAsNone));
13692 }
13693
13694 free(typeids);
13695 }
13696
13697 if (prokind[0] == PROKIND_WINDOW)
13698 appendPQExpBufferStr(q, " WINDOW");
13699
13700 if (provolatile[0] != PROVOLATILE_VOLATILE)
13701 {
13702 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13703 appendPQExpBufferStr(q, " IMMUTABLE");
13704 else if (provolatile[0] == PROVOLATILE_STABLE)
13705 appendPQExpBufferStr(q, " STABLE");
13706 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13707 pg_fatal("unrecognized provolatile value for function \"%s\"",
13708 finfo->dobj.name);
13709 }
13710
13711 if (proisstrict[0] == 't')
13712 appendPQExpBufferStr(q, " STRICT");
13713
13714 if (prosecdef[0] == 't')
13715 appendPQExpBufferStr(q, " SECURITY DEFINER");
13716
13717 if (proleakproof[0] == 't')
13718 appendPQExpBufferStr(q, " LEAKPROOF");
13719
13720 /*
13721 * COST and ROWS are emitted only if present and not default, so as not to
13722 * break backwards-compatibility of the dump without need. Keep this code
13723 * in sync with the defaults in functioncmds.c.
13724 */
13725 if (strcmp(procost, "0") != 0)
13726 {
13727 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13728 {
13729 /* default cost is 1 */
13730 if (strcmp(procost, "1") != 0)
13731 appendPQExpBuffer(q, " COST %s", procost);
13732 }
13733 else
13734 {
13735 /* default cost is 100 */
13736 if (strcmp(procost, "100") != 0)
13737 appendPQExpBuffer(q, " COST %s", procost);
13738 }
13739 }
13740 if (proretset[0] == 't' &&
13741 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13742 appendPQExpBuffer(q, " ROWS %s", prorows);
13743
13744 if (strcmp(prosupport, "-") != 0)
13745 {
13746 /* We rely on regprocout to provide quoting and qualification */
13747 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13748 }
13749
13750 if (proparallel[0] != PROPARALLEL_UNSAFE)
13751 {
13752 if (proparallel[0] == PROPARALLEL_SAFE)
13753 appendPQExpBufferStr(q, " PARALLEL SAFE");
13754 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13755 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13756 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13757 pg_fatal("unrecognized proparallel value for function \"%s\"",
13758 finfo->dobj.name);
13759 }
13760
13761 for (int i = 0; i < nconfigitems; i++)
13762 {
13763 /* we feel free to scribble on configitems[] here */
13764 char *configitem = configitems[i];
13765 char *pos;
13766
13767 pos = strchr(configitem, '=');
13768 if (pos == NULL)
13769 continue;
13770 *pos++ = '\0';
13771 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13772
13773 /*
13774 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13775 * by flatten_set_variable_args() before they were put into the
13776 * proconfig array. However, because the quoting rules used there
13777 * aren't exactly like SQL's, we have to break the list value apart
13778 * and then quote the elements as string literals. (The elements may
13779 * be double-quoted as-is, but we can't just feed them to the SQL
13780 * parser; it would do the wrong thing with elements that are
13781 * zero-length or longer than NAMEDATALEN.) Also, we need a special
13782 * case for empty lists.
13783 *
13784 * Variables that are not so marked should just be emitted as simple
13785 * string literals. If the variable is not known to
13786 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13787 * to use GUC_LIST_QUOTE for extension variables.
13788 */
13789 if (variable_is_guc_list_quote(configitem))
13790 {
13791 char **namelist;
13792 char **nameptr;
13793
13794 /* Parse string into list of identifiers */
13795 /* this shouldn't fail really */
13796 if (SplitGUCList(pos, ',', &namelist))
13797 {
13798 /* Special case: represent an empty list as NULL */
13799 if (*namelist == NULL)
13800 appendPQExpBufferStr(q, "NULL");
13801 for (nameptr = namelist; *nameptr; nameptr++)
13802 {
13803 if (nameptr != namelist)
13804 appendPQExpBufferStr(q, ", ");
13805 appendStringLiteralAH(q, *nameptr, fout);
13806 }
13807 }
13808 pg_free(namelist);
13809 }
13810 else
13811 appendStringLiteralAH(q, pos, fout);
13812 }
13813
13814 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13815
13816 append_depends_on_extension(fout, q, &finfo->dobj,
13817 "pg_catalog.pg_proc", keyword,
13818 qual_funcsig);
13819
13820 if (dopt->binary_upgrade)
13822 keyword, funcsig,
13823 finfo->dobj.namespace->dobj.name);
13824
13825 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13826 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13827 ARCHIVE_OPTS(.tag = funcsig_tag,
13828 .namespace = finfo->dobj.namespace->dobj.name,
13829 .owner = finfo->rolname,
13830 .description = keyword,
13831 .section = finfo->postponed_def ?
13833 .createStmt = q->data,
13834 .dropStmt = delqry->data));
13835
13836 /* Dump Function Comments and Security Labels */
13837 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13838 dumpComment(fout, keyword, funcsig,
13839 finfo->dobj.namespace->dobj.name, finfo->rolname,
13840 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13841
13842 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13843 dumpSecLabel(fout, keyword, funcsig,
13844 finfo->dobj.namespace->dobj.name, finfo->rolname,
13845 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13846
13847 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13848 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13849 funcsig, NULL,
13850 finfo->dobj.namespace->dobj.name,
13851 NULL, finfo->rolname, &finfo->dacl);
13852
13853 PQclear(res);
13854
13855 destroyPQExpBuffer(query);
13857 destroyPQExpBuffer(delqry);
13858 destroyPQExpBuffer(asPart);
13859 free(funcsig);
13860 free(funcfullsig);
13861 free(funcsig_tag);
13862 free(qual_funcsig);
13863 free(configitems);
13864}
13865
13866
13867/*
13868 * Dump a user-defined cast
13869 */
13870static void
13871dumpCast(Archive *fout, const CastInfo *cast)
13872{
13873 DumpOptions *dopt = fout->dopt;
13874 PQExpBuffer defqry;
13875 PQExpBuffer delqry;
13876 PQExpBuffer labelq;
13877 PQExpBuffer castargs;
13878 FuncInfo *funcInfo = NULL;
13879 const char *sourceType;
13880 const char *targetType;
13881
13882 /* Do nothing if not dumping schema */
13883 if (!dopt->dumpSchema)
13884 return;
13885
13886 /* Cannot dump if we don't have the cast function's info */
13887 if (OidIsValid(cast->castfunc))
13888 {
13889 funcInfo = findFuncByOid(cast->castfunc);
13890 if (funcInfo == NULL)
13891 pg_fatal("could not find function definition for function with OID %u",
13892 cast->castfunc);
13893 }
13894
13895 defqry = createPQExpBuffer();
13896 delqry = createPQExpBuffer();
13897 labelq = createPQExpBuffer();
13898 castargs = createPQExpBuffer();
13899
13900 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13901 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13902 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13903 sourceType, targetType);
13904
13905 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13906 sourceType, targetType);
13907
13908 switch (cast->castmethod)
13909 {
13910 case COERCION_METHOD_BINARY:
13911 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13912 break;
13913 case COERCION_METHOD_INOUT:
13914 appendPQExpBufferStr(defqry, "WITH INOUT");
13915 break;
13916 case COERCION_METHOD_FUNCTION:
13917 if (funcInfo)
13918 {
13919 char *fsig = format_function_signature(fout, funcInfo, true);
13920
13921 /*
13922 * Always qualify the function name (format_function_signature
13923 * won't qualify it).
13924 */
13925 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13926 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13927 free(fsig);
13928 }
13929 else
13930 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13931 break;
13932 default:
13933 pg_log_warning("bogus value in pg_cast.castmethod field");
13934 }
13935
13936 if (cast->castcontext == 'a')
13937 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13938 else if (cast->castcontext == 'i')
13939 appendPQExpBufferStr(defqry, " AS IMPLICIT");
13940 appendPQExpBufferStr(defqry, ";\n");
13941
13942 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13943 sourceType, targetType);
13944
13945 appendPQExpBuffer(castargs, "(%s AS %s)",
13946 sourceType, targetType);
13947
13948 if (dopt->binary_upgrade)
13950 "CAST", castargs->data, NULL);
13951
13953 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13954 ARCHIVE_OPTS(.tag = labelq->data,
13955 .description = "CAST",
13956 .section = SECTION_PRE_DATA,
13957 .createStmt = defqry->data,
13958 .dropStmt = delqry->data));
13959
13960 /* Dump Cast Comments */
13961 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13962 dumpComment(fout, "CAST", castargs->data,
13963 NULL, "",
13964 cast->dobj.catId, 0, cast->dobj.dumpId);
13965
13966 destroyPQExpBuffer(defqry);
13967 destroyPQExpBuffer(delqry);
13968 destroyPQExpBuffer(labelq);
13969 destroyPQExpBuffer(castargs);
13970}
13971
13972/*
13973 * Dump a transform
13974 */
13975static void
13976dumpTransform(Archive *fout, const TransformInfo *transform)
13977{
13978 DumpOptions *dopt = fout->dopt;
13979 PQExpBuffer defqry;
13980 PQExpBuffer delqry;
13981 PQExpBuffer labelq;
13982 PQExpBuffer transformargs;
13983 FuncInfo *fromsqlFuncInfo = NULL;
13984 FuncInfo *tosqlFuncInfo = NULL;
13985 char *lanname;
13986 const char *transformType;
13987
13988 /* Do nothing if not dumping schema */
13989 if (!dopt->dumpSchema)
13990 return;
13991
13992 /* Cannot dump if we don't have the transform functions' info */
13993 if (OidIsValid(transform->trffromsql))
13994 {
13995 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13996 if (fromsqlFuncInfo == NULL)
13997 pg_fatal("could not find function definition for function with OID %u",
13998 transform->trffromsql);
13999 }
14000 if (OidIsValid(transform->trftosql))
14001 {
14002 tosqlFuncInfo = findFuncByOid(transform->trftosql);
14003 if (tosqlFuncInfo == NULL)
14004 pg_fatal("could not find function definition for function with OID %u",
14005 transform->trftosql);
14006 }
14007
14008 defqry = createPQExpBuffer();
14009 delqry = createPQExpBuffer();
14010 labelq = createPQExpBuffer();
14011 transformargs = createPQExpBuffer();
14012
14013 lanname = get_language_name(fout, transform->trflang);
14014 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14015
14016 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14017 transformType, lanname);
14018
14019 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14020 transformType, lanname);
14021
14022 if (!transform->trffromsql && !transform->trftosql)
14023 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14024
14025 if (transform->trffromsql)
14026 {
14027 if (fromsqlFuncInfo)
14028 {
14029 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14030
14031 /*
14032 * Always qualify the function name (format_function_signature
14033 * won't qualify it).
14034 */
14035 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14036 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14037 free(fsig);
14038 }
14039 else
14040 pg_log_warning("bogus value in pg_transform.trffromsql field");
14041 }
14042
14043 if (transform->trftosql)
14044 {
14045 if (transform->trffromsql)
14046 appendPQExpBufferStr(defqry, ", ");
14047
14048 if (tosqlFuncInfo)
14049 {
14050 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14051
14052 /*
14053 * Always qualify the function name (format_function_signature
14054 * won't qualify it).
14055 */
14056 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14057 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14058 free(fsig);
14059 }
14060 else
14061 pg_log_warning("bogus value in pg_transform.trftosql field");
14062 }
14063
14064 appendPQExpBufferStr(defqry, ");\n");
14065
14066 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14067 transformType, lanname);
14068
14069 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14070 transformType, lanname);
14071
14072 if (dopt->binary_upgrade)
14073 binary_upgrade_extension_member(defqry, &transform->dobj,
14074 "TRANSFORM", transformargs->data, NULL);
14075
14076 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14077 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14078 ARCHIVE_OPTS(.tag = labelq->data,
14079 .description = "TRANSFORM",
14080 .section = SECTION_PRE_DATA,
14081 .createStmt = defqry->data,
14082 .dropStmt = delqry->data,
14083 .deps = transform->dobj.dependencies,
14084 .nDeps = transform->dobj.nDeps));
14085
14086 /* Dump Transform Comments */
14087 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14088 dumpComment(fout, "TRANSFORM", transformargs->data,
14089 NULL, "",
14090 transform->dobj.catId, 0, transform->dobj.dumpId);
14091
14092 free(lanname);
14093 destroyPQExpBuffer(defqry);
14094 destroyPQExpBuffer(delqry);
14095 destroyPQExpBuffer(labelq);
14096 destroyPQExpBuffer(transformargs);
14097}
14098
14099
14100/*
14101 * dumpOpr
14102 * write out a single operator definition
14103 */
14104static void
14105dumpOpr(Archive *fout, const OprInfo *oprinfo)
14106{
14107 DumpOptions *dopt = fout->dopt;
14108 PQExpBuffer query;
14109 PQExpBuffer q;
14110 PQExpBuffer delq;
14112 PQExpBuffer details;
14113 PGresult *res;
14114 int i_oprkind;
14115 int i_oprcode;
14116 int i_oprleft;
14117 int i_oprright;
14118 int i_oprcom;
14119 int i_oprnegate;
14120 int i_oprrest;
14121 int i_oprjoin;
14122 int i_oprcanmerge;
14123 int i_oprcanhash;
14124 char *oprkind;
14125 char *oprcode;
14126 char *oprleft;
14127 char *oprright;
14128 char *oprcom;
14129 char *oprnegate;
14130 char *oprrest;
14131 char *oprjoin;
14132 char *oprcanmerge;
14133 char *oprcanhash;
14134 char *oprregproc;
14135 char *oprref;
14136
14137 /* Do nothing if not dumping schema */
14138 if (!dopt->dumpSchema)
14139 return;
14140
14141 /*
14142 * some operators are invalid because they were the result of user
14143 * defining operators before commutators exist
14144 */
14145 if (!OidIsValid(oprinfo->oprcode))
14146 return;
14147
14148 query = createPQExpBuffer();
14149 q = createPQExpBuffer();
14150 delq = createPQExpBuffer();
14152 details = createPQExpBuffer();
14153
14154 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14155 {
14156 /* Set up query for operator-specific details */
14158 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14159 "SELECT oprkind, "
14160 "oprcode::pg_catalog.regprocedure, "
14161 "oprleft::pg_catalog.regtype, "
14162 "oprright::pg_catalog.regtype, "
14163 "oprcom, "
14164 "oprnegate, "
14165 "oprrest::pg_catalog.regprocedure, "
14166 "oprjoin::pg_catalog.regprocedure, "
14167 "oprcanmerge, oprcanhash "
14168 "FROM pg_catalog.pg_operator "
14169 "WHERE oid = $1");
14170
14171 ExecuteSqlStatement(fout, query->data);
14172
14173 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14174 }
14175
14176 printfPQExpBuffer(query,
14177 "EXECUTE dumpOpr('%u')",
14178 oprinfo->dobj.catId.oid);
14179
14180 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14181
14182 i_oprkind = PQfnumber(res, "oprkind");
14183 i_oprcode = PQfnumber(res, "oprcode");
14184 i_oprleft = PQfnumber(res, "oprleft");
14185 i_oprright = PQfnumber(res, "oprright");
14186 i_oprcom = PQfnumber(res, "oprcom");
14187 i_oprnegate = PQfnumber(res, "oprnegate");
14188 i_oprrest = PQfnumber(res, "oprrest");
14189 i_oprjoin = PQfnumber(res, "oprjoin");
14190 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14191 i_oprcanhash = PQfnumber(res, "oprcanhash");
14192
14193 oprkind = PQgetvalue(res, 0, i_oprkind);
14194 oprcode = PQgetvalue(res, 0, i_oprcode);
14195 oprleft = PQgetvalue(res, 0, i_oprleft);
14196 oprright = PQgetvalue(res, 0, i_oprright);
14197 oprcom = PQgetvalue(res, 0, i_oprcom);
14198 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14199 oprrest = PQgetvalue(res, 0, i_oprrest);
14200 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14201 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14202 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14203
14204 /* In PG14 upwards postfix operator support does not exist anymore. */
14205 if (strcmp(oprkind, "r") == 0)
14206 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14207 oprcode);
14208
14209 oprregproc = convertRegProcReference(oprcode);
14210 if (oprregproc)
14211 {
14212 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14213 free(oprregproc);
14214 }
14215
14216 appendPQExpBuffer(oprid, "%s (",
14217 oprinfo->dobj.name);
14218
14219 /*
14220 * right unary means there's a left arg and left unary means there's a
14221 * right arg. (Although the "r" case is dead code for PG14 and later,
14222 * continue to support it in case we're dumping from an old server.)
14223 */
14224 if (strcmp(oprkind, "r") == 0 ||
14225 strcmp(oprkind, "b") == 0)
14226 {
14227 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14228 appendPQExpBufferStr(oprid, oprleft);
14229 }
14230 else
14231 appendPQExpBufferStr(oprid, "NONE");
14232
14233 if (strcmp(oprkind, "l") == 0 ||
14234 strcmp(oprkind, "b") == 0)
14235 {
14236 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14237 appendPQExpBuffer(oprid, ", %s)", oprright);
14238 }
14239 else
14240 appendPQExpBufferStr(oprid, ", NONE)");
14241
14242 oprref = getFormattedOperatorName(oprcom);
14243 if (oprref)
14244 {
14245 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14246 free(oprref);
14247 }
14248
14249 oprref = getFormattedOperatorName(oprnegate);
14250 if (oprref)
14251 {
14252 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14253 free(oprref);
14254 }
14255
14256 if (strcmp(oprcanmerge, "t") == 0)
14257 appendPQExpBufferStr(details, ",\n MERGES");
14258
14259 if (strcmp(oprcanhash, "t") == 0)
14260 appendPQExpBufferStr(details, ",\n HASHES");
14261
14262 oprregproc = convertRegProcReference(oprrest);
14263 if (oprregproc)
14264 {
14265 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14266 free(oprregproc);
14267 }
14268
14269 oprregproc = convertRegProcReference(oprjoin);
14270 if (oprregproc)
14271 {
14272 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14273 free(oprregproc);
14274 }
14275
14276 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14277 fmtId(oprinfo->dobj.namespace->dobj.name),
14278 oprid->data);
14279
14280 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14281 fmtId(oprinfo->dobj.namespace->dobj.name),
14282 oprinfo->dobj.name, details->data);
14283
14284 if (dopt->binary_upgrade)
14286 "OPERATOR", oprid->data,
14287 oprinfo->dobj.namespace->dobj.name);
14288
14289 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14290 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14291 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14292 .namespace = oprinfo->dobj.namespace->dobj.name,
14293 .owner = oprinfo->rolname,
14294 .description = "OPERATOR",
14295 .section = SECTION_PRE_DATA,
14296 .createStmt = q->data,
14297 .dropStmt = delq->data));
14298
14299 /* Dump Operator Comments */
14300 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14301 dumpComment(fout, "OPERATOR", oprid->data,
14302 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14303 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14304
14305 PQclear(res);
14306
14307 destroyPQExpBuffer(query);
14309 destroyPQExpBuffer(delq);
14311 destroyPQExpBuffer(details);
14312}
14313
14314/*
14315 * Convert a function reference obtained from pg_operator
14316 *
14317 * Returns allocated string of what to print, or NULL if function references
14318 * is InvalidOid. Returned string is expected to be free'd by the caller.
14319 *
14320 * The input is a REGPROCEDURE display; we have to strip the argument-types
14321 * part.
14322 */
14323static char *
14325{
14326 char *name;
14327 char *paren;
14328 bool inquote;
14329
14330 /* In all cases "-" means a null reference */
14331 if (strcmp(proc, "-") == 0)
14332 return NULL;
14333
14334 name = pg_strdup(proc);
14335 /* find non-double-quoted left paren */
14336 inquote = false;
14337 for (paren = name; *paren; paren++)
14338 {
14339 if (*paren == '(' && !inquote)
14340 {
14341 *paren = '\0';
14342 break;
14343 }
14344 if (*paren == '"')
14345 inquote = !inquote;
14346 }
14347 return name;
14348}
14349
14350/*
14351 * getFormattedOperatorName - retrieve the operator name for the
14352 * given operator OID (presented in string form).
14353 *
14354 * Returns an allocated string, or NULL if the given OID is invalid.
14355 * Caller is responsible for free'ing result string.
14356 *
14357 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14358 * useful in commands where the operator's argument types can be inferred from
14359 * context. We always schema-qualify the name, though. The predecessor to
14360 * this code tried to skip the schema qualification if possible, but that led
14361 * to wrong results in corner cases, such as if an operator and its negator
14362 * are in different schemas.
14363 */
14364static char *
14365getFormattedOperatorName(const char *oproid)
14366{
14367 OprInfo *oprInfo;
14368
14369 /* In all cases "0" means a null reference */
14370 if (strcmp(oproid, "0") == 0)
14371 return NULL;
14372
14373 oprInfo = findOprByOid(atooid(oproid));
14374 if (oprInfo == NULL)
14375 {
14376 pg_log_warning("could not find operator with OID %s",
14377 oproid);
14378 return NULL;
14379 }
14380
14381 return psprintf("OPERATOR(%s.%s)",
14382 fmtId(oprInfo->dobj.namespace->dobj.name),
14383 oprInfo->dobj.name);
14384}
14385
14386/*
14387 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14388 *
14389 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14390 * argument lists of these functions are predetermined. Note that the
14391 * caller should ensure we are in the proper schema, because the results
14392 * are search path dependent!
14393 */
14394static char *
14396{
14397 char *result;
14398 char query[128];
14399 PGresult *res;
14400
14401 snprintf(query, sizeof(query),
14402 "SELECT '%u'::pg_catalog.regproc", funcOid);
14403 res = ExecuteSqlQueryForSingleRow(fout, query);
14404
14405 result = pg_strdup(PQgetvalue(res, 0, 0));
14406
14407 PQclear(res);
14408
14409 return result;
14410}
14411
14412/*
14413 * dumpAccessMethod
14414 * write out a single access method definition
14415 */
14416static void
14418{
14419 DumpOptions *dopt = fout->dopt;
14420 PQExpBuffer q;
14421 PQExpBuffer delq;
14422 char *qamname;
14423
14424 /* Do nothing if not dumping schema */
14425 if (!dopt->dumpSchema)
14426 return;
14427
14428 q = createPQExpBuffer();
14429 delq = createPQExpBuffer();
14430
14431 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14432
14433 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14434
14435 switch (aminfo->amtype)
14436 {
14437 case AMTYPE_INDEX:
14438 appendPQExpBufferStr(q, "TYPE INDEX ");
14439 break;
14440 case AMTYPE_TABLE:
14441 appendPQExpBufferStr(q, "TYPE TABLE ");
14442 break;
14443 default:
14444 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14445 aminfo->amtype, qamname);
14447 destroyPQExpBuffer(delq);
14448 free(qamname);
14449 return;
14450 }
14451
14452 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14453
14454 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14455 qamname);
14456
14457 if (dopt->binary_upgrade)
14459 "ACCESS METHOD", qamname, NULL);
14460
14461 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14462 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14463 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14464 .description = "ACCESS METHOD",
14465 .section = SECTION_PRE_DATA,
14466 .createStmt = q->data,
14467 .dropStmt = delq->data));
14468
14469 /* Dump Access Method Comments */
14470 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14471 dumpComment(fout, "ACCESS METHOD", qamname,
14472 NULL, "",
14473 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14474
14476 destroyPQExpBuffer(delq);
14477 free(qamname);
14478}
14479
14480/*
14481 * dumpOpclass
14482 * write out a single operator class definition
14483 */
14484static void
14485dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14486{
14487 DumpOptions *dopt = fout->dopt;
14488 PQExpBuffer query;
14489 PQExpBuffer q;
14490 PQExpBuffer delq;
14491 PQExpBuffer nameusing;
14492 PGresult *res;
14493 int ntups;
14494 int i_opcintype;
14495 int i_opckeytype;
14496 int i_opcdefault;
14497 int i_opcfamily;
14498 int i_opcfamilyname;
14499 int i_opcfamilynsp;
14500 int i_amname;
14501 int i_amopstrategy;
14502 int i_amopopr;
14503 int i_sortfamily;
14504 int i_sortfamilynsp;
14505 int i_amprocnum;
14506 int i_amproc;
14507 int i_amproclefttype;
14508 int i_amprocrighttype;
14509 char *opcintype;
14510 char *opckeytype;
14511 char *opcdefault;
14512 char *opcfamily;
14513 char *opcfamilyname;
14514 char *opcfamilynsp;
14515 char *amname;
14516 char *amopstrategy;
14517 char *amopopr;
14518 char *sortfamily;
14519 char *sortfamilynsp;
14520 char *amprocnum;
14521 char *amproc;
14522 char *amproclefttype;
14523 char *amprocrighttype;
14524 bool needComma;
14525 int i;
14526
14527 /* Do nothing if not dumping schema */
14528 if (!dopt->dumpSchema)
14529 return;
14530
14531 query = createPQExpBuffer();
14532 q = createPQExpBuffer();
14533 delq = createPQExpBuffer();
14534 nameusing = createPQExpBuffer();
14535
14536 /* Get additional fields from the pg_opclass row */
14537 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14538 "opckeytype::pg_catalog.regtype, "
14539 "opcdefault, opcfamily, "
14540 "opfname AS opcfamilyname, "
14541 "nspname AS opcfamilynsp, "
14542 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14543 "FROM pg_catalog.pg_opclass c "
14544 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14545 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14546 "WHERE c.oid = '%u'::pg_catalog.oid",
14547 opcinfo->dobj.catId.oid);
14548
14549 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14550
14551 i_opcintype = PQfnumber(res, "opcintype");
14552 i_opckeytype = PQfnumber(res, "opckeytype");
14553 i_opcdefault = PQfnumber(res, "opcdefault");
14554 i_opcfamily = PQfnumber(res, "opcfamily");
14555 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14556 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14557 i_amname = PQfnumber(res, "amname");
14558
14559 /* opcintype may still be needed after we PQclear res */
14560 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14561 opckeytype = PQgetvalue(res, 0, i_opckeytype);
14562 opcdefault = PQgetvalue(res, 0, i_opcdefault);
14563 /* opcfamily will still be needed after we PQclear res */
14564 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14565 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14566 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14567 /* amname will still be needed after we PQclear res */
14568 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14569
14570 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14571 fmtQualifiedDumpable(opcinfo));
14572 appendPQExpBuffer(delq, " USING %s;\n",
14573 fmtId(amname));
14574
14575 /* Build the fixed portion of the CREATE command */
14576 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14577 fmtQualifiedDumpable(opcinfo));
14578 if (strcmp(opcdefault, "t") == 0)
14579 appendPQExpBufferStr(q, "DEFAULT ");
14580 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14581 opcintype,
14582 fmtId(amname));
14583 if (strlen(opcfamilyname) > 0)
14584 {
14585 appendPQExpBufferStr(q, " FAMILY ");
14586 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14587 appendPQExpBufferStr(q, fmtId(opcfamilyname));
14588 }
14589 appendPQExpBufferStr(q, " AS\n ");
14590
14591 needComma = false;
14592
14593 if (strcmp(opckeytype, "-") != 0)
14594 {
14595 appendPQExpBuffer(q, "STORAGE %s",
14596 opckeytype);
14597 needComma = true;
14598 }
14599
14600 PQclear(res);
14601
14602 /*
14603 * Now fetch and print the OPERATOR entries (pg_amop rows).
14604 *
14605 * Print only those opfamily members that are tied to the opclass by
14606 * pg_depend entries.
14607 */
14608 resetPQExpBuffer(query);
14609 appendPQExpBuffer(query, "SELECT amopstrategy, "
14610 "amopopr::pg_catalog.regoperator, "
14611 "opfname AS sortfamily, "
14612 "nspname AS sortfamilynsp "
14613 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14614 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14615 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14616 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14617 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14618 "AND refobjid = '%u'::pg_catalog.oid "
14619 "AND amopfamily = '%s'::pg_catalog.oid "
14620 "ORDER BY amopstrategy",
14621 opcinfo->dobj.catId.oid,
14622 opcfamily);
14623
14624 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14625
14626 ntups = PQntuples(res);
14627
14628 i_amopstrategy = PQfnumber(res, "amopstrategy");
14629 i_amopopr = PQfnumber(res, "amopopr");
14630 i_sortfamily = PQfnumber(res, "sortfamily");
14631 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14632
14633 for (i = 0; i < ntups; i++)
14634 {
14635 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14636 amopopr = PQgetvalue(res, i, i_amopopr);
14637 sortfamily = PQgetvalue(res, i, i_sortfamily);
14638 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14639
14640 if (needComma)
14641 appendPQExpBufferStr(q, " ,\n ");
14642
14643 appendPQExpBuffer(q, "OPERATOR %s %s",
14644 amopstrategy, amopopr);
14645
14646 if (strlen(sortfamily) > 0)
14647 {
14648 appendPQExpBufferStr(q, " FOR ORDER BY ");
14649 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14650 appendPQExpBufferStr(q, fmtId(sortfamily));
14651 }
14652
14653 needComma = true;
14654 }
14655
14656 PQclear(res);
14657
14658 /*
14659 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14660 *
14661 * Print only those opfamily members that are tied to the opclass by
14662 * pg_depend entries.
14663 *
14664 * We print the amproclefttype/amprocrighttype even though in most cases
14665 * the backend could deduce the right values, because of the corner case
14666 * of a btree sort support function for a cross-type comparison.
14667 */
14668 resetPQExpBuffer(query);
14669
14670 appendPQExpBuffer(query, "SELECT amprocnum, "
14671 "amproc::pg_catalog.regprocedure, "
14672 "amproclefttype::pg_catalog.regtype, "
14673 "amprocrighttype::pg_catalog.regtype "
14674 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14675 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14676 "AND refobjid = '%u'::pg_catalog.oid "
14677 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14678 "AND objid = ap.oid "
14679 "ORDER BY amprocnum",
14680 opcinfo->dobj.catId.oid);
14681
14682 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14683
14684 ntups = PQntuples(res);
14685
14686 i_amprocnum = PQfnumber(res, "amprocnum");
14687 i_amproc = PQfnumber(res, "amproc");
14688 i_amproclefttype = PQfnumber(res, "amproclefttype");
14689 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14690
14691 for (i = 0; i < ntups; i++)
14692 {
14693 amprocnum = PQgetvalue(res, i, i_amprocnum);
14694 amproc = PQgetvalue(res, i, i_amproc);
14695 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14696 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14697
14698 if (needComma)
14699 appendPQExpBufferStr(q, " ,\n ");
14700
14701 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14702
14703 if (*amproclefttype && *amprocrighttype)
14704 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14705
14706 appendPQExpBuffer(q, " %s", amproc);
14707
14708 needComma = true;
14709 }
14710
14711 PQclear(res);
14712
14713 /*
14714 * If needComma is still false it means we haven't added anything after
14715 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14716 * clause with the same datatype. This isn't sanctioned by the
14717 * documentation, but actually DefineOpClass will treat it as a no-op.
14718 */
14719 if (!needComma)
14720 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14721
14722 appendPQExpBufferStr(q, ";\n");
14723
14724 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14725 appendPQExpBuffer(nameusing, " USING %s",
14726 fmtId(amname));
14727
14728 if (dopt->binary_upgrade)
14730 "OPERATOR CLASS", nameusing->data,
14731 opcinfo->dobj.namespace->dobj.name);
14732
14733 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14734 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14735 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14736 .namespace = opcinfo->dobj.namespace->dobj.name,
14737 .owner = opcinfo->rolname,
14738 .description = "OPERATOR CLASS",
14739 .section = SECTION_PRE_DATA,
14740 .createStmt = q->data,
14741 .dropStmt = delq->data));
14742
14743 /* Dump Operator Class Comments */
14744 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14745 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14746 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14747 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14748
14749 free(opcintype);
14750 free(opcfamily);
14751 free(amname);
14752 destroyPQExpBuffer(query);
14754 destroyPQExpBuffer(delq);
14755 destroyPQExpBuffer(nameusing);
14756}
14757
14758/*
14759 * dumpOpfamily
14760 * write out a single operator family definition
14761 *
14762 * Note: this also dumps any "loose" operator members that aren't bound to a
14763 * specific opclass within the opfamily.
14764 */
14765static void
14766dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14767{
14768 DumpOptions *dopt = fout->dopt;
14769 PQExpBuffer query;
14770 PQExpBuffer q;
14771 PQExpBuffer delq;
14772 PQExpBuffer nameusing;
14773 PGresult *res;
14774 PGresult *res_ops;
14775 PGresult *res_procs;
14776 int ntups;
14777 int i_amname;
14778 int i_amopstrategy;
14779 int i_amopopr;
14780 int i_sortfamily;
14781 int i_sortfamilynsp;
14782 int i_amprocnum;
14783 int i_amproc;
14784 int i_amproclefttype;
14785 int i_amprocrighttype;
14786 char *amname;
14787 char *amopstrategy;
14788 char *amopopr;
14789 char *sortfamily;
14790 char *sortfamilynsp;
14791 char *amprocnum;
14792 char *amproc;
14793 char *amproclefttype;
14794 char *amprocrighttype;
14795 bool needComma;
14796 int i;
14797
14798 /* Do nothing if not dumping schema */
14799 if (!dopt->dumpSchema)
14800 return;
14801
14802 query = createPQExpBuffer();
14803 q = createPQExpBuffer();
14804 delq = createPQExpBuffer();
14805 nameusing = createPQExpBuffer();
14806
14807 /*
14808 * Fetch only those opfamily members that are tied directly to the
14809 * opfamily by pg_depend entries.
14810 */
14811 appendPQExpBuffer(query, "SELECT amopstrategy, "
14812 "amopopr::pg_catalog.regoperator, "
14813 "opfname AS sortfamily, "
14814 "nspname AS sortfamilynsp "
14815 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14816 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14817 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14818 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14819 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14820 "AND refobjid = '%u'::pg_catalog.oid "
14821 "AND amopfamily = '%u'::pg_catalog.oid "
14822 "ORDER BY amopstrategy",
14823 opfinfo->dobj.catId.oid,
14824 opfinfo->dobj.catId.oid);
14825
14826 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14827
14828 resetPQExpBuffer(query);
14829
14830 appendPQExpBuffer(query, "SELECT amprocnum, "
14831 "amproc::pg_catalog.regprocedure, "
14832 "amproclefttype::pg_catalog.regtype, "
14833 "amprocrighttype::pg_catalog.regtype "
14834 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14835 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14836 "AND refobjid = '%u'::pg_catalog.oid "
14837 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14838 "AND objid = ap.oid "
14839 "ORDER BY amprocnum",
14840 opfinfo->dobj.catId.oid);
14841
14842 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14843
14844 /* Get additional fields from the pg_opfamily row */
14845 resetPQExpBuffer(query);
14846
14847 appendPQExpBuffer(query, "SELECT "
14848 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14849 "FROM pg_catalog.pg_opfamily "
14850 "WHERE oid = '%u'::pg_catalog.oid",
14851 opfinfo->dobj.catId.oid);
14852
14853 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14854
14855 i_amname = PQfnumber(res, "amname");
14856
14857 /* amname will still be needed after we PQclear res */
14858 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14859
14860 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14861 fmtQualifiedDumpable(opfinfo));
14862 appendPQExpBuffer(delq, " USING %s;\n",
14863 fmtId(amname));
14864
14865 /* Build the fixed portion of the CREATE command */
14866 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14867 fmtQualifiedDumpable(opfinfo));
14868 appendPQExpBuffer(q, " USING %s;\n",
14869 fmtId(amname));
14870
14871 PQclear(res);
14872
14873 /* Do we need an ALTER to add loose members? */
14874 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14875 {
14876 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14877 fmtQualifiedDumpable(opfinfo));
14878 appendPQExpBuffer(q, " USING %s ADD\n ",
14879 fmtId(amname));
14880
14881 needComma = false;
14882
14883 /*
14884 * Now fetch and print the OPERATOR entries (pg_amop rows).
14885 */
14886 ntups = PQntuples(res_ops);
14887
14888 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14889 i_amopopr = PQfnumber(res_ops, "amopopr");
14890 i_sortfamily = PQfnumber(res_ops, "sortfamily");
14891 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14892
14893 for (i = 0; i < ntups; i++)
14894 {
14895 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14896 amopopr = PQgetvalue(res_ops, i, i_amopopr);
14897 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14898 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14899
14900 if (needComma)
14901 appendPQExpBufferStr(q, " ,\n ");
14902
14903 appendPQExpBuffer(q, "OPERATOR %s %s",
14904 amopstrategy, amopopr);
14905
14906 if (strlen(sortfamily) > 0)
14907 {
14908 appendPQExpBufferStr(q, " FOR ORDER BY ");
14909 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14910 appendPQExpBufferStr(q, fmtId(sortfamily));
14911 }
14912
14913 needComma = true;
14914 }
14915
14916 /*
14917 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14918 */
14919 ntups = PQntuples(res_procs);
14920
14921 i_amprocnum = PQfnumber(res_procs, "amprocnum");
14922 i_amproc = PQfnumber(res_procs, "amproc");
14923 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14924 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14925
14926 for (i = 0; i < ntups; i++)
14927 {
14928 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14929 amproc = PQgetvalue(res_procs, i, i_amproc);
14930 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14931 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14932
14933 if (needComma)
14934 appendPQExpBufferStr(q, " ,\n ");
14935
14936 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14937 amprocnum, amproclefttype, amprocrighttype,
14938 amproc);
14939
14940 needComma = true;
14941 }
14942
14943 appendPQExpBufferStr(q, ";\n");
14944 }
14945
14946 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14947 appendPQExpBuffer(nameusing, " USING %s",
14948 fmtId(amname));
14949
14950 if (dopt->binary_upgrade)
14952 "OPERATOR FAMILY", nameusing->data,
14953 opfinfo->dobj.namespace->dobj.name);
14954
14955 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14956 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14957 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14958 .namespace = opfinfo->dobj.namespace->dobj.name,
14959 .owner = opfinfo->rolname,
14960 .description = "OPERATOR FAMILY",
14961 .section = SECTION_PRE_DATA,
14962 .createStmt = q->data,
14963 .dropStmt = delq->data));
14964
14965 /* Dump Operator Family Comments */
14966 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14967 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14968 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14969 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14970
14971 free(amname);
14972 PQclear(res_ops);
14973 PQclear(res_procs);
14974 destroyPQExpBuffer(query);
14976 destroyPQExpBuffer(delq);
14977 destroyPQExpBuffer(nameusing);
14978}
14979
14980/*
14981 * dumpCollation
14982 * write out a single collation definition
14983 */
14984static void
14985dumpCollation(Archive *fout, const CollInfo *collinfo)
14986{
14987 DumpOptions *dopt = fout->dopt;
14988 PQExpBuffer query;
14989 PQExpBuffer q;
14990 PQExpBuffer delq;
14991 char *qcollname;
14992 PGresult *res;
14993 int i_collprovider;
14994 int i_collisdeterministic;
14995 int i_collcollate;
14996 int i_collctype;
14997 int i_colllocale;
14998 int i_collicurules;
14999 const char *collprovider;
15000 const char *collcollate;
15001 const char *collctype;
15002 const char *colllocale;
15003 const char *collicurules;
15004
15005 /* Do nothing if not dumping schema */
15006 if (!dopt->dumpSchema)
15007 return;
15008
15009 query = createPQExpBuffer();
15010 q = createPQExpBuffer();
15011 delq = createPQExpBuffer();
15012
15013 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15014
15015 /* Get collation-specific details */
15016 appendPQExpBufferStr(query, "SELECT ");
15017
15018 if (fout->remoteVersion >= 100000)
15020 "collprovider, "
15021 "collversion, ");
15022 else
15024 "'c' AS collprovider, "
15025 "NULL AS collversion, ");
15026
15027 if (fout->remoteVersion >= 120000)
15029 "collisdeterministic, ");
15030 else
15032 "true AS collisdeterministic, ");
15033
15034 if (fout->remoteVersion >= 170000)
15036 "colllocale, ");
15037 else if (fout->remoteVersion >= 150000)
15039 "colliculocale AS colllocale, ");
15040 else
15042 "NULL AS colllocale, ");
15043
15044 if (fout->remoteVersion >= 160000)
15046 "collicurules, ");
15047 else
15049 "NULL AS collicurules, ");
15050
15051 appendPQExpBuffer(query,
15052 "collcollate, "
15053 "collctype "
15054 "FROM pg_catalog.pg_collation c "
15055 "WHERE c.oid = '%u'::pg_catalog.oid",
15056 collinfo->dobj.catId.oid);
15057
15058 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15059
15060 i_collprovider = PQfnumber(res, "collprovider");
15061 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15062 i_collcollate = PQfnumber(res, "collcollate");
15063 i_collctype = PQfnumber(res, "collctype");
15064 i_colllocale = PQfnumber(res, "colllocale");
15065 i_collicurules = PQfnumber(res, "collicurules");
15066
15067 collprovider = PQgetvalue(res, 0, i_collprovider);
15068
15069 if (!PQgetisnull(res, 0, i_collcollate))
15070 collcollate = PQgetvalue(res, 0, i_collcollate);
15071 else
15072 collcollate = NULL;
15073
15074 if (!PQgetisnull(res, 0, i_collctype))
15075 collctype = PQgetvalue(res, 0, i_collctype);
15076 else
15077 collctype = NULL;
15078
15079 /*
15080 * Before version 15, collcollate and collctype were of type NAME and
15081 * non-nullable. Treat empty strings as NULL for consistency.
15082 */
15083 if (fout->remoteVersion < 150000)
15084 {
15085 if (collcollate[0] == '\0')
15086 collcollate = NULL;
15087 if (collctype[0] == '\0')
15088 collctype = NULL;
15089 }
15090
15091 if (!PQgetisnull(res, 0, i_colllocale))
15092 colllocale = PQgetvalue(res, 0, i_colllocale);
15093 else
15094 colllocale = NULL;
15095
15096 if (!PQgetisnull(res, 0, i_collicurules))
15097 collicurules = PQgetvalue(res, 0, i_collicurules);
15098 else
15099 collicurules = NULL;
15100
15101 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15102 fmtQualifiedDumpable(collinfo));
15103
15104 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15105 fmtQualifiedDumpable(collinfo));
15106
15107 appendPQExpBufferStr(q, "provider = ");
15108 if (collprovider[0] == 'b')
15109 appendPQExpBufferStr(q, "builtin");
15110 else if (collprovider[0] == 'c')
15111 appendPQExpBufferStr(q, "libc");
15112 else if (collprovider[0] == 'i')
15113 appendPQExpBufferStr(q, "icu");
15114 else if (collprovider[0] == 'd')
15115 /* to allow dumping pg_catalog; not accepted on input */
15116 appendPQExpBufferStr(q, "default");
15117 else
15118 pg_fatal("unrecognized collation provider: %s",
15119 collprovider);
15120
15121 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15122 appendPQExpBufferStr(q, ", deterministic = false");
15123
15124 if (collprovider[0] == 'd')
15125 {
15126 if (collcollate || collctype || colllocale || collicurules)
15127 pg_log_warning("invalid collation \"%s\"", qcollname);
15128
15129 /* no locale -- the default collation cannot be reloaded anyway */
15130 }
15131 else if (collprovider[0] == 'b')
15132 {
15133 if (collcollate || collctype || !colllocale || collicurules)
15134 pg_log_warning("invalid collation \"%s\"", qcollname);
15135
15136 appendPQExpBufferStr(q, ", locale = ");
15137 appendStringLiteralAH(q, colllocale ? colllocale : "",
15138 fout);
15139 }
15140 else if (collprovider[0] == 'i')
15141 {
15142 if (fout->remoteVersion >= 150000)
15143 {
15144 if (collcollate || collctype || !colllocale)
15145 pg_log_warning("invalid collation \"%s\"", qcollname);
15146
15147 appendPQExpBufferStr(q, ", locale = ");
15148 appendStringLiteralAH(q, colllocale ? colllocale : "",
15149 fout);
15150 }
15151 else
15152 {
15153 if (!collcollate || !collctype || colllocale ||
15154 strcmp(collcollate, collctype) != 0)
15155 pg_log_warning("invalid collation \"%s\"", qcollname);
15156
15157 appendPQExpBufferStr(q, ", locale = ");
15158 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15159 }
15160
15161 if (collicurules)
15162 {
15163 appendPQExpBufferStr(q, ", rules = ");
15164 appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15165 }
15166 }
15167 else if (collprovider[0] == 'c')
15168 {
15169 if (colllocale || collicurules || !collcollate || !collctype)
15170 pg_log_warning("invalid collation \"%s\"", qcollname);
15171
15172 if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15173 {
15174 appendPQExpBufferStr(q, ", locale = ");
15175 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15176 }
15177 else
15178 {
15179 appendPQExpBufferStr(q, ", lc_collate = ");
15180 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15181 appendPQExpBufferStr(q, ", lc_ctype = ");
15182 appendStringLiteralAH(q, collctype ? collctype : "", fout);
15183 }
15184 }
15185 else
15186 pg_fatal("unrecognized collation provider: %s", collprovider);
15187
15188 /*
15189 * For binary upgrade, carry over the collation version. For normal
15190 * dump/restore, omit the version, so that it is computed upon restore.
15191 */
15192 if (dopt->binary_upgrade)
15193 {
15194 int i_collversion;
15195
15196 i_collversion = PQfnumber(res, "collversion");
15197 if (!PQgetisnull(res, 0, i_collversion))
15198 {
15199 appendPQExpBufferStr(q, ", version = ");
15201 PQgetvalue(res, 0, i_collversion),
15202 fout);
15203 }
15204 }
15205
15206 appendPQExpBufferStr(q, ");\n");
15207
15208 if (dopt->binary_upgrade)
15210 "COLLATION", qcollname,
15211 collinfo->dobj.namespace->dobj.name);
15212
15213 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15214 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15215 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15216 .namespace = collinfo->dobj.namespace->dobj.name,
15217 .owner = collinfo->rolname,
15218 .description = "COLLATION",
15219 .section = SECTION_PRE_DATA,
15220 .createStmt = q->data,
15221 .dropStmt = delq->data));
15222
15223 /* Dump Collation Comments */
15224 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15225 dumpComment(fout, "COLLATION", qcollname,
15226 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15227 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15228
15229 PQclear(res);
15230
15231 destroyPQExpBuffer(query);
15233 destroyPQExpBuffer(delq);
15234 free(qcollname);
15235}
15236
15237/*
15238 * dumpConversion
15239 * write out a single conversion definition
15240 */
15241static void
15242dumpConversion(Archive *fout, const ConvInfo *convinfo)
15243{
15244 DumpOptions *dopt = fout->dopt;
15245 PQExpBuffer query;
15246 PQExpBuffer q;
15247 PQExpBuffer delq;
15248 char *qconvname;
15249 PGresult *res;
15250 int i_conforencoding;
15251 int i_contoencoding;
15252 int i_conproc;
15253 int i_condefault;
15254 const char *conforencoding;
15255 const char *contoencoding;
15256 const char *conproc;
15257 bool condefault;
15258
15259 /* Do nothing if not dumping schema */
15260 if (!dopt->dumpSchema)
15261 return;
15262
15263 query = createPQExpBuffer();
15264 q = createPQExpBuffer();
15265 delq = createPQExpBuffer();
15266
15267 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15268
15269 /* Get conversion-specific details */
15270 appendPQExpBuffer(query, "SELECT "
15271 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15272 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15273 "conproc, condefault "
15274 "FROM pg_catalog.pg_conversion c "
15275 "WHERE c.oid = '%u'::pg_catalog.oid",
15276 convinfo->dobj.catId.oid);
15277
15278 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15279
15280 i_conforencoding = PQfnumber(res, "conforencoding");
15281 i_contoencoding = PQfnumber(res, "contoencoding");
15282 i_conproc = PQfnumber(res, "conproc");
15283 i_condefault = PQfnumber(res, "condefault");
15284
15285 conforencoding = PQgetvalue(res, 0, i_conforencoding);
15286 contoencoding = PQgetvalue(res, 0, i_contoencoding);
15287 conproc = PQgetvalue(res, 0, i_conproc);
15288 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15289
15290 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15291 fmtQualifiedDumpable(convinfo));
15292
15293 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15294 (condefault) ? "DEFAULT " : "",
15295 fmtQualifiedDumpable(convinfo));
15296 appendStringLiteralAH(q, conforencoding, fout);
15297 appendPQExpBufferStr(q, " TO ");
15298 appendStringLiteralAH(q, contoencoding, fout);
15299 /* regproc output is already sufficiently quoted */
15300 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15301
15302 if (dopt->binary_upgrade)
15304 "CONVERSION", qconvname,
15305 convinfo->dobj.namespace->dobj.name);
15306
15307 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15308 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15309 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15310 .namespace = convinfo->dobj.namespace->dobj.name,
15311 .owner = convinfo->rolname,
15312 .description = "CONVERSION",
15313 .section = SECTION_PRE_DATA,
15314 .createStmt = q->data,
15315 .dropStmt = delq->data));
15316
15317 /* Dump Conversion Comments */
15318 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15319 dumpComment(fout, "CONVERSION", qconvname,
15320 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15321 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15322
15323 PQclear(res);
15324
15325 destroyPQExpBuffer(query);
15327 destroyPQExpBuffer(delq);
15328 free(qconvname);
15329}
15330
15331/*
15332 * format_aggregate_signature: generate aggregate name and argument list
15333 *
15334 * The argument type names are qualified if needed. The aggregate name
15335 * is never qualified.
15336 */
15337static char *
15338format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15339{
15341 int j;
15342
15344 if (honor_quotes)
15345 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15346 else
15347 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15348
15349 if (agginfo->aggfn.nargs == 0)
15350 appendPQExpBufferStr(&buf, "(*)");
15351 else
15352 {
15354 for (j = 0; j < agginfo->aggfn.nargs; j++)
15355 appendPQExpBuffer(&buf, "%s%s",
15356 (j > 0) ? ", " : "",
15358 agginfo->aggfn.argtypes[j],
15359 zeroIsError));
15361 }
15362 return buf.data;
15363}
15364
15365/*
15366 * dumpAgg
15367 * write out a single aggregate definition
15368 */
15369static void
15370dumpAgg(Archive *fout, const AggInfo *agginfo)
15371{
15372 DumpOptions *dopt = fout->dopt;
15373 PQExpBuffer query;
15374 PQExpBuffer q;
15375 PQExpBuffer delq;
15376 PQExpBuffer details;
15377 char *aggsig; /* identity signature */
15378 char *aggfullsig = NULL; /* full signature */
15379 char *aggsig_tag;
15380 PGresult *res;
15381 int i_agginitval;
15382 int i_aggminitval;
15383 const char *aggtransfn;
15384 const char *aggfinalfn;
15385 const char *aggcombinefn;
15386 const char *aggserialfn;
15387 const char *aggdeserialfn;
15388 const char *aggmtransfn;
15389 const char *aggminvtransfn;
15390 const char *aggmfinalfn;
15391 bool aggfinalextra;
15392 bool aggmfinalextra;
15393 char aggfinalmodify;
15394 char aggmfinalmodify;
15395 const char *aggsortop;
15396 char *aggsortconvop;
15397 char aggkind;
15398 const char *aggtranstype;
15399 const char *aggtransspace;
15400 const char *aggmtranstype;
15401 const char *aggmtransspace;
15402 const char *agginitval;
15403 const char *aggminitval;
15404 const char *proparallel;
15405 char defaultfinalmodify;
15406
15407 /* Do nothing if not dumping schema */
15408 if (!dopt->dumpSchema)
15409 return;
15410
15411 query = createPQExpBuffer();
15412 q = createPQExpBuffer();
15413 delq = createPQExpBuffer();
15414 details = createPQExpBuffer();
15415
15416 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15417 {
15418 /* Set up query for aggregate-specific details */
15420 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15421
15423 "SELECT "
15424 "aggtransfn,\n"
15425 "aggfinalfn,\n"
15426 "aggtranstype::pg_catalog.regtype,\n"
15427 "agginitval,\n"
15428 "aggsortop,\n"
15429 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15430 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15431
15432 if (fout->remoteVersion >= 90400)
15434 "aggkind,\n"
15435 "aggmtransfn,\n"
15436 "aggminvtransfn,\n"
15437 "aggmfinalfn,\n"
15438 "aggmtranstype::pg_catalog.regtype,\n"
15439 "aggfinalextra,\n"
15440 "aggmfinalextra,\n"
15441 "aggtransspace,\n"
15442 "aggmtransspace,\n"
15443 "aggminitval,\n");
15444 else
15446 "'n' AS aggkind,\n"
15447 "'-' AS aggmtransfn,\n"
15448 "'-' AS aggminvtransfn,\n"
15449 "'-' AS aggmfinalfn,\n"
15450 "0 AS aggmtranstype,\n"
15451 "false AS aggfinalextra,\n"
15452 "false AS aggmfinalextra,\n"
15453 "0 AS aggtransspace,\n"
15454 "0 AS aggmtransspace,\n"
15455 "NULL AS aggminitval,\n");
15456
15457 if (fout->remoteVersion >= 90600)
15459 "aggcombinefn,\n"
15460 "aggserialfn,\n"
15461 "aggdeserialfn,\n"
15462 "proparallel,\n");
15463 else
15465 "'-' AS aggcombinefn,\n"
15466 "'-' AS aggserialfn,\n"
15467 "'-' AS aggdeserialfn,\n"
15468 "'u' AS proparallel,\n");
15469
15470 if (fout->remoteVersion >= 110000)
15472 "aggfinalmodify,\n"
15473 "aggmfinalmodify\n");
15474 else
15476 "'0' AS aggfinalmodify,\n"
15477 "'0' AS aggmfinalmodify\n");
15478
15480 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15481 "WHERE a.aggfnoid = p.oid "
15482 "AND p.oid = $1");
15483
15484 ExecuteSqlStatement(fout, query->data);
15485
15486 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15487 }
15488
15489 printfPQExpBuffer(query,
15490 "EXECUTE dumpAgg('%u')",
15491 agginfo->aggfn.dobj.catId.oid);
15492
15493 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15494
15495 i_agginitval = PQfnumber(res, "agginitval");
15496 i_aggminitval = PQfnumber(res, "aggminitval");
15497
15498 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15499 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15500 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15501 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15502 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15503 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15504 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15505 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15506 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15507 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15508 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15509 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15510 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15511 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15512 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15513 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15514 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15515 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15516 agginitval = PQgetvalue(res, 0, i_agginitval);
15517 aggminitval = PQgetvalue(res, 0, i_aggminitval);
15518 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15519
15520 {
15521 char *funcargs;
15522 char *funciargs;
15523
15524 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15525 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15526 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15527 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15528 }
15529
15530 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15531
15532 /* identify default modify flag for aggkind (must match DefineAggregate) */
15533 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15534 /* replace omitted flags for old versions */
15535 if (aggfinalmodify == '0')
15536 aggfinalmodify = defaultfinalmodify;
15537 if (aggmfinalmodify == '0')
15538 aggmfinalmodify = defaultfinalmodify;
15539
15540 /* regproc and regtype output is already sufficiently quoted */
15541 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15542 aggtransfn, aggtranstype);
15543
15544 if (strcmp(aggtransspace, "0") != 0)
15545 {
15546 appendPQExpBuffer(details, ",\n SSPACE = %s",
15547 aggtransspace);
15548 }
15549
15550 if (!PQgetisnull(res, 0, i_agginitval))
15551 {
15552 appendPQExpBufferStr(details, ",\n INITCOND = ");
15553 appendStringLiteralAH(details, agginitval, fout);
15554 }
15555
15556 if (strcmp(aggfinalfn, "-") != 0)
15557 {
15558 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15559 aggfinalfn);
15560 if (aggfinalextra)
15561 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15562 if (aggfinalmodify != defaultfinalmodify)
15563 {
15564 switch (aggfinalmodify)
15565 {
15566 case AGGMODIFY_READ_ONLY:
15567 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15568 break;
15569 case AGGMODIFY_SHAREABLE:
15570 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15571 break;
15572 case AGGMODIFY_READ_WRITE:
15573 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15574 break;
15575 default:
15576 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15577 agginfo->aggfn.dobj.name);
15578 break;
15579 }
15580 }
15581 }
15582
15583 if (strcmp(aggcombinefn, "-") != 0)
15584 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15585
15586 if (strcmp(aggserialfn, "-") != 0)
15587 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15588
15589 if (strcmp(aggdeserialfn, "-") != 0)
15590 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15591
15592 if (strcmp(aggmtransfn, "-") != 0)
15593 {
15594 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15595 aggmtransfn,
15596 aggminvtransfn,
15597 aggmtranstype);
15598 }
15599
15600 if (strcmp(aggmtransspace, "0") != 0)
15601 {
15602 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15603 aggmtransspace);
15604 }
15605
15606 if (!PQgetisnull(res, 0, i_aggminitval))
15607 {
15608 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15609 appendStringLiteralAH(details, aggminitval, fout);
15610 }
15611
15612 if (strcmp(aggmfinalfn, "-") != 0)
15613 {
15614 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15615 aggmfinalfn);
15616 if (aggmfinalextra)
15617 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15618 if (aggmfinalmodify != defaultfinalmodify)
15619 {
15620 switch (aggmfinalmodify)
15621 {
15622 case AGGMODIFY_READ_ONLY:
15623 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15624 break;
15625 case AGGMODIFY_SHAREABLE:
15626 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15627 break;
15628 case AGGMODIFY_READ_WRITE:
15629 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15630 break;
15631 default:
15632 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15633 agginfo->aggfn.dobj.name);
15634 break;
15635 }
15636 }
15637 }
15638
15639 aggsortconvop = getFormattedOperatorName(aggsortop);
15640 if (aggsortconvop)
15641 {
15642 appendPQExpBuffer(details, ",\n SORTOP = %s",
15643 aggsortconvop);
15644 free(aggsortconvop);
15645 }
15646
15647 if (aggkind == AGGKIND_HYPOTHETICAL)
15648 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15649
15650 if (proparallel[0] != PROPARALLEL_UNSAFE)
15651 {
15652 if (proparallel[0] == PROPARALLEL_SAFE)
15653 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15654 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15655 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15656 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15657 pg_fatal("unrecognized proparallel value for function \"%s\"",
15658 agginfo->aggfn.dobj.name);
15659 }
15660
15661 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15662 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15663 aggsig);
15664
15665 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15666 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15667 aggfullsig ? aggfullsig : aggsig, details->data);
15668
15669 if (dopt->binary_upgrade)
15670 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15671 "AGGREGATE", aggsig,
15672 agginfo->aggfn.dobj.namespace->dobj.name);
15673
15674 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15675 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15676 agginfo->aggfn.dobj.dumpId,
15677 ARCHIVE_OPTS(.tag = aggsig_tag,
15678 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15679 .owner = agginfo->aggfn.rolname,
15680 .description = "AGGREGATE",
15681 .section = SECTION_PRE_DATA,
15682 .createStmt = q->data,
15683 .dropStmt = delq->data));
15684
15685 /* Dump Aggregate Comments */
15686 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15687 dumpComment(fout, "AGGREGATE", aggsig,
15688 agginfo->aggfn.dobj.namespace->dobj.name,
15689 agginfo->aggfn.rolname,
15690 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15691
15692 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15693 dumpSecLabel(fout, "AGGREGATE", aggsig,
15694 agginfo->aggfn.dobj.namespace->dobj.name,
15695 agginfo->aggfn.rolname,
15696 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15697
15698 /*
15699 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15700 * command look like a function's GRANT; in particular this affects the
15701 * syntax for zero-argument aggregates and ordered-set aggregates.
15702 */
15703 free(aggsig);
15704
15705 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15706
15707 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15708 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15709 "FUNCTION", aggsig, NULL,
15710 agginfo->aggfn.dobj.namespace->dobj.name,
15711 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15712
15713 free(aggsig);
15714 free(aggfullsig);
15715 free(aggsig_tag);
15716
15717 PQclear(res);
15718
15719 destroyPQExpBuffer(query);
15721 destroyPQExpBuffer(delq);
15722 destroyPQExpBuffer(details);
15723}
15724
15725/*
15726 * dumpTSParser
15727 * write out a single text search parser
15728 */
15729static void
15730dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15731{
15732 DumpOptions *dopt = fout->dopt;
15733 PQExpBuffer q;
15734 PQExpBuffer delq;
15735 char *qprsname;
15736
15737 /* Do nothing if not dumping schema */
15738 if (!dopt->dumpSchema)
15739 return;
15740
15741 q = createPQExpBuffer();
15742 delq = createPQExpBuffer();
15743
15744 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15745
15746 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15747 fmtQualifiedDumpable(prsinfo));
15748
15749 appendPQExpBuffer(q, " START = %s,\n",
15750 convertTSFunction(fout, prsinfo->prsstart));
15751 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15752 convertTSFunction(fout, prsinfo->prstoken));
15753 appendPQExpBuffer(q, " END = %s,\n",
15754 convertTSFunction(fout, prsinfo->prsend));
15755 if (prsinfo->prsheadline != InvalidOid)
15756 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15757 convertTSFunction(fout, prsinfo->prsheadline));
15758 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15759 convertTSFunction(fout, prsinfo->prslextype));
15760
15761 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15762 fmtQualifiedDumpable(prsinfo));
15763
15764 if (dopt->binary_upgrade)
15766 "TEXT SEARCH PARSER", qprsname,
15767 prsinfo->dobj.namespace->dobj.name);
15768
15769 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15770 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15771 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15772 .namespace = prsinfo->dobj.namespace->dobj.name,
15773 .description = "TEXT SEARCH PARSER",
15774 .section = SECTION_PRE_DATA,
15775 .createStmt = q->data,
15776 .dropStmt = delq->data));
15777
15778 /* Dump Parser Comments */
15779 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15780 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15781 prsinfo->dobj.namespace->dobj.name, "",
15782 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15783
15785 destroyPQExpBuffer(delq);
15786 free(qprsname);
15787}
15788
15789/*
15790 * dumpTSDictionary
15791 * write out a single text search dictionary
15792 */
15793static void
15794dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15795{
15796 DumpOptions *dopt = fout->dopt;
15797 PQExpBuffer q;
15798 PQExpBuffer delq;
15799 PQExpBuffer query;
15800 char *qdictname;
15801 PGresult *res;
15802 char *nspname;
15803 char *tmplname;
15804
15805 /* Do nothing if not dumping schema */
15806 if (!dopt->dumpSchema)
15807 return;
15808
15809 q = createPQExpBuffer();
15810 delq = createPQExpBuffer();
15811 query = createPQExpBuffer();
15812
15813 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15814
15815 /* Fetch name and namespace of the dictionary's template */
15816 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15817 "FROM pg_ts_template p, pg_namespace n "
15818 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15819 dictinfo->dicttemplate);
15820 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15821 nspname = PQgetvalue(res, 0, 0);
15822 tmplname = PQgetvalue(res, 0, 1);
15823
15824 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15825 fmtQualifiedDumpable(dictinfo));
15826
15827 appendPQExpBufferStr(q, " TEMPLATE = ");
15828 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15829 appendPQExpBufferStr(q, fmtId(tmplname));
15830
15831 PQclear(res);
15832
15833 /* the dictinitoption can be dumped straight into the command */
15834 if (dictinfo->dictinitoption)
15835 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15836
15837 appendPQExpBufferStr(q, " );\n");
15838
15839 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15840 fmtQualifiedDumpable(dictinfo));
15841
15842 if (dopt->binary_upgrade)
15844 "TEXT SEARCH DICTIONARY", qdictname,
15845 dictinfo->dobj.namespace->dobj.name);
15846
15847 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15848 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15849 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15850 .namespace = dictinfo->dobj.namespace->dobj.name,
15851 .owner = dictinfo->rolname,
15852 .description = "TEXT SEARCH DICTIONARY",
15853 .section = SECTION_PRE_DATA,
15854 .createStmt = q->data,
15855 .dropStmt = delq->data));
15856
15857 /* Dump Dictionary Comments */
15858 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15859 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15860 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15861 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15862
15864 destroyPQExpBuffer(delq);
15865 destroyPQExpBuffer(query);
15866 free(qdictname);
15867}
15868
15869/*
15870 * dumpTSTemplate
15871 * write out a single text search template
15872 */
15873static void
15875{
15876 DumpOptions *dopt = fout->dopt;
15877 PQExpBuffer q;
15878 PQExpBuffer delq;
15879 char *qtmplname;
15880
15881 /* Do nothing if not dumping schema */
15882 if (!dopt->dumpSchema)
15883 return;
15884
15885 q = createPQExpBuffer();
15886 delq = createPQExpBuffer();
15887
15888 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15889
15890 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15891 fmtQualifiedDumpable(tmplinfo));
15892
15893 if (tmplinfo->tmplinit != InvalidOid)
15894 appendPQExpBuffer(q, " INIT = %s,\n",
15895 convertTSFunction(fout, tmplinfo->tmplinit));
15896 appendPQExpBuffer(q, " LEXIZE = %s );\n",
15897 convertTSFunction(fout, tmplinfo->tmpllexize));
15898
15899 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15900 fmtQualifiedDumpable(tmplinfo));
15901
15902 if (dopt->binary_upgrade)
15904 "TEXT SEARCH TEMPLATE", qtmplname,
15905 tmplinfo->dobj.namespace->dobj.name);
15906
15907 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15908 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15909 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15910 .namespace = tmplinfo->dobj.namespace->dobj.name,
15911 .description = "TEXT SEARCH TEMPLATE",
15912 .section = SECTION_PRE_DATA,
15913 .createStmt = q->data,
15914 .dropStmt = delq->data));
15915
15916 /* Dump Template Comments */
15917 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15918 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15919 tmplinfo->dobj.namespace->dobj.name, "",
15920 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15921
15923 destroyPQExpBuffer(delq);
15924 free(qtmplname);
15925}
15926
15927/*
15928 * dumpTSConfig
15929 * write out a single text search configuration
15930 */
15931static void
15932dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15933{
15934 DumpOptions *dopt = fout->dopt;
15935 PQExpBuffer q;
15936 PQExpBuffer delq;
15937 PQExpBuffer query;
15938 char *qcfgname;
15939 PGresult *res;
15940 char *nspname;
15941 char *prsname;
15942 int ntups,
15943 i;
15944 int i_tokenname;
15945 int i_dictname;
15946
15947 /* Do nothing if not dumping schema */
15948 if (!dopt->dumpSchema)
15949 return;
15950
15951 q = createPQExpBuffer();
15952 delq = createPQExpBuffer();
15953 query = createPQExpBuffer();
15954
15955 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15956
15957 /* Fetch name and namespace of the config's parser */
15958 appendPQExpBuffer(query, "SELECT nspname, prsname "
15959 "FROM pg_ts_parser p, pg_namespace n "
15960 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15961 cfginfo->cfgparser);
15962 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15963 nspname = PQgetvalue(res, 0, 0);
15964 prsname = PQgetvalue(res, 0, 1);
15965
15966 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15967 fmtQualifiedDumpable(cfginfo));
15968
15969 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15970 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15971
15972 PQclear(res);
15973
15974 resetPQExpBuffer(query);
15975 appendPQExpBuffer(query,
15976 "SELECT\n"
15977 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15978 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15979 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15980 "FROM pg_catalog.pg_ts_config_map AS m\n"
15981 "WHERE m.mapcfg = '%u'\n"
15982 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15983 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15984
15985 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15986 ntups = PQntuples(res);
15987
15988 i_tokenname = PQfnumber(res, "tokenname");
15989 i_dictname = PQfnumber(res, "dictname");
15990
15991 for (i = 0; i < ntups; i++)
15992 {
15993 char *tokenname = PQgetvalue(res, i, i_tokenname);
15994 char *dictname = PQgetvalue(res, i, i_dictname);
15995
15996 if (i == 0 ||
15997 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15998 {
15999 /* starting a new token type, so start a new command */
16000 if (i > 0)
16001 appendPQExpBufferStr(q, ";\n");
16002 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16003 fmtQualifiedDumpable(cfginfo));
16004 /* tokenname needs quoting, dictname does NOT */
16005 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16006 fmtId(tokenname), dictname);
16007 }
16008 else
16009 appendPQExpBuffer(q, ", %s", dictname);
16010 }
16011
16012 if (ntups > 0)
16013 appendPQExpBufferStr(q, ";\n");
16014
16015 PQclear(res);
16016
16017 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16018 fmtQualifiedDumpable(cfginfo));
16019
16020 if (dopt->binary_upgrade)
16022 "TEXT SEARCH CONFIGURATION", qcfgname,
16023 cfginfo->dobj.namespace->dobj.name);
16024
16025 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16026 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16027 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16028 .namespace = cfginfo->dobj.namespace->dobj.name,
16029 .owner = cfginfo->rolname,
16030 .description = "TEXT SEARCH CONFIGURATION",
16031 .section = SECTION_PRE_DATA,
16032 .createStmt = q->data,
16033 .dropStmt = delq->data));
16034
16035 /* Dump Configuration Comments */
16036 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16037 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16038 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16039 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16040
16042 destroyPQExpBuffer(delq);
16043 destroyPQExpBuffer(query);
16044 free(qcfgname);
16045}
16046
16047/*
16048 * dumpForeignDataWrapper
16049 * write out a single foreign-data wrapper definition
16050 */
16051static void
16053{
16054 DumpOptions *dopt = fout->dopt;
16055 PQExpBuffer q;
16056 PQExpBuffer delq;
16057 char *qfdwname;
16058
16059 /* Do nothing if not dumping schema */
16060 if (!dopt->dumpSchema)
16061 return;
16062
16063 q = createPQExpBuffer();
16064 delq = createPQExpBuffer();
16065
16066 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16067
16068 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16069 qfdwname);
16070
16071 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16072 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16073
16074 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16075 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16076
16077 if (strlen(fdwinfo->fdwoptions) > 0)
16078 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16079
16080 appendPQExpBufferStr(q, ";\n");
16081
16082 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16083 qfdwname);
16084
16085 if (dopt->binary_upgrade)
16087 "FOREIGN DATA WRAPPER", qfdwname,
16088 NULL);
16089
16090 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16091 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16092 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16093 .owner = fdwinfo->rolname,
16094 .description = "FOREIGN DATA WRAPPER",
16095 .section = SECTION_PRE_DATA,
16096 .createStmt = q->data,
16097 .dropStmt = delq->data));
16098
16099 /* Dump Foreign Data Wrapper Comments */
16100 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16101 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16102 NULL, fdwinfo->rolname,
16103 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16104
16105 /* Handle the ACL */
16106 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16107 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16108 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16109 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16110
16111 free(qfdwname);
16112
16114 destroyPQExpBuffer(delq);
16115}
16116
16117/*
16118 * dumpForeignServer
16119 * write out a foreign server definition
16120 */
16121static void
16123{
16124 DumpOptions *dopt = fout->dopt;
16125 PQExpBuffer q;
16126 PQExpBuffer delq;
16127 PQExpBuffer query;
16128 PGresult *res;
16129 char *qsrvname;
16130 char *fdwname;
16131
16132 /* Do nothing if not dumping schema */
16133 if (!dopt->dumpSchema)
16134 return;
16135
16136 q = createPQExpBuffer();
16137 delq = createPQExpBuffer();
16138 query = createPQExpBuffer();
16139
16140 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16141
16142 /* look up the foreign-data wrapper */
16143 appendPQExpBuffer(query, "SELECT fdwname "
16144 "FROM pg_foreign_data_wrapper w "
16145 "WHERE w.oid = '%u'",
16146 srvinfo->srvfdw);
16147 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16148 fdwname = PQgetvalue(res, 0, 0);
16149
16150 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16151 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16152 {
16153 appendPQExpBufferStr(q, " TYPE ");
16154 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16155 }
16156 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16157 {
16158 appendPQExpBufferStr(q, " VERSION ");
16159 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16160 }
16161
16162 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16163 appendPQExpBufferStr(q, fmtId(fdwname));
16164
16165 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16166 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16167
16168 appendPQExpBufferStr(q, ";\n");
16169
16170 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16171 qsrvname);
16172
16173 if (dopt->binary_upgrade)
16175 "SERVER", qsrvname, NULL);
16176
16177 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16178 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16179 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16180 .owner = srvinfo->rolname,
16181 .description = "SERVER",
16182 .section = SECTION_PRE_DATA,
16183 .createStmt = q->data,
16184 .dropStmt = delq->data));
16185
16186 /* Dump Foreign Server Comments */
16187 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16188 dumpComment(fout, "SERVER", qsrvname,
16189 NULL, srvinfo->rolname,
16190 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16191
16192 /* Handle the ACL */
16193 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16194 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16195 "FOREIGN SERVER", qsrvname, NULL, NULL,
16196 NULL, srvinfo->rolname, &srvinfo->dacl);
16197
16198 /* Dump user mappings */
16199 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16200 dumpUserMappings(fout,
16201 srvinfo->dobj.name, NULL,
16202 srvinfo->rolname,
16203 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16204
16205 PQclear(res);
16206
16207 free(qsrvname);
16208
16210 destroyPQExpBuffer(delq);
16211 destroyPQExpBuffer(query);
16212}
16213
16214/*
16215 * dumpUserMappings
16216 *
16217 * This routine is used to dump any user mappings associated with the
16218 * server handed to this routine. Should be called after ArchiveEntry()
16219 * for the server.
16220 */
16221static void
16223 const char *servername, const char *namespace,
16224 const char *owner,
16225 CatalogId catalogId, DumpId dumpId)
16226{
16227 PQExpBuffer q;
16228 PQExpBuffer delq;
16229 PQExpBuffer query;
16230 PQExpBuffer tag;
16231 PGresult *res;
16232 int ntups;
16233 int i_usename;
16234 int i_umoptions;
16235 int i;
16236
16237 q = createPQExpBuffer();
16238 tag = createPQExpBuffer();
16239 delq = createPQExpBuffer();
16240 query = createPQExpBuffer();
16241
16242 /*
16243 * We read from the publicly accessible view pg_user_mappings, so as not
16244 * to fail if run by a non-superuser. Note that the view will show
16245 * umoptions as null if the user hasn't got privileges for the associated
16246 * server; this means that pg_dump will dump such a mapping, but with no
16247 * OPTIONS clause. A possible alternative is to skip such mappings
16248 * altogether, but it's not clear that that's an improvement.
16249 */
16250 appendPQExpBuffer(query,
16251 "SELECT usename, "
16252 "array_to_string(ARRAY("
16253 "SELECT quote_ident(option_name) || ' ' || "
16254 "quote_literal(option_value) "
16255 "FROM pg_options_to_table(umoptions) "
16256 "ORDER BY option_name"
16257 "), E',\n ') AS umoptions "
16258 "FROM pg_user_mappings "
16259 "WHERE srvid = '%u' "
16260 "ORDER BY usename",
16261 catalogId.oid);
16262
16263 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16264
16265 ntups = PQntuples(res);
16266 i_usename = PQfnumber(res, "usename");
16267 i_umoptions = PQfnumber(res, "umoptions");
16268
16269 for (i = 0; i < ntups; i++)
16270 {
16271 char *usename;
16272 char *umoptions;
16273
16274 usename = PQgetvalue(res, i, i_usename);
16275 umoptions = PQgetvalue(res, i, i_umoptions);
16276
16278 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16279 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16280
16281 if (umoptions && strlen(umoptions) > 0)
16282 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16283
16284 appendPQExpBufferStr(q, ";\n");
16285
16286 resetPQExpBuffer(delq);
16287 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16288 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16289
16290 resetPQExpBuffer(tag);
16291 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16292 usename, servername);
16293
16295 ARCHIVE_OPTS(.tag = tag->data,
16296 .namespace = namespace,
16297 .owner = owner,
16298 .description = "USER MAPPING",
16299 .section = SECTION_PRE_DATA,
16300 .createStmt = q->data,
16301 .dropStmt = delq->data));
16302 }
16303
16304 PQclear(res);
16305
16306 destroyPQExpBuffer(query);
16307 destroyPQExpBuffer(delq);
16308 destroyPQExpBuffer(tag);
16310}
16311
16312/*
16313 * Write out default privileges information
16314 */
16315static void
16317{
16318 DumpOptions *dopt = fout->dopt;
16319 PQExpBuffer q;
16320 PQExpBuffer tag;
16321 const char *type;
16322
16323 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16324 if (!dopt->dumpSchema || dopt->aclsSkip)
16325 return;
16326
16327 q = createPQExpBuffer();
16328 tag = createPQExpBuffer();
16329
16330 switch (daclinfo->defaclobjtype)
16331 {
16332 case DEFACLOBJ_RELATION:
16333 type = "TABLES";
16334 break;
16335 case DEFACLOBJ_SEQUENCE:
16336 type = "SEQUENCES";
16337 break;
16338 case DEFACLOBJ_FUNCTION:
16339 type = "FUNCTIONS";
16340 break;
16341 case DEFACLOBJ_TYPE:
16342 type = "TYPES";
16343 break;
16344 case DEFACLOBJ_NAMESPACE:
16345 type = "SCHEMAS";
16346 break;
16347 case DEFACLOBJ_LARGEOBJECT:
16348 type = "LARGE OBJECTS";
16349 break;
16350 default:
16351 /* shouldn't get here */
16352 pg_fatal("unrecognized object type in default privileges: %d",
16353 (int) daclinfo->defaclobjtype);
16354 type = ""; /* keep compiler quiet */
16355 }
16356
16357 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16358
16359 /* build the actual command(s) for this tuple */
16361 daclinfo->dobj.namespace != NULL ?
16362 daclinfo->dobj.namespace->dobj.name : NULL,
16363 daclinfo->dacl.acl,
16364 daclinfo->dacl.acldefault,
16365 daclinfo->defaclrole,
16366 fout->remoteVersion,
16367 q))
16368 pg_fatal("could not parse default ACL list (%s)",
16369 daclinfo->dacl.acl);
16370
16371 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16372 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16373 ARCHIVE_OPTS(.tag = tag->data,
16374 .namespace = daclinfo->dobj.namespace ?
16375 daclinfo->dobj.namespace->dobj.name : NULL,
16376 .owner = daclinfo->defaclrole,
16377 .description = "DEFAULT ACL",
16378 .section = SECTION_POST_DATA,
16379 .createStmt = q->data));
16380
16381 destroyPQExpBuffer(tag);
16383}
16384
16385/*----------
16386 * Write out grant/revoke information
16387 *
16388 * 'objDumpId' is the dump ID of the underlying object.
16389 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16390 * or InvalidDumpId if there is no need for a second dependency.
16391 * 'type' must be one of
16392 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16393 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16394 * 'name' is the formatted name of the object. Must be quoted etc. already.
16395 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16396 * (Currently we assume that subname is only provided for table columns.)
16397 * 'nspname' is the namespace the object is in (NULL if none).
16398 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16399 * to use the default for the object type.
16400 * 'owner' is the owner, NULL if there is no owner (for languages).
16401 * 'dacl' is the DumpableAcl struct for the object.
16402 *
16403 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16404 * no ACL entry was created.
16405 *----------
16406 */
16407static DumpId
16408dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16409 const char *type, const char *name, const char *subname,
16410 const char *nspname, const char *tag, const char *owner,
16411 const DumpableAcl *dacl)
16412{
16413 DumpId aclDumpId = InvalidDumpId;
16414 DumpOptions *dopt = fout->dopt;
16415 const char *acls = dacl->acl;
16416 const char *acldefault = dacl->acldefault;
16417 char privtype = dacl->privtype;
16418 const char *initprivs = dacl->initprivs;
16419 const char *baseacls;
16420 PQExpBuffer sql;
16421
16422 /* Do nothing if ACL dump is not enabled */
16423 if (dopt->aclsSkip)
16424 return InvalidDumpId;
16425
16426 /* --data-only skips ACLs *except* large object ACLs */
16427 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16428 return InvalidDumpId;
16429
16430 sql = createPQExpBuffer();
16431
16432 /*
16433 * In binary upgrade mode, we don't run an extension's script but instead
16434 * dump out the objects independently and then recreate them. To preserve
16435 * any initial privileges which were set on extension objects, we need to
16436 * compute the set of GRANT and REVOKE commands necessary to get from the
16437 * default privileges of an object to its initial privileges as recorded
16438 * in pg_init_privs.
16439 *
16440 * At restore time, we apply these commands after having called
16441 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16442 * copy the results into pg_init_privs. This is how we preserve the
16443 * contents of that catalog across binary upgrades.
16444 */
16445 if (dopt->binary_upgrade && privtype == 'e' &&
16446 initprivs && *initprivs != '\0')
16447 {
16448 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16449 if (!buildACLCommands(name, subname, nspname, type,
16450 initprivs, acldefault, owner,
16451 "", fout->remoteVersion, sql))
16452 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16453 initprivs, acldefault, name, type);
16454 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16455 }
16456
16457 /*
16458 * Now figure the GRANT and REVOKE commands needed to get to the object's
16459 * actual current ACL, starting from the initprivs if given, else from the
16460 * object-type-specific default. Also, while buildACLCommands will assume
16461 * that a NULL/empty acls string means it needn't do anything, what that
16462 * actually represents is the object-type-specific default; so we need to
16463 * substitute the acldefault string to get the right results in that case.
16464 */
16465 if (initprivs && *initprivs != '\0')
16466 {
16467 baseacls = initprivs;
16468 if (acls == NULL || *acls == '\0')
16469 acls = acldefault;
16470 }
16471 else
16472 baseacls = acldefault;
16473
16474 if (!buildACLCommands(name, subname, nspname, type,
16475 acls, baseacls, owner,
16476 "", fout->remoteVersion, sql))
16477 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16478 acls, baseacls, name, type);
16479
16480 if (sql->len > 0)
16481 {
16482 PQExpBuffer tagbuf = createPQExpBuffer();
16483 DumpId aclDeps[2];
16484 int nDeps = 0;
16485
16486 if (tag)
16487 appendPQExpBufferStr(tagbuf, tag);
16488 else if (subname)
16489 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16490 else
16491 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16492
16493 aclDeps[nDeps++] = objDumpId;
16494 if (altDumpId != InvalidDumpId)
16495 aclDeps[nDeps++] = altDumpId;
16496
16497 aclDumpId = createDumpId();
16498
16499 ArchiveEntry(fout, nilCatalogId, aclDumpId,
16500 ARCHIVE_OPTS(.tag = tagbuf->data,
16501 .namespace = nspname,
16502 .owner = owner,
16503 .description = "ACL",
16504 .section = SECTION_NONE,
16505 .createStmt = sql->data,
16506 .deps = aclDeps,
16507 .nDeps = nDeps));
16508
16509 destroyPQExpBuffer(tagbuf);
16510 }
16511
16512 destroyPQExpBuffer(sql);
16513
16514 return aclDumpId;
16515}
16516
16517/*
16518 * dumpSecLabel
16519 *
16520 * This routine is used to dump any security labels associated with the
16521 * object handed to this routine. The routine takes the object type
16522 * and object name (ready to print, except for schema decoration), plus
16523 * the namespace and owner of the object (for labeling the ArchiveEntry),
16524 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16525 * plus the dump ID for the object (for setting a dependency).
16526 * If a matching pg_seclabel entry is found, it is dumped.
16527 *
16528 * Note: although this routine takes a dumpId for dependency purposes,
16529 * that purpose is just to mark the dependency in the emitted dump file
16530 * for possible future use by pg_restore. We do NOT use it for determining
16531 * ordering of the label in the dump file, because this routine is called
16532 * after dependency sorting occurs. This routine should be called just after
16533 * calling ArchiveEntry() for the specified object.
16534 */
16535static void
16536dumpSecLabel(Archive *fout, const char *type, const char *name,
16537 const char *namespace, const char *owner,
16538 CatalogId catalogId, int subid, DumpId dumpId)
16539{
16540 DumpOptions *dopt = fout->dopt;
16541 SecLabelItem *labels;
16542 int nlabels;
16543 int i;
16544 PQExpBuffer query;
16545
16546 /* do nothing, if --no-security-labels is supplied */
16547 if (dopt->no_security_labels)
16548 return;
16549
16550 /*
16551 * Security labels are schema not data ... except large object labels are
16552 * data
16553 */
16554 if (strcmp(type, "LARGE OBJECT") != 0)
16555 {
16556 if (!dopt->dumpSchema)
16557 return;
16558 }
16559 else
16560 {
16561 /* We do dump large object security labels in binary-upgrade mode */
16562 if (!dopt->dumpData && !dopt->binary_upgrade)
16563 return;
16564 }
16565
16566 /* Search for security labels associated with catalogId, using table */
16567 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16568
16569 query = createPQExpBuffer();
16570
16571 for (i = 0; i < nlabels; i++)
16572 {
16573 /*
16574 * Ignore label entries for which the subid doesn't match.
16575 */
16576 if (labels[i].objsubid != subid)
16577 continue;
16578
16579 appendPQExpBuffer(query,
16580 "SECURITY LABEL FOR %s ON %s ",
16581 fmtId(labels[i].provider), type);
16582 if (namespace && *namespace)
16583 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16584 appendPQExpBuffer(query, "%s IS ", name);
16585 appendStringLiteralAH(query, labels[i].label, fout);
16586 appendPQExpBufferStr(query, ";\n");
16587 }
16588
16589 if (query->len > 0)
16590 {
16592
16593 appendPQExpBuffer(tag, "%s %s", type, name);
16595 ARCHIVE_OPTS(.tag = tag->data,
16596 .namespace = namespace,
16597 .owner = owner,
16598 .description = "SECURITY LABEL",
16599 .section = SECTION_NONE,
16600 .createStmt = query->data,
16601 .deps = &dumpId,
16602 .nDeps = 1));
16603 destroyPQExpBuffer(tag);
16604 }
16605
16606 destroyPQExpBuffer(query);
16607}
16608
16609/*
16610 * dumpTableSecLabel
16611 *
16612 * As above, but dump security label for both the specified table (or view)
16613 * and its columns.
16614 */
16615static void
16616dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16617{
16618 DumpOptions *dopt = fout->dopt;
16619 SecLabelItem *labels;
16620 int nlabels;
16621 int i;
16622 PQExpBuffer query;
16623 PQExpBuffer target;
16624
16625 /* do nothing, if --no-security-labels is supplied */
16626 if (dopt->no_security_labels)
16627 return;
16628
16629 /* SecLabel are SCHEMA not data */
16630 if (!dopt->dumpSchema)
16631 return;
16632
16633 /* Search for comments associated with relation, using table */
16634 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16635 tbinfo->dobj.catId.oid,
16636 &labels);
16637
16638 /* If security labels exist, build SECURITY LABEL statements */
16639 if (nlabels <= 0)
16640 return;
16641
16642 query = createPQExpBuffer();
16643 target = createPQExpBuffer();
16644
16645 for (i = 0; i < nlabels; i++)
16646 {
16647 const char *colname;
16648 const char *provider = labels[i].provider;
16649 const char *label = labels[i].label;
16650 int objsubid = labels[i].objsubid;
16651
16652 resetPQExpBuffer(target);
16653 if (objsubid == 0)
16654 {
16655 appendPQExpBuffer(target, "%s %s", reltypename,
16656 fmtQualifiedDumpable(tbinfo));
16657 }
16658 else
16659 {
16660 colname = getAttrName(objsubid, tbinfo);
16661 /* first fmtXXX result must be consumed before calling again */
16662 appendPQExpBuffer(target, "COLUMN %s",
16663 fmtQualifiedDumpable(tbinfo));
16664 appendPQExpBuffer(target, ".%s", fmtId(colname));
16665 }
16666 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16667 fmtId(provider), target->data);
16668 appendStringLiteralAH(query, label, fout);
16669 appendPQExpBufferStr(query, ";\n");
16670 }
16671 if (query->len > 0)
16672 {
16673 resetPQExpBuffer(target);
16674 appendPQExpBuffer(target, "%s %s", reltypename,
16675 fmtId(tbinfo->dobj.name));
16677 ARCHIVE_OPTS(.tag = target->data,
16678 .namespace = tbinfo->dobj.namespace->dobj.name,
16679 .owner = tbinfo->rolname,
16680 .description = "SECURITY LABEL",
16681 .section = SECTION_NONE,
16682 .createStmt = query->data,
16683 .deps = &(tbinfo->dobj.dumpId),
16684 .nDeps = 1));
16685 }
16686 destroyPQExpBuffer(query);
16687 destroyPQExpBuffer(target);
16688}
16689
16690/*
16691 * findSecLabels
16692 *
16693 * Find the security label(s), if any, associated with the given object.
16694 * All the objsubid values associated with the given classoid/objoid are
16695 * found with one search.
16696 */
16697static int
16699{
16700 SecLabelItem *middle = NULL;
16701 SecLabelItem *low;
16702 SecLabelItem *high;
16703 int nmatch;
16704
16705 if (nseclabels <= 0) /* no labels, so no match is possible */
16706 {
16707 *items = NULL;
16708 return 0;
16709 }
16710
16711 /*
16712 * Do binary search to find some item matching the object.
16713 */
16714 low = &seclabels[0];
16715 high = &seclabels[nseclabels - 1];
16716 while (low <= high)
16717 {
16718 middle = low + (high - low) / 2;
16719
16720 if (classoid < middle->classoid)
16721 high = middle - 1;
16722 else if (classoid > middle->classoid)
16723 low = middle + 1;
16724 else if (objoid < middle->objoid)
16725 high = middle - 1;
16726 else if (objoid > middle->objoid)
16727 low = middle + 1;
16728 else
16729 break; /* found a match */
16730 }
16731
16732 if (low > high) /* no matches */
16733 {
16734 *items = NULL;
16735 return 0;
16736 }
16737
16738 /*
16739 * Now determine how many items match the object. The search loop
16740 * invariant still holds: only items between low and high inclusive could
16741 * match.
16742 */
16743 nmatch = 1;
16744 while (middle > low)
16745 {
16746 if (classoid != middle[-1].classoid ||
16747 objoid != middle[-1].objoid)
16748 break;
16749 middle--;
16750 nmatch++;
16751 }
16752
16753 *items = middle;
16754
16755 middle += nmatch;
16756 while (middle <= high)
16757 {
16758 if (classoid != middle->classoid ||
16759 objoid != middle->objoid)
16760 break;
16761 middle++;
16762 nmatch++;
16763 }
16764
16765 return nmatch;
16766}
16767
16768/*
16769 * collectSecLabels
16770 *
16771 * Construct a table of all security labels available for database objects;
16772 * also set the has-seclabel component flag for each relevant object.
16773 *
16774 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16775 */
16776static void
16778{
16779 PGresult *res;
16780 PQExpBuffer query;
16781 int i_label;
16782 int i_provider;
16783 int i_classoid;
16784 int i_objoid;
16785 int i_objsubid;
16786 int ntups;
16787 int i;
16788 DumpableObject *dobj;
16789
16790 query = createPQExpBuffer();
16791
16793 "SELECT label, provider, classoid, objoid, objsubid "
16794 "FROM pg_catalog.pg_seclabels "
16795 "ORDER BY classoid, objoid, objsubid");
16796
16797 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16798
16799 /* Construct lookup table containing OIDs in numeric form */
16800 i_label = PQfnumber(res, "label");
16801 i_provider = PQfnumber(res, "provider");
16802 i_classoid = PQfnumber(res, "classoid");
16803 i_objoid = PQfnumber(res, "objoid");
16804 i_objsubid = PQfnumber(res, "objsubid");
16805
16806 ntups = PQntuples(res);
16807
16808 seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16809 nseclabels = 0;
16810 dobj = NULL;
16811
16812 for (i = 0; i < ntups; i++)
16813 {
16814 CatalogId objId;
16815 int subid;
16816
16817 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16818 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16819 subid = atoi(PQgetvalue(res, i, i_objsubid));
16820
16821 /* We needn't remember labels that don't match any dumpable object */
16822 if (dobj == NULL ||
16823 dobj->catId.tableoid != objId.tableoid ||
16824 dobj->catId.oid != objId.oid)
16825 dobj = findObjectByCatalogId(objId);
16826 if (dobj == NULL)
16827 continue;
16828
16829 /*
16830 * Labels on columns of composite types are linked to the type's
16831 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16832 * in the type's own DumpableObject.
16833 */
16834 if (subid != 0 && dobj->objType == DO_TABLE &&
16835 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16836 {
16837 TypeInfo *cTypeInfo;
16838
16839 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16840 if (cTypeInfo)
16842 }
16843 else
16844 dobj->components |= DUMP_COMPONENT_SECLABEL;
16845
16846 seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16847 seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16849 seclabels[nseclabels].objoid = objId.oid;
16850 seclabels[nseclabels].objsubid = subid;
16851 nseclabels++;
16852 }
16853
16854 PQclear(res);
16855 destroyPQExpBuffer(query);
16856}
16857
16858/*
16859 * dumpTable
16860 * write out to fout the declarations (not data) of a user-defined table
16861 */
16862static void
16863dumpTable(Archive *fout, const TableInfo *tbinfo)
16864{
16865 DumpOptions *dopt = fout->dopt;
16866 DumpId tableAclDumpId = InvalidDumpId;
16867 char *namecopy;
16868
16869 /* Do nothing if not dumping schema */
16870 if (!dopt->dumpSchema)
16871 return;
16872
16873 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16874 {
16875 if (tbinfo->relkind == RELKIND_SEQUENCE)
16876 dumpSequence(fout, tbinfo);
16877 else
16878 dumpTableSchema(fout, tbinfo);
16879 }
16880
16881 /* Handle the ACL here */
16882 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16883 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16884 {
16885 const char *objtype =
16886 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16887
16888 tableAclDumpId =
16889 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16890 objtype, namecopy, NULL,
16891 tbinfo->dobj.namespace->dobj.name,
16892 NULL, tbinfo->rolname, &tbinfo->dacl);
16893 }
16894
16895 /*
16896 * Handle column ACLs, if any. Note: we pull these with a separate query
16897 * rather than trying to fetch them during getTableAttrs, so that we won't
16898 * miss ACLs on system columns. Doing it this way also allows us to dump
16899 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16900 */
16901 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16902 {
16904 PGresult *res;
16905 int i;
16906
16908 {
16909 /* Set up query for column ACLs */
16911 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16912
16913 if (fout->remoteVersion >= 90600)
16914 {
16915 /*
16916 * In principle we should call acldefault('c', relowner) to
16917 * get the default ACL for a column. However, we don't
16918 * currently store the numeric OID of the relowner in
16919 * TableInfo. We could convert the owner name using regrole,
16920 * but that creates a risk of failure due to concurrent role
16921 * renames. Given that the default ACL for columns is empty
16922 * and is likely to stay that way, it's not worth extra cycles
16923 * and risk to avoid hard-wiring that knowledge here.
16924 */
16926 "SELECT at.attname, "
16927 "at.attacl, "
16928 "'{}' AS acldefault, "
16929 "pip.privtype, pip.initprivs "
16930 "FROM pg_catalog.pg_attribute at "
16931 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16932 "(at.attrelid = pip.objoid "
16933 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16934 "AND at.attnum = pip.objsubid) "
16935 "WHERE at.attrelid = $1 AND "
16936 "NOT at.attisdropped "
16937 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16938 "ORDER BY at.attnum");
16939 }
16940 else
16941 {
16943 "SELECT attname, attacl, '{}' AS acldefault, "
16944 "NULL AS privtype, NULL AS initprivs "
16945 "FROM pg_catalog.pg_attribute "
16946 "WHERE attrelid = $1 AND NOT attisdropped "
16947 "AND attacl IS NOT NULL "
16948 "ORDER BY attnum");
16949 }
16950
16951 ExecuteSqlStatement(fout, query->data);
16952
16954 }
16955
16956 printfPQExpBuffer(query,
16957 "EXECUTE getColumnACLs('%u')",
16958 tbinfo->dobj.catId.oid);
16959
16960 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16961
16962 for (i = 0; i < PQntuples(res); i++)
16963 {
16964 char *attname = PQgetvalue(res, i, 0);
16965 char *attacl = PQgetvalue(res, i, 1);
16966 char *acldefault = PQgetvalue(res, i, 2);
16967 char privtype = *(PQgetvalue(res, i, 3));
16968 char *initprivs = PQgetvalue(res, i, 4);
16969 DumpableAcl coldacl;
16970 char *attnamecopy;
16971
16972 coldacl.acl = attacl;
16973 coldacl.acldefault = acldefault;
16974 coldacl.privtype = privtype;
16975 coldacl.initprivs = initprivs;
16976 attnamecopy = pg_strdup(fmtId(attname));
16977
16978 /*
16979 * Column's GRANT type is always TABLE. Each column ACL depends
16980 * on the table-level ACL, since we can restore column ACLs in
16981 * parallel but the table-level ACL has to be done first.
16982 */
16983 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16984 "TABLE", namecopy, attnamecopy,
16985 tbinfo->dobj.namespace->dobj.name,
16986 NULL, tbinfo->rolname, &coldacl);
16987 free(attnamecopy);
16988 }
16989 PQclear(res);
16990 destroyPQExpBuffer(query);
16991 }
16992
16993 free(namecopy);
16994}
16995
16996/*
16997 * Create the AS clause for a view or materialized view. The semicolon is
16998 * stripped because a materialized view must add a WITH NO DATA clause.
16999 *
17000 * This returns a new buffer which must be freed by the caller.
17001 */
17002static PQExpBuffer
17004{
17006 PQExpBuffer result = createPQExpBuffer();
17007 PGresult *res;
17008 int len;
17009
17010 /* Fetch the view definition */
17011 appendPQExpBuffer(query,
17012 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17013 tbinfo->dobj.catId.oid);
17014
17015 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17016
17017 if (PQntuples(res) != 1)
17018 {
17019 if (PQntuples(res) < 1)
17020 pg_fatal("query to obtain definition of view \"%s\" returned no data",
17021 tbinfo->dobj.name);
17022 else
17023 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17024 tbinfo->dobj.name);
17025 }
17026
17027 len = PQgetlength(res, 0, 0);
17028
17029 if (len == 0)
17030 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17031 tbinfo->dobj.name);
17032
17033 /* Strip off the trailing semicolon so that other things may follow. */
17034 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17035 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17036
17037 PQclear(res);
17038 destroyPQExpBuffer(query);
17039
17040 return result;
17041}
17042
17043/*
17044 * Create a dummy AS clause for a view. This is used when the real view
17045 * definition has to be postponed because of circular dependencies.
17046 * We must duplicate the view's external properties -- column names and types
17047 * (including collation) -- so that it works for subsequent references.
17048 *
17049 * This returns a new buffer which must be freed by the caller.
17050 */
17051static PQExpBuffer
17053{
17054 PQExpBuffer result = createPQExpBuffer();
17055 int j;
17056
17057 appendPQExpBufferStr(result, "SELECT");
17058
17059 for (j = 0; j < tbinfo->numatts; j++)
17060 {
17061 if (j > 0)
17062 appendPQExpBufferChar(result, ',');
17063 appendPQExpBufferStr(result, "\n ");
17064
17065 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17066
17067 /*
17068 * Must add collation if not default for the type, because CREATE OR
17069 * REPLACE VIEW won't change it
17070 */
17071 if (OidIsValid(tbinfo->attcollation[j]))
17072 {
17073 CollInfo *coll;
17074
17075 coll = findCollationByOid(tbinfo->attcollation[j]);
17076 if (coll)
17077 appendPQExpBuffer(result, " COLLATE %s",
17078 fmtQualifiedDumpable(coll));
17079 }
17080
17081 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17082 }
17083
17084 return result;
17085}
17086
17087/*
17088 * dumpTableSchema
17089 * write the declaration (not data) of one user-defined table or view
17090 */
17091static void
17092dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17093{
17094 DumpOptions *dopt = fout->dopt;
17098 char *qrelname;
17099 char *qualrelname;
17100 int numParents;
17101 TableInfo **parents;
17102 int actual_atts; /* number of attrs in this CREATE statement */
17103 const char *reltypename;
17104 char *storage;
17105 int j,
17106 k;
17107
17108 /* We had better have loaded per-column details about this table */
17109 Assert(tbinfo->interesting);
17110
17111 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17112 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17113
17114 if (tbinfo->hasoids)
17115 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17116 qrelname);
17117
17118 if (dopt->binary_upgrade)
17119 binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17120
17121 /* Is it a table or a view? */
17122 if (tbinfo->relkind == RELKIND_VIEW)
17123 {
17124 PQExpBuffer result;
17125
17126 /*
17127 * Note: keep this code in sync with the is_view case in dumpRule()
17128 */
17129
17130 reltypename = "VIEW";
17131
17132 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17133
17134 if (dopt->binary_upgrade)
17136 tbinfo->dobj.catId.oid);
17137
17138 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17139
17140 if (tbinfo->dummy_view)
17141 result = createDummyViewAsClause(fout, tbinfo);
17142 else
17143 {
17144 if (nonemptyReloptions(tbinfo->reloptions))
17145 {
17146 appendPQExpBufferStr(q, " WITH (");
17147 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17148 appendPQExpBufferChar(q, ')');
17149 }
17150 result = createViewAsClause(fout, tbinfo);
17151 }
17152 appendPQExpBuffer(q, " AS\n%s", result->data);
17153 destroyPQExpBuffer(result);
17154
17155 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17156 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17157 appendPQExpBufferStr(q, ";\n");
17158 }
17159 else
17160 {
17161 char *partkeydef = NULL;
17162 char *ftoptions = NULL;
17163 char *srvname = NULL;
17164 const char *foreign = "";
17165
17166 /*
17167 * Set reltypename, and collect any relkind-specific data that we
17168 * didn't fetch during getTables().
17169 */
17170 switch (tbinfo->relkind)
17171 {
17172 case RELKIND_PARTITIONED_TABLE:
17173 {
17175 PGresult *res;
17176
17177 reltypename = "TABLE";
17178
17179 /* retrieve partition key definition */
17180 appendPQExpBuffer(query,
17181 "SELECT pg_get_partkeydef('%u')",
17182 tbinfo->dobj.catId.oid);
17183 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17184 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17185 PQclear(res);
17186 destroyPQExpBuffer(query);
17187 break;
17188 }
17189 case RELKIND_FOREIGN_TABLE:
17190 {
17192 PGresult *res;
17193 int i_srvname;
17194 int i_ftoptions;
17195
17196 reltypename = "FOREIGN TABLE";
17197
17198 /* retrieve name of foreign server and generic options */
17199 appendPQExpBuffer(query,
17200 "SELECT fs.srvname, "
17201 "pg_catalog.array_to_string(ARRAY("
17202 "SELECT pg_catalog.quote_ident(option_name) || "
17203 "' ' || pg_catalog.quote_literal(option_value) "
17204 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17205 "ORDER BY option_name"
17206 "), E',\n ') AS ftoptions "
17207 "FROM pg_catalog.pg_foreign_table ft "
17208 "JOIN pg_catalog.pg_foreign_server fs "
17209 "ON (fs.oid = ft.ftserver) "
17210 "WHERE ft.ftrelid = '%u'",
17211 tbinfo->dobj.catId.oid);
17212 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17213 i_srvname = PQfnumber(res, "srvname");
17214 i_ftoptions = PQfnumber(res, "ftoptions");
17215 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17216 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17217 PQclear(res);
17218 destroyPQExpBuffer(query);
17219
17220 foreign = "FOREIGN ";
17221 break;
17222 }
17223 case RELKIND_MATVIEW:
17224 reltypename = "MATERIALIZED VIEW";
17225 break;
17226 default:
17227 reltypename = "TABLE";
17228 break;
17229 }
17230
17231 numParents = tbinfo->numParents;
17232 parents = tbinfo->parents;
17233
17234 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17235
17236 if (dopt->binary_upgrade)
17238 tbinfo->dobj.catId.oid);
17239
17240 /*
17241 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17242 * ignore it when dumping if it was set in this case.
17243 */
17244 appendPQExpBuffer(q, "CREATE %s%s %s",
17245 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17246 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17247 "UNLOGGED " : "",
17248 reltypename,
17249 qualrelname);
17250
17251 /*
17252 * Attach to type, if reloftype; except in case of a binary upgrade,
17253 * we dump the table normally and attach it to the type afterward.
17254 */
17255 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17256 appendPQExpBuffer(q, " OF %s",
17257 getFormattedTypeName(fout, tbinfo->reloftype,
17258 zeroIsError));
17259
17260 if (tbinfo->relkind != RELKIND_MATVIEW)
17261 {
17262 /* Dump the attributes */
17263 actual_atts = 0;
17264 for (j = 0; j < tbinfo->numatts; j++)
17265 {
17266 /*
17267 * Normally, dump if it's locally defined in this table, and
17268 * not dropped. But for binary upgrade, we'll dump all the
17269 * columns, and then fix up the dropped and nonlocal cases
17270 * below.
17271 */
17272 if (shouldPrintColumn(dopt, tbinfo, j))
17273 {
17274 bool print_default;
17275 bool print_notnull;
17276
17277 /*
17278 * Default value --- suppress if to be printed separately
17279 * or not at all.
17280 */
17281 print_default = (tbinfo->attrdefs[j] != NULL &&
17282 tbinfo->attrdefs[j]->dobj.dump &&
17283 !tbinfo->attrdefs[j]->separate);
17284
17285 /*
17286 * Not Null constraint --- print it if it is locally
17287 * defined, or if binary upgrade. (In the latter case, we
17288 * reset conislocal below.)
17289 */
17290 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17291 (tbinfo->notnull_islocal[j] ||
17292 dopt->binary_upgrade ||
17293 tbinfo->ispartition));
17294
17295 /*
17296 * Skip column if fully defined by reloftype, except in
17297 * binary upgrade
17298 */
17299 if (OidIsValid(tbinfo->reloftype) &&
17300 !print_default && !print_notnull &&
17301 !dopt->binary_upgrade)
17302 continue;
17303
17304 /* Format properly if not first attr */
17305 if (actual_atts == 0)
17306 appendPQExpBufferStr(q, " (");
17307 else
17308 appendPQExpBufferChar(q, ',');
17309 appendPQExpBufferStr(q, "\n ");
17310 actual_atts++;
17311
17312 /* Attribute name */
17313 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17314
17315 if (tbinfo->attisdropped[j])
17316 {
17317 /*
17318 * ALTER TABLE DROP COLUMN clears
17319 * pg_attribute.atttypid, so we will not have gotten a
17320 * valid type name; insert INTEGER as a stopgap. We'll
17321 * clean things up later.
17322 */
17323 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17324 /* and skip to the next column */
17325 continue;
17326 }
17327
17328 /*
17329 * Attribute type; print it except when creating a typed
17330 * table ('OF type_name'), but in binary-upgrade mode,
17331 * print it in that case too.
17332 */
17333 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17334 {
17335 appendPQExpBuffer(q, " %s",
17336 tbinfo->atttypnames[j]);
17337 }
17338
17339 if (print_default)
17340 {
17341 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17342 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17343 tbinfo->attrdefs[j]->adef_expr);
17344 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17345 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17346 tbinfo->attrdefs[j]->adef_expr);
17347 else
17348 appendPQExpBuffer(q, " DEFAULT %s",
17349 tbinfo->attrdefs[j]->adef_expr);
17350 }
17351
17352 if (print_notnull)
17353 {
17354 if (tbinfo->notnull_constrs[j][0] == '\0')
17355 appendPQExpBufferStr(q, " NOT NULL");
17356 else
17357 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17358 fmtId(tbinfo->notnull_constrs[j]));
17359
17360 if (tbinfo->notnull_noinh[j])
17361 appendPQExpBufferStr(q, " NO INHERIT");
17362 }
17363
17364 /* Add collation if not default for the type */
17365 if (OidIsValid(tbinfo->attcollation[j]))
17366 {
17367 CollInfo *coll;
17368
17369 coll = findCollationByOid(tbinfo->attcollation[j]);
17370 if (coll)
17371 appendPQExpBuffer(q, " COLLATE %s",
17372 fmtQualifiedDumpable(coll));
17373 }
17374 }
17375
17376 /*
17377 * On the other hand, if we choose not to print a column
17378 * (likely because it is created by inheritance), but the
17379 * column has a locally-defined not-null constraint, we need
17380 * to dump the constraint as a standalone object.
17381 *
17382 * This syntax isn't SQL-conforming, but if you wanted
17383 * standard output you wouldn't be creating non-standard
17384 * objects to begin with.
17385 */
17386 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17387 !tbinfo->attisdropped[j] &&
17388 tbinfo->notnull_constrs[j] != NULL &&
17389 tbinfo->notnull_islocal[j])
17390 {
17391 /* Format properly if not first attr */
17392 if (actual_atts == 0)
17393 appendPQExpBufferStr(q, " (");
17394 else
17395 appendPQExpBufferChar(q, ',');
17396 appendPQExpBufferStr(q, "\n ");
17397 actual_atts++;
17398
17399 if (tbinfo->notnull_constrs[j][0] == '\0')
17400 appendPQExpBuffer(q, "NOT NULL %s",
17401 fmtId(tbinfo->attnames[j]));
17402 else
17403 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17404 tbinfo->notnull_constrs[j],
17405 fmtId(tbinfo->attnames[j]));
17406 }
17407 }
17408
17409 /*
17410 * Add non-inherited CHECK constraints, if any.
17411 *
17412 * For partitions, we need to include check constraints even if
17413 * they're not defined locally, because the ALTER TABLE ATTACH
17414 * PARTITION that we'll emit later expects the constraint to be
17415 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17416 */
17417 for (j = 0; j < tbinfo->ncheck; j++)
17418 {
17419 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17420
17421 if (constr->separate ||
17422 (!constr->conislocal && !tbinfo->ispartition))
17423 continue;
17424
17425 if (actual_atts == 0)
17426 appendPQExpBufferStr(q, " (\n ");
17427 else
17428 appendPQExpBufferStr(q, ",\n ");
17429
17430 appendPQExpBuffer(q, "CONSTRAINT %s ",
17431 fmtId(constr->dobj.name));
17432 appendPQExpBufferStr(q, constr->condef);
17433
17434 actual_atts++;
17435 }
17436
17437 if (actual_atts)
17438 appendPQExpBufferStr(q, "\n)");
17439 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17440 {
17441 /*
17442 * No attributes? we must have a parenthesized attribute list,
17443 * even though empty, when not using the OF TYPE syntax.
17444 */
17445 appendPQExpBufferStr(q, " (\n)");
17446 }
17447
17448 /*
17449 * Emit the INHERITS clause (not for partitions), except in
17450 * binary-upgrade mode.
17451 */
17452 if (numParents > 0 && !tbinfo->ispartition &&
17453 !dopt->binary_upgrade)
17454 {
17455 appendPQExpBufferStr(q, "\nINHERITS (");
17456 for (k = 0; k < numParents; k++)
17457 {
17458 TableInfo *parentRel = parents[k];
17459
17460 if (k > 0)
17461 appendPQExpBufferStr(q, ", ");
17463 }
17464 appendPQExpBufferChar(q, ')');
17465 }
17466
17467 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17468 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17469
17470 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17471 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17472 }
17473
17474 if (nonemptyReloptions(tbinfo->reloptions) ||
17476 {
17477 bool addcomma = false;
17478
17479 appendPQExpBufferStr(q, "\nWITH (");
17480 if (nonemptyReloptions(tbinfo->reloptions))
17481 {
17482 addcomma = true;
17483 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17484 }
17486 {
17487 if (addcomma)
17488 appendPQExpBufferStr(q, ", ");
17489 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17490 fout);
17491 }
17492 appendPQExpBufferChar(q, ')');
17493 }
17494
17495 /* Dump generic options if any */
17496 if (ftoptions && ftoptions[0])
17497 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17498
17499 /*
17500 * For materialized views, create the AS clause just like a view. At
17501 * this point, we always mark the view as not populated.
17502 */
17503 if (tbinfo->relkind == RELKIND_MATVIEW)
17504 {
17505 PQExpBuffer result;
17506
17507 result = createViewAsClause(fout, tbinfo);
17508 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17509 result->data);
17510 destroyPQExpBuffer(result);
17511 }
17512 else
17513 appendPQExpBufferStr(q, ";\n");
17514
17515 /* Materialized views can depend on extensions */
17516 if (tbinfo->relkind == RELKIND_MATVIEW)
17517 append_depends_on_extension(fout, q, &tbinfo->dobj,
17518 "pg_catalog.pg_class",
17519 "MATERIALIZED VIEW",
17520 qualrelname);
17521
17522 /*
17523 * in binary upgrade mode, update the catalog with any missing values
17524 * that might be present.
17525 */
17526 if (dopt->binary_upgrade)
17527 {
17528 for (j = 0; j < tbinfo->numatts; j++)
17529 {
17530 if (tbinfo->attmissingval[j][0] != '\0')
17531 {
17532 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17534 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17535 appendStringLiteralAH(q, qualrelname, fout);
17536 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17537 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17538 appendPQExpBufferChar(q, ',');
17539 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17540 appendPQExpBufferStr(q, ");\n\n");
17541 }
17542 }
17543 }
17544
17545 /*
17546 * To create binary-compatible heap files, we have to ensure the same
17547 * physical column order, including dropped columns, as in the
17548 * original. Therefore, we create dropped columns above and drop them
17549 * here, also updating their attlen/attalign values so that the
17550 * dropped column can be skipped properly. (We do not bother with
17551 * restoring the original attbyval setting.) Also, inheritance
17552 * relationships are set up by doing ALTER TABLE INHERIT rather than
17553 * using an INHERITS clause --- the latter would possibly mess up the
17554 * column order. That also means we have to take care about setting
17555 * attislocal correctly, plus fix up any inherited CHECK constraints.
17556 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17557 *
17558 * We process foreign and partitioned tables here, even though they
17559 * lack heap storage, because they can participate in inheritance
17560 * relationships and we want this stuff to be consistent across the
17561 * inheritance tree. We can exclude indexes, toast tables, sequences
17562 * and matviews, even though they have storage, because we don't
17563 * support altering or dropping columns in them, nor can they be part
17564 * of inheritance trees.
17565 */
17566 if (dopt->binary_upgrade &&
17567 (tbinfo->relkind == RELKIND_RELATION ||
17568 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17569 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17570 {
17571 bool firstitem;
17572 bool firstitem_extra;
17573
17574 /*
17575 * Drop any dropped columns. Merge the pg_attribute manipulations
17576 * into a single SQL command, so that we don't cause repeated
17577 * relcache flushes on the target table. Otherwise we risk O(N^2)
17578 * relcache bloat while dropping N columns.
17579 */
17580 resetPQExpBuffer(extra);
17581 firstitem = true;
17582 for (j = 0; j < tbinfo->numatts; j++)
17583 {
17584 if (tbinfo->attisdropped[j])
17585 {
17586 if (firstitem)
17587 {
17588 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17589 "UPDATE pg_catalog.pg_attribute\n"
17590 "SET attlen = v.dlen, "
17591 "attalign = v.dalign, "
17592 "attbyval = false\n"
17593 "FROM (VALUES ");
17594 firstitem = false;
17595 }
17596 else
17597 appendPQExpBufferStr(q, ",\n ");
17598 appendPQExpBufferChar(q, '(');
17599 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17600 appendPQExpBuffer(q, ", %d, '%c')",
17601 tbinfo->attlen[j],
17602 tbinfo->attalign[j]);
17603 /* The ALTER ... DROP COLUMN commands must come after */
17604 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17605 foreign, qualrelname);
17606 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17607 fmtId(tbinfo->attnames[j]));
17608 }
17609 }
17610 if (!firstitem)
17611 {
17612 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17613 "WHERE attrelid = ");
17614 appendStringLiteralAH(q, qualrelname, fout);
17615 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17616 " AND attname = v.dname;\n");
17617 /* Now we can issue the actual DROP COLUMN commands */
17618 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17619 }
17620
17621 /*
17622 * Fix up inherited columns. As above, do the pg_attribute
17623 * manipulations in a single SQL command.
17624 */
17625 firstitem = true;
17626 for (j = 0; j < tbinfo->numatts; j++)
17627 {
17628 if (!tbinfo->attisdropped[j] &&
17629 !tbinfo->attislocal[j])
17630 {
17631 if (firstitem)
17632 {
17633 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17634 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17635 "SET attislocal = false\n"
17636 "WHERE attrelid = ");
17637 appendStringLiteralAH(q, qualrelname, fout);
17638 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17639 " AND attname IN (");
17640 firstitem = false;
17641 }
17642 else
17643 appendPQExpBufferStr(q, ", ");
17644 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17645 }
17646 }
17647 if (!firstitem)
17648 appendPQExpBufferStr(q, ");\n");
17649
17650 /*
17651 * Fix up not-null constraints that come from inheritance. As
17652 * above, do the pg_constraint manipulations in a single SQL
17653 * command. (Actually, two in special cases, if we're doing an
17654 * upgrade from < 18).
17655 */
17656 firstitem = true;
17657 firstitem_extra = true;
17658 resetPQExpBuffer(extra);
17659 for (j = 0; j < tbinfo->numatts; j++)
17660 {
17661 /*
17662 * If a not-null constraint comes from inheritance, reset
17663 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17664 * below. Special hack: in versions < 18, columns with no
17665 * local definition need their constraint to be matched by
17666 * column number in conkeys instead of by constraint name,
17667 * because the latter is not available. (We distinguish the
17668 * case because the constraint name is the empty string.)
17669 */
17670 if (tbinfo->notnull_constrs[j] != NULL &&
17671 !tbinfo->notnull_islocal[j])
17672 {
17673 if (tbinfo->notnull_constrs[j][0] != '\0')
17674 {
17675 if (firstitem)
17676 {
17677 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17678 "SET conislocal = false\n"
17679 "WHERE contype = 'n' AND conrelid = ");
17680 appendStringLiteralAH(q, qualrelname, fout);
17681 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17682 "conname IN (");
17683 firstitem = false;
17684 }
17685 else
17686 appendPQExpBufferStr(q, ", ");
17687 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17688 }
17689 else
17690 {
17691 if (firstitem_extra)
17692 {
17693 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17694 "SET conislocal = false\n"
17695 "WHERE contype = 'n' AND conrelid = ");
17696 appendStringLiteralAH(extra, qualrelname, fout);
17697 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17698 "conkey IN (");
17699 firstitem_extra = false;
17700 }
17701 else
17702 appendPQExpBufferStr(extra, ", ");
17703 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17704 }
17705 }
17706 }
17707 if (!firstitem)
17708 appendPQExpBufferStr(q, ");\n");
17709 if (!firstitem_extra)
17710 appendPQExpBufferStr(extra, ");\n");
17711
17712 if (extra->len > 0)
17713 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17714
17715 /*
17716 * Add inherited CHECK constraints, if any.
17717 *
17718 * For partitions, they were already dumped, and conislocal
17719 * doesn't need fixing.
17720 *
17721 * As above, issue only one direct manipulation of pg_constraint.
17722 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17723 * commands into one as well, refrain for now due to concern about
17724 * possible backend memory bloat if there are many such
17725 * constraints.
17726 */
17727 resetPQExpBuffer(extra);
17728 firstitem = true;
17729 for (k = 0; k < tbinfo->ncheck; k++)
17730 {
17731 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17732
17733 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17734 continue;
17735
17736 if (firstitem)
17737 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17738 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17739 foreign, qualrelname,
17740 fmtId(constr->dobj.name),
17741 constr->condef);
17742 /* Update pg_constraint after all the ALTER TABLEs */
17743 if (firstitem)
17744 {
17745 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17746 "SET conislocal = false\n"
17747 "WHERE contype = 'c' AND conrelid = ");
17748 appendStringLiteralAH(extra, qualrelname, fout);
17749 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17750 appendPQExpBufferStr(extra, " AND conname IN (");
17751 firstitem = false;
17752 }
17753 else
17754 appendPQExpBufferStr(extra, ", ");
17755 appendStringLiteralAH(extra, constr->dobj.name, fout);
17756 }
17757 if (!firstitem)
17758 {
17759 appendPQExpBufferStr(extra, ");\n");
17760 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17761 }
17762
17763 if (numParents > 0 && !tbinfo->ispartition)
17764 {
17765 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17766 for (k = 0; k < numParents; k++)
17767 {
17768 TableInfo *parentRel = parents[k];
17769
17770 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17771 qualrelname,
17772 fmtQualifiedDumpable(parentRel));
17773 }
17774 }
17775
17776 if (OidIsValid(tbinfo->reloftype))
17777 {
17778 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17779 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17780 qualrelname,
17781 getFormattedTypeName(fout, tbinfo->reloftype,
17782 zeroIsError));
17783 }
17784 }
17785
17786 /*
17787 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17788 * relminmxid of all vacuumable relations. (While vacuum.c processes
17789 * TOAST tables semi-independently, here we see them only as children
17790 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17791 * child toast table is handled below.)
17792 */
17793 if (dopt->binary_upgrade &&
17794 (tbinfo->relkind == RELKIND_RELATION ||
17795 tbinfo->relkind == RELKIND_MATVIEW))
17796 {
17797 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17798 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17799 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17800 "WHERE oid = ",
17801 tbinfo->frozenxid, tbinfo->minmxid);
17802 appendStringLiteralAH(q, qualrelname, fout);
17803 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17804
17805 if (tbinfo->toast_oid)
17806 {
17807 /*
17808 * The toast table will have the same OID at restore, so we
17809 * can safely target it by OID.
17810 */
17811 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17812 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17813 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17814 "WHERE oid = '%u';\n",
17815 tbinfo->toast_frozenxid,
17816 tbinfo->toast_minmxid, tbinfo->toast_oid);
17817 }
17818 }
17819
17820 /*
17821 * In binary_upgrade mode, restore matviews' populated status by
17822 * poking pg_class directly. This is pretty ugly, but we can't use
17823 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17824 * matview is not populated even though this matview is; in any case,
17825 * we want to transfer the matview's heap storage, not run REFRESH.
17826 */
17827 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17828 tbinfo->relispopulated)
17829 {
17830 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17831 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17832 "SET relispopulated = 't'\n"
17833 "WHERE oid = ");
17834 appendStringLiteralAH(q, qualrelname, fout);
17835 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17836 }
17837
17838 /*
17839 * Dump additional per-column properties that we can't handle in the
17840 * main CREATE TABLE command.
17841 */
17842 for (j = 0; j < tbinfo->numatts; j++)
17843 {
17844 /* None of this applies to dropped columns */
17845 if (tbinfo->attisdropped[j])
17846 continue;
17847
17848 /*
17849 * Dump per-column statistics information. We only issue an ALTER
17850 * TABLE statement if the attstattarget entry for this column is
17851 * not the default value.
17852 */
17853 if (tbinfo->attstattarget[j] >= 0)
17854 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17855 foreign, qualrelname,
17856 fmtId(tbinfo->attnames[j]),
17857 tbinfo->attstattarget[j]);
17858
17859 /*
17860 * Dump per-column storage information. The statement is only
17861 * dumped if the storage has been changed from the type's default.
17862 */
17863 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17864 {
17865 switch (tbinfo->attstorage[j])
17866 {
17867 case TYPSTORAGE_PLAIN:
17868 storage = "PLAIN";
17869 break;
17870 case TYPSTORAGE_EXTERNAL:
17871 storage = "EXTERNAL";
17872 break;
17873 case TYPSTORAGE_EXTENDED:
17874 storage = "EXTENDED";
17875 break;
17876 case TYPSTORAGE_MAIN:
17877 storage = "MAIN";
17878 break;
17879 default:
17880 storage = NULL;
17881 }
17882
17883 /*
17884 * Only dump the statement if it's a storage type we recognize
17885 */
17886 if (storage != NULL)
17887 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17888 foreign, qualrelname,
17889 fmtId(tbinfo->attnames[j]),
17890 storage);
17891 }
17892
17893 /*
17894 * Dump per-column compression, if it's been set.
17895 */
17896 if (!dopt->no_toast_compression)
17897 {
17898 const char *cmname;
17899
17900 switch (tbinfo->attcompression[j])
17901 {
17902 case 'p':
17903 cmname = "pglz";
17904 break;
17905 case 'l':
17906 cmname = "lz4";
17907 break;
17908 default:
17909 cmname = NULL;
17910 break;
17911 }
17912
17913 if (cmname != NULL)
17914 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17915 foreign, qualrelname,
17916 fmtId(tbinfo->attnames[j]),
17917 cmname);
17918 }
17919
17920 /*
17921 * Dump per-column attributes.
17922 */
17923 if (tbinfo->attoptions[j][0] != '\0')
17924 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17925 foreign, qualrelname,
17926 fmtId(tbinfo->attnames[j]),
17927 tbinfo->attoptions[j]);
17928
17929 /*
17930 * Dump per-column fdw options.
17931 */
17932 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17933 tbinfo->attfdwoptions[j][0] != '\0')
17935 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17936 " %s\n"
17937 ");\n",
17938 qualrelname,
17939 fmtId(tbinfo->attnames[j]),
17940 tbinfo->attfdwoptions[j]);
17941 } /* end loop over columns */
17942
17943 free(partkeydef);
17944 free(ftoptions);
17945 free(srvname);
17946 }
17947
17948 /*
17949 * dump properties we only have ALTER TABLE syntax for
17950 */
17951 if ((tbinfo->relkind == RELKIND_RELATION ||
17952 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17953 tbinfo->relkind == RELKIND_MATVIEW) &&
17954 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17955 {
17956 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17957 {
17958 /* nothing to do, will be set when the index is dumped */
17959 }
17960 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17961 {
17962 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17963 qualrelname);
17964 }
17965 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17966 {
17967 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17968 qualrelname);
17969 }
17970 }
17971
17972 if (tbinfo->forcerowsec)
17973 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17974 qualrelname);
17975
17976 if (dopt->binary_upgrade)
17978 reltypename, qrelname,
17979 tbinfo->dobj.namespace->dobj.name);
17980
17981 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17982 {
17983 char *tablespace = NULL;
17984 char *tableam = NULL;
17985
17986 /*
17987 * _selectTablespace() relies on tablespace-enabled objects in the
17988 * default tablespace to have a tablespace of "" (empty string) versus
17989 * non-tablespace-enabled objects to have a tablespace of NULL.
17990 * getTables() sets tbinfo->reltablespace to "" for the default
17991 * tablespace (not NULL).
17992 */
17993 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17994 tablespace = tbinfo->reltablespace;
17995
17996 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17997 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17998 tableam = tbinfo->amname;
17999
18000 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18001 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18002 .namespace = tbinfo->dobj.namespace->dobj.name,
18003 .tablespace = tablespace,
18004 .tableam = tableam,
18005 .relkind = tbinfo->relkind,
18006 .owner = tbinfo->rolname,
18007 .description = reltypename,
18008 .section = tbinfo->postponed_def ?
18010 .createStmt = q->data,
18011 .dropStmt = delq->data));
18012 }
18013
18014 /* Dump Table Comments */
18015 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18016 dumpTableComment(fout, tbinfo, reltypename);
18017
18018 /* Dump Table Security Labels */
18019 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18020 dumpTableSecLabel(fout, tbinfo, reltypename);
18021
18022 /*
18023 * Dump comments for not-null constraints that aren't to be dumped
18024 * separately (those are processed by collectComments/dumpComment).
18025 */
18026 if (!fout->dopt->no_comments && dopt->dumpSchema &&
18027 fout->remoteVersion >= 180000)
18028 {
18029 PQExpBuffer comment = NULL;
18030 PQExpBuffer tag = NULL;
18031
18032 for (j = 0; j < tbinfo->numatts; j++)
18033 {
18034 if (tbinfo->notnull_constrs[j] != NULL &&
18035 tbinfo->notnull_comment[j] != NULL)
18036 {
18037 if (comment == NULL)
18038 {
18040 tag = createPQExpBuffer();
18041 }
18042 else
18043 {
18045 resetPQExpBuffer(tag);
18046 }
18047
18048 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18049 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18052
18053 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18054 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18055
18057 ARCHIVE_OPTS(.tag = tag->data,
18058 .namespace = tbinfo->dobj.namespace->dobj.name,
18059 .owner = tbinfo->rolname,
18060 .description = "COMMENT",
18061 .section = SECTION_NONE,
18062 .createStmt = comment->data,
18063 .deps = &(tbinfo->dobj.dumpId),
18064 .nDeps = 1));
18065 }
18066 }
18067
18069 destroyPQExpBuffer(tag);
18070 }
18071
18072 /* Dump comments on inlined table constraints */
18073 for (j = 0; j < tbinfo->ncheck; j++)
18074 {
18075 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18076
18077 if (constr->separate || !constr->conislocal)
18078 continue;
18079
18080 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18081 dumpTableConstraintComment(fout, constr);
18082 }
18083
18085 destroyPQExpBuffer(delq);
18086 destroyPQExpBuffer(extra);
18087 free(qrelname);
18088 free(qualrelname);
18089}
18090
18091/*
18092 * dumpTableAttach
18093 * write to fout the commands to attach a child partition
18094 *
18095 * Child partitions are always made by creating them separately
18096 * and then using ATTACH PARTITION, rather than using
18097 * CREATE TABLE ... PARTITION OF. This is important for preserving
18098 * any possible discrepancy in column layout, to allow assigning the
18099 * correct tablespace if different, and so that it's possible to restore
18100 * a partition without restoring its parent. (You'll get an error from
18101 * the ATTACH PARTITION command, but that can be ignored, or skipped
18102 * using "pg_restore -L" if you prefer.) The last point motivates
18103 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18104 * rather than emitting it within the child partition's ArchiveEntry.
18105 */
18106static void
18107dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18108{
18109 DumpOptions *dopt = fout->dopt;
18110 PQExpBuffer q;
18111 PGresult *res;
18112 char *partbound;
18113
18114 /* Do nothing if not dumping schema */
18115 if (!dopt->dumpSchema)
18116 return;
18117
18118 q = createPQExpBuffer();
18119
18121 {
18122 /* Set up query for partbound details */
18124 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18125
18127 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18128 "FROM pg_class c "
18129 "WHERE c.oid = $1");
18130
18131 ExecuteSqlStatement(fout, q->data);
18132
18134 }
18135
18137 "EXECUTE dumpTableAttach('%u')",
18138 attachinfo->partitionTbl->dobj.catId.oid);
18139
18140 res = ExecuteSqlQueryForSingleRow(fout, q->data);
18141 partbound = PQgetvalue(res, 0, 0);
18142
18143 /* Perform ALTER TABLE on the parent */
18145 "ALTER TABLE ONLY %s ",
18146 fmtQualifiedDumpable(attachinfo->parentTbl));
18148 "ATTACH PARTITION %s %s;\n",
18150 partbound);
18151
18152 /*
18153 * There is no point in creating a drop query as the drop is done by table
18154 * drop. (If you think to change this, see also _printTocEntry().)
18155 * Although this object doesn't really have ownership as such, set the
18156 * owner field anyway to ensure that the command is run by the correct
18157 * role at restore time.
18158 */
18159 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18160 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18161 .namespace = attachinfo->dobj.namespace->dobj.name,
18162 .owner = attachinfo->partitionTbl->rolname,
18163 .description = "TABLE ATTACH",
18164 .section = SECTION_PRE_DATA,
18165 .createStmt = q->data));
18166
18167 PQclear(res);
18169}
18170
18171/*
18172 * dumpAttrDef --- dump an attribute's default-value declaration
18173 */
18174static void
18175dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18176{
18177 DumpOptions *dopt = fout->dopt;
18178 TableInfo *tbinfo = adinfo->adtable;
18179 int adnum = adinfo->adnum;
18180 PQExpBuffer q;
18181 PQExpBuffer delq;
18182 char *qualrelname;
18183 char *tag;
18184 char *foreign;
18185
18186 /* Do nothing if not dumping schema */
18187 if (!dopt->dumpSchema)
18188 return;
18189
18190 /* Skip if not "separate"; it was dumped in the table's definition */
18191 if (!adinfo->separate)
18192 return;
18193
18194 q = createPQExpBuffer();
18195 delq = createPQExpBuffer();
18196
18197 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18198
18199 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18200
18202 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18203 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18204 adinfo->adef_expr);
18205
18206 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18207 foreign, qualrelname,
18208 fmtId(tbinfo->attnames[adnum - 1]));
18209
18210 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18211
18212 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18213 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18214 ARCHIVE_OPTS(.tag = tag,
18215 .namespace = tbinfo->dobj.namespace->dobj.name,
18216 .owner = tbinfo->rolname,
18217 .description = "DEFAULT",
18218 .section = SECTION_PRE_DATA,
18219 .createStmt = q->data,
18220 .dropStmt = delq->data));
18221
18222 free(tag);
18224 destroyPQExpBuffer(delq);
18225 free(qualrelname);
18226}
18227
18228/*
18229 * getAttrName: extract the correct name for an attribute
18230 *
18231 * The array tblInfo->attnames[] only provides names of user attributes;
18232 * if a system attribute number is supplied, we have to fake it.
18233 * We also do a little bit of bounds checking for safety's sake.
18234 */
18235static const char *
18236getAttrName(int attrnum, const TableInfo *tblInfo)
18237{
18238 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18239 return tblInfo->attnames[attrnum - 1];
18240 switch (attrnum)
18241 {
18243 return "ctid";
18245 return "xmin";
18247 return "cmin";
18249 return "xmax";
18251 return "cmax";
18253 return "tableoid";
18254 }
18255 pg_fatal("invalid column number %d for table \"%s\"",
18256 attrnum, tblInfo->dobj.name);
18257 return NULL; /* keep compiler quiet */
18258}
18259
18260/*
18261 * dumpIndex
18262 * write out to fout a user-defined index
18263 */
18264static void
18265dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18266{
18267 DumpOptions *dopt = fout->dopt;
18268 TableInfo *tbinfo = indxinfo->indextable;
18269 bool is_constraint = (indxinfo->indexconstraint != 0);
18270 PQExpBuffer q;
18271 PQExpBuffer delq;
18272 char *qindxname;
18273 char *qqindxname;
18274
18275 /* Do nothing if not dumping schema */
18276 if (!dopt->dumpSchema)
18277 return;
18278
18279 q = createPQExpBuffer();
18280 delq = createPQExpBuffer();
18281
18282 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18283 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18284
18285 /*
18286 * If there's an associated constraint, don't dump the index per se, but
18287 * do dump any comment for it. (This is safe because dependency ordering
18288 * will have ensured the constraint is emitted first.) Note that the
18289 * emitted comment has to be shown as depending on the constraint, not the
18290 * index, in such cases.
18291 */
18292 if (!is_constraint)
18293 {
18294 char *indstatcols = indxinfo->indstatcols;
18295 char *indstatvals = indxinfo->indstatvals;
18296 char **indstatcolsarray = NULL;
18297 char **indstatvalsarray = NULL;
18298 int nstatcols = 0;
18299 int nstatvals = 0;
18300
18301 if (dopt->binary_upgrade)
18303 indxinfo->dobj.catId.oid);
18304
18305 /* Plain secondary index */
18306 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18307
18308 /*
18309 * Append ALTER TABLE commands as needed to set properties that we
18310 * only have ALTER TABLE syntax for. Keep this in sync with the
18311 * similar code in dumpConstraint!
18312 */
18313
18314 /* If the index is clustered, we need to record that. */
18315 if (indxinfo->indisclustered)
18316 {
18317 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18318 fmtQualifiedDumpable(tbinfo));
18319 /* index name is not qualified in this syntax */
18320 appendPQExpBuffer(q, " ON %s;\n",
18321 qindxname);
18322 }
18323
18324 /*
18325 * If the index has any statistics on some of its columns, generate
18326 * the associated ALTER INDEX queries.
18327 */
18328 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18329 {
18330 int j;
18331
18332 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18333 pg_fatal("could not parse index statistic columns");
18334 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18335 pg_fatal("could not parse index statistic values");
18336 if (nstatcols != nstatvals)
18337 pg_fatal("mismatched number of columns and values for index statistics");
18338
18339 for (j = 0; j < nstatcols; j++)
18340 {
18341 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18342
18343 /*
18344 * Note that this is a column number, so no quotes should be
18345 * used.
18346 */
18347 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18348 indstatcolsarray[j]);
18349 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18350 indstatvalsarray[j]);
18351 }
18352 }
18353
18354 /* Indexes can depend on extensions */
18355 append_depends_on_extension(fout, q, &indxinfo->dobj,
18356 "pg_catalog.pg_class",
18357 "INDEX", qqindxname);
18358
18359 /* If the index defines identity, we need to record that. */
18360 if (indxinfo->indisreplident)
18361 {
18362 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18363 fmtQualifiedDumpable(tbinfo));
18364 /* index name is not qualified in this syntax */
18365 appendPQExpBuffer(q, " INDEX %s;\n",
18366 qindxname);
18367 }
18368
18369 /*
18370 * If this index is a member of a partitioned index, the backend will
18371 * not allow us to drop it separately, so don't try. It will go away
18372 * automatically when we drop either the index's table or the
18373 * partitioned index. (If, in a selective restore with --clean, we
18374 * drop neither of those, then this index will not be dropped either.
18375 * But that's fine, and even if you think it's not, the backend won't
18376 * let us do differently.)
18377 */
18378 if (indxinfo->parentidx == 0)
18379 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18380
18381 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18382 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18383 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18384 .namespace = tbinfo->dobj.namespace->dobj.name,
18385 .tablespace = indxinfo->tablespace,
18386 .owner = tbinfo->rolname,
18387 .description = "INDEX",
18388 .section = SECTION_POST_DATA,
18389 .createStmt = q->data,
18390 .dropStmt = delq->data));
18391
18392 free(indstatcolsarray);
18393 free(indstatvalsarray);
18394 }
18395
18396 /* Dump Index Comments */
18397 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18398 dumpComment(fout, "INDEX", qindxname,
18399 tbinfo->dobj.namespace->dobj.name,
18400 tbinfo->rolname,
18401 indxinfo->dobj.catId, 0,
18402 is_constraint ? indxinfo->indexconstraint :
18403 indxinfo->dobj.dumpId);
18404
18406 destroyPQExpBuffer(delq);
18407 free(qindxname);
18408 free(qqindxname);
18409}
18410
18411/*
18412 * dumpIndexAttach
18413 * write out to fout a partitioned-index attachment clause
18414 */
18415static void
18416dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18417{
18418 /* Do nothing if not dumping schema */
18419 if (!fout->dopt->dumpSchema)
18420 return;
18421
18423 {
18425
18426 appendPQExpBuffer(q, "ALTER INDEX %s ",
18427 fmtQualifiedDumpable(attachinfo->parentIdx));
18428 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18429 fmtQualifiedDumpable(attachinfo->partitionIdx));
18430
18431 /*
18432 * There is no need for a dropStmt since the drop is done implicitly
18433 * when we drop either the index's table or the partitioned index.
18434 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18435 * there's no way to do it anyway. (If you think to change this,
18436 * consider also what to do with --if-exists.)
18437 *
18438 * Although this object doesn't really have ownership as such, set the
18439 * owner field anyway to ensure that the command is run by the correct
18440 * role at restore time.
18441 */
18442 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18443 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18444 .namespace = attachinfo->dobj.namespace->dobj.name,
18445 .owner = attachinfo->parentIdx->indextable->rolname,
18446 .description = "INDEX ATTACH",
18447 .section = SECTION_POST_DATA,
18448 .createStmt = q->data));
18449
18451 }
18452}
18453
18454/*
18455 * dumpStatisticsExt
18456 * write out to fout an extended statistics object
18457 */
18458static void
18459dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18460{
18461 DumpOptions *dopt = fout->dopt;
18462 PQExpBuffer q;
18463 PQExpBuffer delq;
18464 PQExpBuffer query;
18465 char *qstatsextname;
18466 PGresult *res;
18467 char *stxdef;
18468
18469 /* Do nothing if not dumping schema */
18470 if (!dopt->dumpSchema)
18471 return;
18472
18473 q = createPQExpBuffer();
18474 delq = createPQExpBuffer();
18475 query = createPQExpBuffer();
18476
18477 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18478
18479 appendPQExpBuffer(query, "SELECT "
18480 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18481 statsextinfo->dobj.catId.oid);
18482
18483 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18484
18485 stxdef = PQgetvalue(res, 0, 0);
18486
18487 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18488 appendPQExpBuffer(q, "%s;\n", stxdef);
18489
18490 /*
18491 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18492 * for this statistics object is not the default value.
18493 */
18494 if (statsextinfo->stattarget >= 0)
18495 {
18496 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18497 fmtQualifiedDumpable(statsextinfo));
18498 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18499 statsextinfo->stattarget);
18500 }
18501
18502 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18503 fmtQualifiedDumpable(statsextinfo));
18504
18505 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18506 ArchiveEntry(fout, statsextinfo->dobj.catId,
18507 statsextinfo->dobj.dumpId,
18508 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18509 .namespace = statsextinfo->dobj.namespace->dobj.name,
18510 .owner = statsextinfo->rolname,
18511 .description = "STATISTICS",
18512 .section = SECTION_POST_DATA,
18513 .createStmt = q->data,
18514 .dropStmt = delq->data));
18515
18516 /* Dump Statistics Comments */
18517 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18518 dumpComment(fout, "STATISTICS", qstatsextname,
18519 statsextinfo->dobj.namespace->dobj.name,
18520 statsextinfo->rolname,
18521 statsextinfo->dobj.catId, 0,
18522 statsextinfo->dobj.dumpId);
18523
18524 PQclear(res);
18526 destroyPQExpBuffer(delq);
18527 destroyPQExpBuffer(query);
18528 free(qstatsextname);
18529}
18530
18531/*
18532 * dumpConstraint
18533 * write out to fout a user-defined constraint
18534 */
18535static void
18537{
18538 DumpOptions *dopt = fout->dopt;
18539 TableInfo *tbinfo = coninfo->contable;
18540 PQExpBuffer q;
18541 PQExpBuffer delq;
18542 char *tag = NULL;
18543 char *foreign;
18544
18545 /* Do nothing if not dumping schema */
18546 if (!dopt->dumpSchema)
18547 return;
18548
18549 q = createPQExpBuffer();
18550 delq = createPQExpBuffer();
18551
18552 foreign = tbinfo &&
18553 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18554
18555 if (coninfo->contype == 'p' ||
18556 coninfo->contype == 'u' ||
18557 coninfo->contype == 'x')
18558 {
18559 /* Index-related constraint */
18560 IndxInfo *indxinfo;
18561 int k;
18562
18563 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18564
18565 if (indxinfo == NULL)
18566 pg_fatal("missing index for constraint \"%s\"",
18567 coninfo->dobj.name);
18568
18569 if (dopt->binary_upgrade)
18571 indxinfo->dobj.catId.oid);
18572
18573 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18574 fmtQualifiedDumpable(tbinfo));
18575 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18576 fmtId(coninfo->dobj.name));
18577
18578 if (coninfo->condef)
18579 {
18580 /* pg_get_constraintdef should have provided everything */
18581 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18582 }
18583 else
18584 {
18586 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18587
18588 /*
18589 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18590 * indexes. Being able to create this was fixed, but we need to
18591 * make the index distinct in order to be able to restore the
18592 * dump.
18593 */
18594 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18595 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18596 appendPQExpBufferStr(q, " (");
18597 for (k = 0; k < indxinfo->indnkeyattrs; k++)
18598 {
18599 int indkey = (int) indxinfo->indkeys[k];
18600 const char *attname;
18601
18602 if (indkey == InvalidAttrNumber)
18603 break;
18604 attname = getAttrName(indkey, tbinfo);
18605
18606 appendPQExpBuffer(q, "%s%s",
18607 (k == 0) ? "" : ", ",
18608 fmtId(attname));
18609 }
18610 if (coninfo->conperiod)
18611 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18612
18613 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18614 appendPQExpBufferStr(q, ") INCLUDE (");
18615
18616 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18617 {
18618 int indkey = (int) indxinfo->indkeys[k];
18619 const char *attname;
18620
18621 if (indkey == InvalidAttrNumber)
18622 break;
18623 attname = getAttrName(indkey, tbinfo);
18624
18625 appendPQExpBuffer(q, "%s%s",
18626 (k == indxinfo->indnkeyattrs) ? "" : ", ",
18627 fmtId(attname));
18628 }
18629
18630 appendPQExpBufferChar(q, ')');
18631
18632 if (nonemptyReloptions(indxinfo->indreloptions))
18633 {
18634 appendPQExpBufferStr(q, " WITH (");
18635 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18636 appendPQExpBufferChar(q, ')');
18637 }
18638
18639 if (coninfo->condeferrable)
18640 {
18641 appendPQExpBufferStr(q, " DEFERRABLE");
18642 if (coninfo->condeferred)
18643 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18644 }
18645
18646 appendPQExpBufferStr(q, ";\n");
18647 }
18648
18649 /*
18650 * Append ALTER TABLE commands as needed to set properties that we
18651 * only have ALTER TABLE syntax for. Keep this in sync with the
18652 * similar code in dumpIndex!
18653 */
18654
18655 /* If the index is clustered, we need to record that. */
18656 if (indxinfo->indisclustered)
18657 {
18658 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18659 fmtQualifiedDumpable(tbinfo));
18660 /* index name is not qualified in this syntax */
18661 appendPQExpBuffer(q, " ON %s;\n",
18662 fmtId(indxinfo->dobj.name));
18663 }
18664
18665 /* If the index defines identity, we need to record that. */
18666 if (indxinfo->indisreplident)
18667 {
18668 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18669 fmtQualifiedDumpable(tbinfo));
18670 /* index name is not qualified in this syntax */
18671 appendPQExpBuffer(q, " INDEX %s;\n",
18672 fmtId(indxinfo->dobj.name));
18673 }
18674
18675 /* Indexes can depend on extensions */
18676 append_depends_on_extension(fout, q, &indxinfo->dobj,
18677 "pg_catalog.pg_class", "INDEX",
18678 fmtQualifiedDumpable(indxinfo));
18679
18680 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18681 fmtQualifiedDumpable(tbinfo));
18682 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18683 fmtId(coninfo->dobj.name));
18684
18685 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18686
18687 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18688 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18689 ARCHIVE_OPTS(.tag = tag,
18690 .namespace = tbinfo->dobj.namespace->dobj.name,
18691 .tablespace = indxinfo->tablespace,
18692 .owner = tbinfo->rolname,
18693 .description = "CONSTRAINT",
18694 .section = SECTION_POST_DATA,
18695 .createStmt = q->data,
18696 .dropStmt = delq->data));
18697 }
18698 else if (coninfo->contype == 'f')
18699 {
18700 char *only;
18701
18702 /*
18703 * Foreign keys on partitioned tables are always declared as
18704 * inheriting to partitions; for all other cases, emit them as
18705 * applying ONLY directly to the named table, because that's how they
18706 * work for regular inherited tables.
18707 */
18708 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18709
18710 /*
18711 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18712 * current table data is not processed
18713 */
18714 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18715 only, fmtQualifiedDumpable(tbinfo));
18716 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18717 fmtId(coninfo->dobj.name),
18718 coninfo->condef);
18719
18720 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18721 only, fmtQualifiedDumpable(tbinfo));
18722 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18723 fmtId(coninfo->dobj.name));
18724
18725 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18726
18727 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18728 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18729 ARCHIVE_OPTS(.tag = tag,
18730 .namespace = tbinfo->dobj.namespace->dobj.name,
18731 .owner = tbinfo->rolname,
18732 .description = "FK CONSTRAINT",
18733 .section = SECTION_POST_DATA,
18734 .createStmt = q->data,
18735 .dropStmt = delq->data));
18736 }
18737 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18738 {
18739 /* CHECK or invalid not-null constraint on a table */
18740
18741 /* Ignore if not to be dumped separately, or if it was inherited */
18742 if (coninfo->separate && coninfo->conislocal)
18743 {
18744 const char *keyword;
18745
18746 if (coninfo->contype == 'c')
18747 keyword = "CHECK CONSTRAINT";
18748 else
18749 keyword = "CONSTRAINT";
18750
18751 /* not ONLY since we want it to propagate to children */
18752 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18753 fmtQualifiedDumpable(tbinfo));
18754 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18755 fmtId(coninfo->dobj.name),
18756 coninfo->condef);
18757
18758 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18759 fmtQualifiedDumpable(tbinfo));
18760 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18761 fmtId(coninfo->dobj.name));
18762
18763 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18764
18765 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18766 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18767 ARCHIVE_OPTS(.tag = tag,
18768 .namespace = tbinfo->dobj.namespace->dobj.name,
18769 .owner = tbinfo->rolname,
18770 .description = keyword,
18771 .section = SECTION_POST_DATA,
18772 .createStmt = q->data,
18773 .dropStmt = delq->data));
18774 }
18775 }
18776 else if (tbinfo == NULL)
18777 {
18778 /* CHECK, NOT NULL constraint on a domain */
18779 TypeInfo *tyinfo = coninfo->condomain;
18780
18781 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18782
18783 /* Ignore if not to be dumped separately */
18784 if (coninfo->separate)
18785 {
18786 const char *keyword;
18787
18788 if (coninfo->contype == 'c')
18789 keyword = "CHECK CONSTRAINT";
18790 else
18791 keyword = "CONSTRAINT";
18792
18793 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18794 fmtQualifiedDumpable(tyinfo));
18795 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18796 fmtId(coninfo->dobj.name),
18797 coninfo->condef);
18798
18799 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18800 fmtQualifiedDumpable(tyinfo));
18801 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18802 fmtId(coninfo->dobj.name));
18803
18804 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18805
18806 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18807 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18808 ARCHIVE_OPTS(.tag = tag,
18809 .namespace = tyinfo->dobj.namespace->dobj.name,
18810 .owner = tyinfo->rolname,
18811 .description = keyword,
18812 .section = SECTION_POST_DATA,
18813 .createStmt = q->data,
18814 .dropStmt = delq->data));
18815
18816 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18817 {
18818 PQExpBuffer conprefix = createPQExpBuffer();
18819 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18820
18821 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18822 fmtId(coninfo->dobj.name));
18823
18824 dumpComment(fout, conprefix->data, qtypname,
18825 tyinfo->dobj.namespace->dobj.name,
18826 tyinfo->rolname,
18827 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18828 destroyPQExpBuffer(conprefix);
18829 free(qtypname);
18830 }
18831 }
18832 }
18833 else
18834 {
18835 pg_fatal("unrecognized constraint type: %c",
18836 coninfo->contype);
18837 }
18838
18839 /* Dump Constraint Comments --- only works for table constraints */
18840 if (tbinfo && coninfo->separate &&
18841 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18842 dumpTableConstraintComment(fout, coninfo);
18843
18844 free(tag);
18846 destroyPQExpBuffer(delq);
18847}
18848
18849/*
18850 * dumpTableConstraintComment --- dump a constraint's comment if any
18851 *
18852 * This is split out because we need the function in two different places
18853 * depending on whether the constraint is dumped as part of CREATE TABLE
18854 * or as a separate ALTER command.
18855 */
18856static void
18858{
18859 TableInfo *tbinfo = coninfo->contable;
18860 PQExpBuffer conprefix = createPQExpBuffer();
18861 char *qtabname;
18862
18863 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18864
18865 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18866 fmtId(coninfo->dobj.name));
18867
18868 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18869 dumpComment(fout, conprefix->data, qtabname,
18870 tbinfo->dobj.namespace->dobj.name,
18871 tbinfo->rolname,
18872 coninfo->dobj.catId, 0,
18873 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18874
18875 destroyPQExpBuffer(conprefix);
18876 free(qtabname);
18877}
18878
18879static inline SeqType
18881{
18882 for (int i = 0; i < lengthof(SeqTypeNames); i++)
18883 {
18884 if (strcmp(SeqTypeNames[i], name) == 0)
18885 return (SeqType) i;
18886 }
18887
18888 pg_fatal("unrecognized sequence type: %s", name);
18889 return (SeqType) 0; /* keep compiler quiet */
18890}
18891
18892/*
18893 * bsearch() comparator for SequenceItem
18894 */
18895static int
18896SequenceItemCmp(const void *p1, const void *p2)
18897{
18898 SequenceItem v1 = *((const SequenceItem *) p1);
18899 SequenceItem v2 = *((const SequenceItem *) p2);
18900
18901 return pg_cmp_u32(v1.oid, v2.oid);
18902}
18903
18904/*
18905 * collectSequences
18906 *
18907 * Construct a table of sequence information. This table is sorted by OID for
18908 * speed in lookup.
18909 */
18910static void
18912{
18913 PGresult *res;
18914 const char *query;
18915
18916 /*
18917 * Before Postgres 10, sequence metadata is in the sequence itself. With
18918 * some extra effort, we might be able to use the sorted table for those
18919 * versions, but for now it seems unlikely to be worth it.
18920 *
18921 * Since version 18, we can gather the sequence data in this query with
18922 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18923 */
18924 if (fout->remoteVersion < 100000)
18925 return;
18926 else if (fout->remoteVersion < 180000 ||
18927 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18928 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18929 "seqstart, seqincrement, "
18930 "seqmax, seqmin, "
18931 "seqcache, seqcycle, "
18932 "NULL, 'f' "
18933 "FROM pg_catalog.pg_sequence "
18934 "ORDER BY seqrelid";
18935 else
18936 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18937 "seqstart, seqincrement, "
18938 "seqmax, seqmin, "
18939 "seqcache, seqcycle, "
18940 "last_value, is_called "
18941 "FROM pg_catalog.pg_sequence, "
18942 "pg_get_sequence_data(seqrelid) "
18943 "ORDER BY seqrelid;";
18944
18945 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18946
18947 nsequences = PQntuples(res);
18949
18950 for (int i = 0; i < nsequences; i++)
18951 {
18952 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18954 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18955 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18956 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18957 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18958 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18959 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18960 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18961 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18962 }
18963
18964 PQclear(res);
18965}
18966
18967/*
18968 * dumpSequence
18969 * write the declaration (not data) of one user-defined sequence
18970 */
18971static void
18972dumpSequence(Archive *fout, const TableInfo *tbinfo)
18973{
18974 DumpOptions *dopt = fout->dopt;
18975 SequenceItem *seq;
18976 bool is_ascending;
18977 int64 default_minv,
18978 default_maxv;
18980 PQExpBuffer delqry = createPQExpBuffer();
18981 char *qseqname;
18982 TableInfo *owning_tab = NULL;
18983
18984 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18985
18986 /*
18987 * For versions >= 10, the sequence information is gathered in a sorted
18988 * table before any calls to dumpSequence(). See collectSequences() for
18989 * more information.
18990 */
18991 if (fout->remoteVersion >= 100000)
18992 {
18993 SequenceItem key = {0};
18994
18996
18997 key.oid = tbinfo->dobj.catId.oid;
18998 seq = bsearch(&key, sequences, nsequences,
18999 sizeof(SequenceItem), SequenceItemCmp);
19000 }
19001 else
19002 {
19003 PGresult *res;
19004
19005 /*
19006 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19007 *
19008 * Note: it might seem that 'bigint' potentially needs to be
19009 * schema-qualified, but actually that's a keyword.
19010 */
19011 appendPQExpBuffer(query,
19012 "SELECT 'bigint' AS sequence_type, "
19013 "start_value, increment_by, max_value, min_value, "
19014 "cache_value, is_cycled FROM %s",
19015 fmtQualifiedDumpable(tbinfo));
19016
19017 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19018
19019 if (PQntuples(res) != 1)
19020 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19021 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19022 PQntuples(res)),
19023 tbinfo->dobj.name, PQntuples(res));
19024
19025 seq = pg_malloc0(sizeof(SequenceItem));
19026 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19027 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19028 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19029 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19030 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19031 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19032 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19033
19034 PQclear(res);
19035 }
19036
19037 /* Calculate default limits for a sequence of this type */
19038 is_ascending = (seq->incby >= 0);
19039 if (seq->seqtype == SEQTYPE_SMALLINT)
19040 {
19041 default_minv = is_ascending ? 1 : PG_INT16_MIN;
19042 default_maxv = is_ascending ? PG_INT16_MAX : -1;
19043 }
19044 else if (seq->seqtype == SEQTYPE_INTEGER)
19045 {
19046 default_minv = is_ascending ? 1 : PG_INT32_MIN;
19047 default_maxv = is_ascending ? PG_INT32_MAX : -1;
19048 }
19049 else if (seq->seqtype == SEQTYPE_BIGINT)
19050 {
19051 default_minv = is_ascending ? 1 : PG_INT64_MIN;
19052 default_maxv = is_ascending ? PG_INT64_MAX : -1;
19053 }
19054 else
19055 {
19056 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19057 default_minv = default_maxv = 0; /* keep compiler quiet */
19058 }
19059
19060 /*
19061 * Identity sequences are not to be dropped separately.
19062 */
19063 if (!tbinfo->is_identity_sequence)
19064 {
19065 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19066 fmtQualifiedDumpable(tbinfo));
19067 }
19068
19069 resetPQExpBuffer(query);
19070
19071 if (dopt->binary_upgrade)
19072 {
19074 tbinfo->dobj.catId.oid);
19075
19076 /*
19077 * In older PG versions a sequence will have a pg_type entry, but v14
19078 * and up don't use that, so don't attempt to preserve the type OID.
19079 */
19080 }
19081
19082 if (tbinfo->is_identity_sequence)
19083 {
19084 owning_tab = findTableByOid(tbinfo->owning_tab);
19085
19086 appendPQExpBuffer(query,
19087 "ALTER TABLE %s ",
19088 fmtQualifiedDumpable(owning_tab));
19089 appendPQExpBuffer(query,
19090 "ALTER COLUMN %s ADD GENERATED ",
19091 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19092 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19093 appendPQExpBufferStr(query, "ALWAYS");
19094 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19095 appendPQExpBufferStr(query, "BY DEFAULT");
19096 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19097 fmtQualifiedDumpable(tbinfo));
19098
19099 /*
19100 * Emit persistence option only if it's different from the owning
19101 * table's. This avoids using this new syntax unnecessarily.
19102 */
19103 if (tbinfo->relpersistence != owning_tab->relpersistence)
19104 appendPQExpBuffer(query, " %s\n",
19105 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19106 "UNLOGGED" : "LOGGED");
19107 }
19108 else
19109 {
19110 appendPQExpBuffer(query,
19111 "CREATE %sSEQUENCE %s\n",
19112 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19113 "UNLOGGED " : "",
19114 fmtQualifiedDumpable(tbinfo));
19115
19116 if (seq->seqtype != SEQTYPE_BIGINT)
19117 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19118 }
19119
19120 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19121
19122 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19123
19124 if (seq->minv != default_minv)
19125 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19126 else
19127 appendPQExpBufferStr(query, " NO MINVALUE\n");
19128
19129 if (seq->maxv != default_maxv)
19130 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19131 else
19132 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19133
19134 appendPQExpBuffer(query,
19135 " CACHE " INT64_FORMAT "%s",
19136 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19137
19138 if (tbinfo->is_identity_sequence)
19139 appendPQExpBufferStr(query, "\n);\n");
19140 else
19141 appendPQExpBufferStr(query, ";\n");
19142
19143 /* binary_upgrade: no need to clear TOAST table oid */
19144
19145 if (dopt->binary_upgrade)
19146 binary_upgrade_extension_member(query, &tbinfo->dobj,
19147 "SEQUENCE", qseqname,
19148 tbinfo->dobj.namespace->dobj.name);
19149
19150 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19151 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19152 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19153 .namespace = tbinfo->dobj.namespace->dobj.name,
19154 .owner = tbinfo->rolname,
19155 .description = "SEQUENCE",
19156 .section = SECTION_PRE_DATA,
19157 .createStmt = query->data,
19158 .dropStmt = delqry->data));
19159
19160 /*
19161 * If the sequence is owned by a table column, emit the ALTER for it as a
19162 * separate TOC entry immediately following the sequence's own entry. It's
19163 * OK to do this rather than using full sorting logic, because the
19164 * dependency that tells us it's owned will have forced the table to be
19165 * created first. We can't just include the ALTER in the TOC entry
19166 * because it will fail if we haven't reassigned the sequence owner to
19167 * match the table's owner.
19168 *
19169 * We need not schema-qualify the table reference because both sequence
19170 * and table must be in the same schema.
19171 */
19172 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19173 {
19174 owning_tab = findTableByOid(tbinfo->owning_tab);
19175
19176 if (owning_tab == NULL)
19177 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19178 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19179
19180 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19181 {
19182 resetPQExpBuffer(query);
19183 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19184 fmtQualifiedDumpable(tbinfo));
19185 appendPQExpBuffer(query, " OWNED BY %s",
19186 fmtQualifiedDumpable(owning_tab));
19187 appendPQExpBuffer(query, ".%s;\n",
19188 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19189
19190 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19192 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19193 .namespace = tbinfo->dobj.namespace->dobj.name,
19194 .owner = tbinfo->rolname,
19195 .description = "SEQUENCE OWNED BY",
19196 .section = SECTION_PRE_DATA,
19197 .createStmt = query->data,
19198 .deps = &(tbinfo->dobj.dumpId),
19199 .nDeps = 1));
19200 }
19201 }
19202
19203 /* Dump Sequence Comments and Security Labels */
19204 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19205 dumpComment(fout, "SEQUENCE", qseqname,
19206 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19207 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19208
19209 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19210 dumpSecLabel(fout, "SEQUENCE", qseqname,
19211 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19212 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19213
19214 if (fout->remoteVersion < 100000)
19215 pg_free(seq);
19216 destroyPQExpBuffer(query);
19217 destroyPQExpBuffer(delqry);
19218 free(qseqname);
19219}
19220
19221/*
19222 * dumpSequenceData
19223 * write the data of one user-defined sequence
19224 */
19225static void
19227{
19228 TableInfo *tbinfo = tdinfo->tdtable;
19229 int64 last;
19230 bool called;
19232
19233 /*
19234 * For versions >= 18, the sequence information is gathered in the sorted
19235 * array before any calls to dumpSequenceData(). See collectSequences()
19236 * for more information.
19237 *
19238 * For older versions, we have to query the sequence relations
19239 * individually.
19240 */
19241 if (fout->remoteVersion < 180000)
19242 {
19243 PGresult *res;
19244
19245 appendPQExpBuffer(query,
19246 "SELECT last_value, is_called FROM %s",
19247 fmtQualifiedDumpable(tbinfo));
19248
19249 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19250
19251 if (PQntuples(res) != 1)
19252 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19253 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19254 PQntuples(res)),
19255 tbinfo->dobj.name, PQntuples(res));
19256
19257 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19258 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19259
19260 PQclear(res);
19261 }
19262 else
19263 {
19264 SequenceItem key = {0};
19265 SequenceItem *entry;
19266
19268 Assert(tbinfo->dobj.catId.oid);
19269
19270 key.oid = tbinfo->dobj.catId.oid;
19271 entry = bsearch(&key, sequences, nsequences,
19272 sizeof(SequenceItem), SequenceItemCmp);
19273
19274 last = entry->last_value;
19275 called = entry->is_called;
19276 }
19277
19278 resetPQExpBuffer(query);
19279 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19280 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19281 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19282 last, (called ? "true" : "false"));
19283
19284 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19286 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19287 .namespace = tbinfo->dobj.namespace->dobj.name,
19288 .owner = tbinfo->rolname,
19289 .description = "SEQUENCE SET",
19290 .section = SECTION_DATA,
19291 .createStmt = query->data,
19292 .deps = &(tbinfo->dobj.dumpId),
19293 .nDeps = 1));
19294
19295 destroyPQExpBuffer(query);
19296}
19297
19298/*
19299 * dumpTrigger
19300 * write the declaration of one user-defined table trigger
19301 */
19302static void
19303dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19304{
19305 DumpOptions *dopt = fout->dopt;
19306 TableInfo *tbinfo = tginfo->tgtable;
19307 PQExpBuffer query;
19308 PQExpBuffer delqry;
19309 PQExpBuffer trigprefix;
19310 PQExpBuffer trigidentity;
19311 char *qtabname;
19312 char *tag;
19313
19314 /* Do nothing if not dumping schema */
19315 if (!dopt->dumpSchema)
19316 return;
19317
19318 query = createPQExpBuffer();
19319 delqry = createPQExpBuffer();
19320 trigprefix = createPQExpBuffer();
19321 trigidentity = createPQExpBuffer();
19322
19323 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19324
19325 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19326 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19327
19328 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19329 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19330
19331 /* Triggers can depend on extensions */
19332 append_depends_on_extension(fout, query, &tginfo->dobj,
19333 "pg_catalog.pg_trigger", "TRIGGER",
19334 trigidentity->data);
19335
19336 if (tginfo->tgispartition)
19337 {
19338 Assert(tbinfo->ispartition);
19339
19340 /*
19341 * Partition triggers only appear here because their 'tgenabled' flag
19342 * differs from its parent's. The trigger is created already, so
19343 * remove the CREATE and replace it with an ALTER. (Clear out the
19344 * DROP query too, so that pg_dump --create does not cause errors.)
19345 */
19346 resetPQExpBuffer(query);
19347 resetPQExpBuffer(delqry);
19348 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19349 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19350 fmtQualifiedDumpable(tbinfo));
19351 switch (tginfo->tgenabled)
19352 {
19353 case 'f':
19354 case 'D':
19355 appendPQExpBufferStr(query, "DISABLE");
19356 break;
19357 case 't':
19358 case 'O':
19359 appendPQExpBufferStr(query, "ENABLE");
19360 break;
19361 case 'R':
19362 appendPQExpBufferStr(query, "ENABLE REPLICA");
19363 break;
19364 case 'A':
19365 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19366 break;
19367 }
19368 appendPQExpBuffer(query, " TRIGGER %s;\n",
19369 fmtId(tginfo->dobj.name));
19370 }
19371 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19372 {
19373 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19374 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19375 fmtQualifiedDumpable(tbinfo));
19376 switch (tginfo->tgenabled)
19377 {
19378 case 'D':
19379 case 'f':
19380 appendPQExpBufferStr(query, "DISABLE");
19381 break;
19382 case 'A':
19383 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19384 break;
19385 case 'R':
19386 appendPQExpBufferStr(query, "ENABLE REPLICA");
19387 break;
19388 default:
19389 appendPQExpBufferStr(query, "ENABLE");
19390 break;
19391 }
19392 appendPQExpBuffer(query, " TRIGGER %s;\n",
19393 fmtId(tginfo->dobj.name));
19394 }
19395
19396 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19397 fmtId(tginfo->dobj.name));
19398
19399 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19400
19401 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19402 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19403 ARCHIVE_OPTS(.tag = tag,
19404 .namespace = tbinfo->dobj.namespace->dobj.name,
19405 .owner = tbinfo->rolname,
19406 .description = "TRIGGER",
19407 .section = SECTION_POST_DATA,
19408 .createStmt = query->data,
19409 .dropStmt = delqry->data));
19410
19411 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19412 dumpComment(fout, trigprefix->data, qtabname,
19413 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19414 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19415
19416 free(tag);
19417 destroyPQExpBuffer(query);
19418 destroyPQExpBuffer(delqry);
19419 destroyPQExpBuffer(trigprefix);
19420 destroyPQExpBuffer(trigidentity);
19421 free(qtabname);
19422}
19423
19424/*
19425 * dumpEventTrigger
19426 * write the declaration of one user-defined event trigger
19427 */
19428static void
19430{
19431 DumpOptions *dopt = fout->dopt;
19432 PQExpBuffer query;
19433 PQExpBuffer delqry;
19434 char *qevtname;
19435
19436 /* Do nothing if not dumping schema */
19437 if (!dopt->dumpSchema)
19438 return;
19439
19440 query = createPQExpBuffer();
19441 delqry = createPQExpBuffer();
19442
19443 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19444
19445 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19446 appendPQExpBufferStr(query, qevtname);
19447 appendPQExpBufferStr(query, " ON ");
19448 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19449
19450 if (strcmp("", evtinfo->evttags) != 0)
19451 {
19452 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19453 appendPQExpBufferStr(query, evtinfo->evttags);
19454 appendPQExpBufferChar(query, ')');
19455 }
19456
19457 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19458 appendPQExpBufferStr(query, evtinfo->evtfname);
19459 appendPQExpBufferStr(query, "();\n");
19460
19461 if (evtinfo->evtenabled != 'O')
19462 {
19463 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19464 qevtname);
19465 switch (evtinfo->evtenabled)
19466 {
19467 case 'D':
19468 appendPQExpBufferStr(query, "DISABLE");
19469 break;
19470 case 'A':
19471 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19472 break;
19473 case 'R':
19474 appendPQExpBufferStr(query, "ENABLE REPLICA");
19475 break;
19476 default:
19477 appendPQExpBufferStr(query, "ENABLE");
19478 break;
19479 }
19480 appendPQExpBufferStr(query, ";\n");
19481 }
19482
19483 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19484 qevtname);
19485
19486 if (dopt->binary_upgrade)
19487 binary_upgrade_extension_member(query, &evtinfo->dobj,
19488 "EVENT TRIGGER", qevtname, NULL);
19489
19490 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19491 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19492 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19493 .owner = evtinfo->evtowner,
19494 .description = "EVENT TRIGGER",
19495 .section = SECTION_POST_DATA,
19496 .createStmt = query->data,
19497 .dropStmt = delqry->data));
19498
19499 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19500 dumpComment(fout, "EVENT TRIGGER", qevtname,
19501 NULL, evtinfo->evtowner,
19502 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19503
19504 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19505 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19506 NULL, evtinfo->evtowner,
19507 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19508
19509 destroyPQExpBuffer(query);
19510 destroyPQExpBuffer(delqry);
19511 free(qevtname);
19512}
19513
19514/*
19515 * dumpRule
19516 * Dump a rule
19517 */
19518static void
19519dumpRule(Archive *fout, const RuleInfo *rinfo)
19520{
19521 DumpOptions *dopt = fout->dopt;
19522 TableInfo *tbinfo = rinfo->ruletable;
19523 bool is_view;
19524 PQExpBuffer query;
19525 PQExpBuffer cmd;
19526 PQExpBuffer delcmd;
19527 PQExpBuffer ruleprefix;
19528 char *qtabname;
19529 PGresult *res;
19530 char *tag;
19531
19532 /* Do nothing if not dumping schema */
19533 if (!dopt->dumpSchema)
19534 return;
19535
19536 /*
19537 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19538 * we do not want to dump it as a separate object.
19539 */
19540 if (!rinfo->separate)
19541 return;
19542
19543 /*
19544 * If it's an ON SELECT rule, we want to print it as a view definition,
19545 * instead of a rule.
19546 */
19547 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19548
19549 query = createPQExpBuffer();
19550 cmd = createPQExpBuffer();
19551 delcmd = createPQExpBuffer();
19552 ruleprefix = createPQExpBuffer();
19553
19554 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19555
19556 if (is_view)
19557 {
19558 PQExpBuffer result;
19559
19560 /*
19561 * We need OR REPLACE here because we'll be replacing a dummy view.
19562 * Otherwise this should look largely like the regular view dump code.
19563 */
19564 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19565 fmtQualifiedDumpable(tbinfo));
19566 if (nonemptyReloptions(tbinfo->reloptions))
19567 {
19568 appendPQExpBufferStr(cmd, " WITH (");
19569 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19570 appendPQExpBufferChar(cmd, ')');
19571 }
19572 result = createViewAsClause(fout, tbinfo);
19573 appendPQExpBuffer(cmd, " AS\n%s", result->data);
19574 destroyPQExpBuffer(result);
19575 if (tbinfo->checkoption != NULL)
19576 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19577 tbinfo->checkoption);
19578 appendPQExpBufferStr(cmd, ";\n");
19579 }
19580 else
19581 {
19582 /* In the rule case, just print pg_get_ruledef's result verbatim */
19583 appendPQExpBuffer(query,
19584 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19585 rinfo->dobj.catId.oid);
19586
19587 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19588
19589 if (PQntuples(res) != 1)
19590 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19591 rinfo->dobj.name, tbinfo->dobj.name);
19592
19593 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19594
19595 PQclear(res);
19596 }
19597
19598 /*
19599 * Add the command to alter the rules replication firing semantics if it
19600 * differs from the default.
19601 */
19602 if (rinfo->ev_enabled != 'O')
19603 {
19604 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19605 switch (rinfo->ev_enabled)
19606 {
19607 case 'A':
19608 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19609 fmtId(rinfo->dobj.name));
19610 break;
19611 case 'R':
19612 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19613 fmtId(rinfo->dobj.name));
19614 break;
19615 case 'D':
19616 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19617 fmtId(rinfo->dobj.name));
19618 break;
19619 }
19620 }
19621
19622 if (is_view)
19623 {
19624 /*
19625 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19626 * REPLACE VIEW to replace the rule with something with minimal
19627 * dependencies.
19628 */
19629 PQExpBuffer result;
19630
19631 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19632 fmtQualifiedDumpable(tbinfo));
19633 result = createDummyViewAsClause(fout, tbinfo);
19634 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19635 destroyPQExpBuffer(result);
19636 }
19637 else
19638 {
19639 appendPQExpBuffer(delcmd, "DROP RULE %s ",
19640 fmtId(rinfo->dobj.name));
19641 appendPQExpBuffer(delcmd, "ON %s;\n",
19642 fmtQualifiedDumpable(tbinfo));
19643 }
19644
19645 appendPQExpBuffer(ruleprefix, "RULE %s ON",
19646 fmtId(rinfo->dobj.name));
19647
19648 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19649
19650 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19651 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19652 ARCHIVE_OPTS(.tag = tag,
19653 .namespace = tbinfo->dobj.namespace->dobj.name,
19654 .owner = tbinfo->rolname,
19655 .description = "RULE",
19656 .section = SECTION_POST_DATA,
19657 .createStmt = cmd->data,
19658 .dropStmt = delcmd->data));
19659
19660 /* Dump rule comments */
19661 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19662 dumpComment(fout, ruleprefix->data, qtabname,
19663 tbinfo->dobj.namespace->dobj.name,
19664 tbinfo->rolname,
19665 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19666
19667 free(tag);
19668 destroyPQExpBuffer(query);
19669 destroyPQExpBuffer(cmd);
19670 destroyPQExpBuffer(delcmd);
19671 destroyPQExpBuffer(ruleprefix);
19672 free(qtabname);
19673}
19674
19675/*
19676 * getExtensionMembership --- obtain extension membership data
19677 *
19678 * We need to identify objects that are extension members as soon as they're
19679 * loaded, so that we can correctly determine whether they need to be dumped.
19680 * Generally speaking, extension member objects will get marked as *not* to
19681 * be dumped, as they will be recreated by the single CREATE EXTENSION
19682 * command. However, in binary upgrade mode we still need to dump the members
19683 * individually.
19684 */
19685void
19687 int numExtensions)
19688{
19689 PQExpBuffer query;
19690 PGresult *res;
19691 int ntups,
19692 i;
19693 int i_classid,
19694 i_objid,
19695 i_refobjid;
19696 ExtensionInfo *ext;
19697
19698 /* Nothing to do if no extensions */
19699 if (numExtensions == 0)
19700 return;
19701
19702 query = createPQExpBuffer();
19703
19704 /* refclassid constraint is redundant but may speed the search */
19705 appendPQExpBufferStr(query, "SELECT "
19706 "classid, objid, refobjid "
19707 "FROM pg_depend "
19708 "WHERE refclassid = 'pg_extension'::regclass "
19709 "AND deptype = 'e' "
19710 "ORDER BY 3");
19711
19712 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19713
19714 ntups = PQntuples(res);
19715
19716 i_classid = PQfnumber(res, "classid");
19717 i_objid = PQfnumber(res, "objid");
19718 i_refobjid = PQfnumber(res, "refobjid");
19719
19720 /*
19721 * Since we ordered the SELECT by referenced ID, we can expect that
19722 * multiple entries for the same extension will appear together; this
19723 * saves on searches.
19724 */
19725 ext = NULL;
19726
19727 for (i = 0; i < ntups; i++)
19728 {
19729 CatalogId objId;
19730 Oid extId;
19731
19732 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19733 objId.oid = atooid(PQgetvalue(res, i, i_objid));
19734 extId = atooid(PQgetvalue(res, i, i_refobjid));
19735
19736 if (ext == NULL ||
19737 ext->dobj.catId.oid != extId)
19738 ext = findExtensionByOid(extId);
19739
19740 if (ext == NULL)
19741 {
19742 /* shouldn't happen */
19743 pg_log_warning("could not find referenced extension %u", extId);
19744 continue;
19745 }
19746
19747 recordExtensionMembership(objId, ext);
19748 }
19749
19750 PQclear(res);
19751
19752 destroyPQExpBuffer(query);
19753}
19754
19755/*
19756 * processExtensionTables --- deal with extension configuration tables
19757 *
19758 * There are two parts to this process:
19759 *
19760 * 1. Identify and create dump records for extension configuration tables.
19761 *
19762 * Extensions can mark tables as "configuration", which means that the user
19763 * is able and expected to modify those tables after the extension has been
19764 * loaded. For these tables, we dump out only the data- the structure is
19765 * expected to be handled at CREATE EXTENSION time, including any indexes or
19766 * foreign keys, which brings us to-
19767 *
19768 * 2. Record FK dependencies between configuration tables.
19769 *
19770 * Due to the FKs being created at CREATE EXTENSION time and therefore before
19771 * the data is loaded, we have to work out what the best order for reloading
19772 * the data is, to avoid FK violations when the tables are restored. This is
19773 * not perfect- we can't handle circular dependencies and if any exist they
19774 * will cause an invalid dump to be produced (though at least all of the data
19775 * is included for a user to manually restore). This is currently documented
19776 * but perhaps we can provide a better solution in the future.
19777 */
19778void
19780 int numExtensions)
19781{
19782 DumpOptions *dopt = fout->dopt;
19783 PQExpBuffer query;
19784 PGresult *res;
19785 int ntups,
19786 i;
19787 int i_conrelid,
19788 i_confrelid;
19789
19790 /* Nothing to do if no extensions */
19791 if (numExtensions == 0)
19792 return;
19793
19794 /*
19795 * Identify extension configuration tables and create TableDataInfo
19796 * objects for them, ensuring their data will be dumped even though the
19797 * tables themselves won't be.
19798 *
19799 * Note that we create TableDataInfo objects even in schema-only mode, ie,
19800 * user data in a configuration table is treated like schema data. This
19801 * seems appropriate since system data in a config table would get
19802 * reloaded by CREATE EXTENSION. If the extension is not listed in the
19803 * list of extensions to be included, none of its data is dumped.
19804 */
19805 for (i = 0; i < numExtensions; i++)
19806 {
19807 ExtensionInfo *curext = &(extinfo[i]);
19808 char *extconfig = curext->extconfig;
19809 char *extcondition = curext->extcondition;
19810 char **extconfigarray = NULL;
19811 char **extconditionarray = NULL;
19812 int nconfigitems = 0;
19813 int nconditionitems = 0;
19814
19815 /*
19816 * Check if this extension is listed as to include in the dump. If
19817 * not, any table data associated with it is discarded.
19818 */
19819 if (extension_include_oids.head != NULL &&
19821 curext->dobj.catId.oid))
19822 continue;
19823
19824 /*
19825 * Check if this extension is listed as to exclude in the dump. If
19826 * yes, any table data associated with it is discarded.
19827 */
19828 if (extension_exclude_oids.head != NULL &&
19830 curext->dobj.catId.oid))
19831 continue;
19832
19833 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19834 {
19835 int j;
19836
19837 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19838 pg_fatal("could not parse %s array", "extconfig");
19839 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19840 pg_fatal("could not parse %s array", "extcondition");
19841 if (nconfigitems != nconditionitems)
19842 pg_fatal("mismatched number of configurations and conditions for extension");
19843
19844 for (j = 0; j < nconfigitems; j++)
19845 {
19846 TableInfo *configtbl;
19847 Oid configtbloid = atooid(extconfigarray[j]);
19848 bool dumpobj =
19850
19851 configtbl = findTableByOid(configtbloid);
19852 if (configtbl == NULL)
19853 continue;
19854
19855 /*
19856 * Tables of not-to-be-dumped extensions shouldn't be dumped
19857 * unless the table or its schema is explicitly included
19858 */
19859 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19860 {
19861 /* check table explicitly requested */
19862 if (table_include_oids.head != NULL &&
19864 configtbloid))
19865 dumpobj = true;
19866
19867 /* check table's schema explicitly requested */
19868 if (configtbl->dobj.namespace->dobj.dump &
19870 dumpobj = true;
19871 }
19872
19873 /* check table excluded by an exclusion switch */
19874 if (table_exclude_oids.head != NULL &&
19876 configtbloid))
19877 dumpobj = false;
19878
19879 /* check schema excluded by an exclusion switch */
19881 configtbl->dobj.namespace->dobj.catId.oid))
19882 dumpobj = false;
19883
19884 if (dumpobj)
19885 {
19886 makeTableDataInfo(dopt, configtbl);
19887 if (configtbl->dataObj != NULL)
19888 {
19889 if (strlen(extconditionarray[j]) > 0)
19890 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19891 }
19892 }
19893 }
19894 }
19895 if (extconfigarray)
19896 free(extconfigarray);
19897 if (extconditionarray)
19898 free(extconditionarray);
19899 }
19900
19901 /*
19902 * Now that all the TableDataInfo objects have been created for all the
19903 * extensions, check their FK dependencies and register them to try and
19904 * dump the data out in an order that they can be restored in.
19905 *
19906 * Note that this is not a problem for user tables as their FKs are
19907 * recreated after the data has been loaded.
19908 */
19909
19910 query = createPQExpBuffer();
19911
19912 printfPQExpBuffer(query,
19913 "SELECT conrelid, confrelid "
19914 "FROM pg_constraint "
19915 "JOIN pg_depend ON (objid = confrelid) "
19916 "WHERE contype = 'f' "
19917 "AND refclassid = 'pg_extension'::regclass "
19918 "AND classid = 'pg_class'::regclass;");
19919
19920 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19921 ntups = PQntuples(res);
19922
19923 i_conrelid = PQfnumber(res, "conrelid");
19924 i_confrelid = PQfnumber(res, "confrelid");
19925
19926 /* Now get the dependencies and register them */
19927 for (i = 0; i < ntups; i++)
19928 {
19929 Oid conrelid,
19930 confrelid;
19931 TableInfo *reftable,
19932 *contable;
19933
19934 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19935 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19936 contable = findTableByOid(conrelid);
19937 reftable = findTableByOid(confrelid);
19938
19939 if (reftable == NULL ||
19940 reftable->dataObj == NULL ||
19941 contable == NULL ||
19942 contable->dataObj == NULL)
19943 continue;
19944
19945 /*
19946 * Make referencing TABLE_DATA object depend on the referenced table's
19947 * TABLE_DATA object.
19948 */
19949 addObjectDependency(&contable->dataObj->dobj,
19950 reftable->dataObj->dobj.dumpId);
19951 }
19952 PQclear(res);
19953 destroyPQExpBuffer(query);
19954}
19955
19956/*
19957 * getDependencies --- obtain available dependency data
19958 */
19959static void
19961{
19962 PQExpBuffer query;
19963 PGresult *res;
19964 int ntups,
19965 i;
19966 int i_classid,
19967 i_objid,
19968 i_refclassid,
19969 i_refobjid,
19970 i_deptype;
19971 DumpableObject *dobj,
19972 *refdobj;
19973
19974 pg_log_info("reading dependency data");
19975
19976 query = createPQExpBuffer();
19977
19978 /*
19979 * Messy query to collect the dependency data we need. Note that we
19980 * ignore the sub-object column, so that dependencies of or on a column
19981 * look the same as dependencies of or on a whole table.
19982 *
19983 * PIN dependencies aren't interesting, and EXTENSION dependencies were
19984 * already processed by getExtensionMembership.
19985 */
19986 appendPQExpBufferStr(query, "SELECT "
19987 "classid, objid, refclassid, refobjid, deptype "
19988 "FROM pg_depend "
19989 "WHERE deptype != 'p' AND deptype != 'e'\n");
19990
19991 /*
19992 * Since we don't treat pg_amop entries as separate DumpableObjects, we
19993 * have to translate their dependencies into dependencies of their parent
19994 * opfamily. Ignore internal dependencies though, as those will point to
19995 * their parent opclass, which we needn't consider here (and if we did,
19996 * it'd just result in circular dependencies). Also, "loose" opfamily
19997 * entries will have dependencies on their parent opfamily, which we
19998 * should drop since they'd likewise become useless self-dependencies.
19999 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20000 */
20001 appendPQExpBufferStr(query, "UNION ALL\n"
20002 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20003 "FROM pg_depend d, pg_amop o "
20004 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20005 "classid = 'pg_amop'::regclass AND objid = o.oid "
20006 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20007
20008 /* Likewise for pg_amproc entries */
20009 appendPQExpBufferStr(query, "UNION ALL\n"
20010 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20011 "FROM pg_depend d, pg_amproc p "
20012 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20013 "classid = 'pg_amproc'::regclass AND objid = p.oid "
20014 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20015
20016 /* Sort the output for efficiency below */
20017 appendPQExpBufferStr(query, "ORDER BY 1,2");
20018
20019 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20020
20021 ntups = PQntuples(res);
20022
20023 i_classid = PQfnumber(res, "classid");
20024 i_objid = PQfnumber(res, "objid");
20025 i_refclassid = PQfnumber(res, "refclassid");
20026 i_refobjid = PQfnumber(res, "refobjid");
20027 i_deptype = PQfnumber(res, "deptype");
20028
20029 /*
20030 * Since we ordered the SELECT by referencing ID, we can expect that
20031 * multiple entries for the same object will appear together; this saves
20032 * on searches.
20033 */
20034 dobj = NULL;
20035
20036 for (i = 0; i < ntups; i++)
20037 {
20038 CatalogId objId;
20039 CatalogId refobjId;
20040 char deptype;
20041
20042 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20043 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20044 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20045 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20046 deptype = *(PQgetvalue(res, i, i_deptype));
20047
20048 if (dobj == NULL ||
20049 dobj->catId.tableoid != objId.tableoid ||
20050 dobj->catId.oid != objId.oid)
20051 dobj = findObjectByCatalogId(objId);
20052
20053 /*
20054 * Failure to find objects mentioned in pg_depend is not unexpected,
20055 * since for example we don't collect info about TOAST tables.
20056 */
20057 if (dobj == NULL)
20058 {
20059#ifdef NOT_USED
20060 pg_log_warning("no referencing object %u %u",
20061 objId.tableoid, objId.oid);
20062#endif
20063 continue;
20064 }
20065
20066 refdobj = findObjectByCatalogId(refobjId);
20067
20068 if (refdobj == NULL)
20069 {
20070#ifdef NOT_USED
20071 pg_log_warning("no referenced object %u %u",
20072 refobjId.tableoid, refobjId.oid);
20073#endif
20074 continue;
20075 }
20076
20077 /*
20078 * For 'x' dependencies, mark the object for later; we still add the
20079 * normal dependency, for possible ordering purposes. Currently
20080 * pg_dump_sort.c knows to put extensions ahead of all object types
20081 * that could possibly depend on them, but this is safer.
20082 */
20083 if (deptype == 'x')
20084 dobj->depends_on_ext = true;
20085
20086 /*
20087 * Ordinarily, table rowtypes have implicit dependencies on their
20088 * tables. However, for a composite type the implicit dependency goes
20089 * the other way in pg_depend; which is the right thing for DROP but
20090 * it doesn't produce the dependency ordering we need. So in that one
20091 * case, we reverse the direction of the dependency.
20092 */
20093 if (deptype == 'i' &&
20094 dobj->objType == DO_TABLE &&
20095 refdobj->objType == DO_TYPE)
20096 addObjectDependency(refdobj, dobj->dumpId);
20097 else
20098 /* normal case */
20099 addObjectDependency(dobj, refdobj->dumpId);
20100 }
20101
20102 PQclear(res);
20103
20104 destroyPQExpBuffer(query);
20105}
20106
20107
20108/*
20109 * createBoundaryObjects - create dummy DumpableObjects to represent
20110 * dump section boundaries.
20111 */
20112static DumpableObject *
20114{
20115 DumpableObject *dobjs;
20116
20117 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20118
20119 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20120 dobjs[0].catId = nilCatalogId;
20121 AssignDumpId(dobjs + 0);
20122 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20123
20124 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20125 dobjs[1].catId = nilCatalogId;
20126 AssignDumpId(dobjs + 1);
20127 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20128
20129 return dobjs;
20130}
20131
20132/*
20133 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20134 * section boundaries.
20135 */
20136static void
20138 DumpableObject *boundaryObjs)
20139{
20140 DumpableObject *preDataBound = boundaryObjs + 0;
20141 DumpableObject *postDataBound = boundaryObjs + 1;
20142 int i;
20143
20144 for (i = 0; i < numObjs; i++)
20145 {
20146 DumpableObject *dobj = dobjs[i];
20147
20148 /*
20149 * The classification of object types here must match the SECTION_xxx
20150 * values assigned during subsequent ArchiveEntry calls!
20151 */
20152 switch (dobj->objType)
20153 {
20154 case DO_NAMESPACE:
20155 case DO_EXTENSION:
20156 case DO_TYPE:
20157 case DO_SHELL_TYPE:
20158 case DO_FUNC:
20159 case DO_AGG:
20160 case DO_OPERATOR:
20161 case DO_ACCESS_METHOD:
20162 case DO_OPCLASS:
20163 case DO_OPFAMILY:
20164 case DO_COLLATION:
20165 case DO_CONVERSION:
20166 case DO_TABLE:
20167 case DO_TABLE_ATTACH:
20168 case DO_ATTRDEF:
20169 case DO_PROCLANG:
20170 case DO_CAST:
20171 case DO_DUMMY_TYPE:
20172 case DO_TSPARSER:
20173 case DO_TSDICT:
20174 case DO_TSTEMPLATE:
20175 case DO_TSCONFIG:
20176 case DO_FDW:
20177 case DO_FOREIGN_SERVER:
20178 case DO_TRANSFORM:
20179 /* Pre-data objects: must come before the pre-data boundary */
20180 addObjectDependency(preDataBound, dobj->dumpId);
20181 break;
20182 case DO_TABLE_DATA:
20183 case DO_SEQUENCE_SET:
20184 case DO_LARGE_OBJECT:
20186 /* Data objects: must come between the boundaries */
20187 addObjectDependency(dobj, preDataBound->dumpId);
20188 addObjectDependency(postDataBound, dobj->dumpId);
20189 break;
20190 case DO_INDEX:
20191 case DO_INDEX_ATTACH:
20192 case DO_STATSEXT:
20193 case DO_REFRESH_MATVIEW:
20194 case DO_TRIGGER:
20195 case DO_EVENT_TRIGGER:
20196 case DO_DEFAULT_ACL:
20197 case DO_POLICY:
20198 case DO_PUBLICATION:
20199 case DO_PUBLICATION_REL:
20201 case DO_SUBSCRIPTION:
20203 /* Post-data objects: must come after the post-data boundary */
20204 addObjectDependency(dobj, postDataBound->dumpId);
20205 break;
20206 case DO_RULE:
20207 /* Rules are post-data, but only if dumped separately */
20208 if (((RuleInfo *) dobj)->separate)
20209 addObjectDependency(dobj, postDataBound->dumpId);
20210 break;
20211 case DO_CONSTRAINT:
20212 case DO_FK_CONSTRAINT:
20213 /* Constraints are post-data, but only if dumped separately */
20214 if (((ConstraintInfo *) dobj)->separate)
20215 addObjectDependency(dobj, postDataBound->dumpId);
20216 break;
20218 /* nothing to do */
20219 break;
20221 /* must come after the pre-data boundary */
20222 addObjectDependency(dobj, preDataBound->dumpId);
20223 break;
20224 case DO_REL_STATS:
20225 /* stats section varies by parent object type, DATA or POST */
20226 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20227 {
20228 addObjectDependency(dobj, preDataBound->dumpId);
20229 addObjectDependency(postDataBound, dobj->dumpId);
20230 }
20231 else
20232 addObjectDependency(dobj, postDataBound->dumpId);
20233 break;
20234 }
20235 }
20236}
20237
20238
20239/*
20240 * BuildArchiveDependencies - create dependency data for archive TOC entries
20241 *
20242 * The raw dependency data obtained by getDependencies() is not terribly
20243 * useful in an archive dump, because in many cases there are dependency
20244 * chains linking through objects that don't appear explicitly in the dump.
20245 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20246 * will depend on other objects --- but the rule will not appear as a separate
20247 * object in the dump. We need to adjust the view's dependencies to include
20248 * whatever the rule depends on that is included in the dump.
20249 *
20250 * Just to make things more complicated, there are also "special" dependencies
20251 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20252 * not rearrange because pg_restore knows that TABLE DATA only depends on
20253 * its table. In these cases we must leave the dependencies strictly as-is
20254 * even if they refer to not-to-be-dumped objects.
20255 *
20256 * To handle this, the convention is that "special" dependencies are created
20257 * during ArchiveEntry calls, and an archive TOC item that has any such
20258 * entries will not be touched here. Otherwise, we recursively search the
20259 * DumpableObject data structures to build the correct dependencies for each
20260 * archive TOC item.
20261 */
20262static void
20264{
20265 ArchiveHandle *AH = (ArchiveHandle *) fout;
20266 TocEntry *te;
20267
20268 /* Scan all TOC entries in the archive */
20269 for (te = AH->toc->next; te != AH->toc; te = te->next)
20270 {
20271 DumpableObject *dobj;
20272 DumpId *dependencies;
20273 int nDeps;
20274 int allocDeps;
20275
20276 /* No need to process entries that will not be dumped */
20277 if (te->reqs == 0)
20278 continue;
20279 /* Ignore entries that already have "special" dependencies */
20280 if (te->nDeps > 0)
20281 continue;
20282 /* Otherwise, look up the item's original DumpableObject, if any */
20283 dobj = findObjectByDumpId(te->dumpId);
20284 if (dobj == NULL)
20285 continue;
20286 /* No work if it has no dependencies */
20287 if (dobj->nDeps <= 0)
20288 continue;
20289 /* Set up work array */
20290 allocDeps = 64;
20291 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20292 nDeps = 0;
20293 /* Recursively find all dumpable dependencies */
20294 findDumpableDependencies(AH, dobj,
20295 &dependencies, &nDeps, &allocDeps);
20296 /* And save 'em ... */
20297 if (nDeps > 0)
20298 {
20299 dependencies = (DumpId *) pg_realloc(dependencies,
20300 nDeps * sizeof(DumpId));
20301 te->dependencies = dependencies;
20302 te->nDeps = nDeps;
20303 }
20304 else
20305 free(dependencies);
20306 }
20307}
20308
20309/* Recursive search subroutine for BuildArchiveDependencies */
20310static void
20312 DumpId **dependencies, int *nDeps, int *allocDeps)
20313{
20314 int i;
20315
20316 /*
20317 * Ignore section boundary objects: if we search through them, we'll
20318 * report lots of bogus dependencies.
20319 */
20320 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20322 return;
20323
20324 for (i = 0; i < dobj->nDeps; i++)
20325 {
20326 DumpId depid = dobj->dependencies[i];
20327
20328 if (TocIDRequired(AH, depid) != 0)
20329 {
20330 /* Object will be dumped, so just reference it as a dependency */
20331 if (*nDeps >= *allocDeps)
20332 {
20333 *allocDeps *= 2;
20334 *dependencies = (DumpId *) pg_realloc(*dependencies,
20335 *allocDeps * sizeof(DumpId));
20336 }
20337 (*dependencies)[*nDeps] = depid;
20338 (*nDeps)++;
20339 }
20340 else
20341 {
20342 /*
20343 * Object will not be dumped, so recursively consider its deps. We
20344 * rely on the assumption that sortDumpableObjects already broke
20345 * any dependency loops, else we might recurse infinitely.
20346 */
20347 DumpableObject *otherdobj = findObjectByDumpId(depid);
20348
20349 if (otherdobj)
20350 findDumpableDependencies(AH, otherdobj,
20351 dependencies, nDeps, allocDeps);
20352 }
20353 }
20354}
20355
20356
20357/*
20358 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20359 * given type OID.
20360 *
20361 * This does not guarantee to schema-qualify the output, so it should not
20362 * be used to create the target object name for CREATE or ALTER commands.
20363 *
20364 * Note that the result is cached and must not be freed by the caller.
20365 */
20366static const char *
20368{
20369 TypeInfo *typeInfo;
20370 char *result;
20371 PQExpBuffer query;
20372 PGresult *res;
20373
20374 if (oid == 0)
20375 {
20376 if ((opts & zeroAsStar) != 0)
20377 return "*";
20378 else if ((opts & zeroAsNone) != 0)
20379 return "NONE";
20380 }
20381
20382 /* see if we have the result cached in the type's TypeInfo record */
20383 typeInfo = findTypeByOid(oid);
20384 if (typeInfo && typeInfo->ftypname)
20385 return typeInfo->ftypname;
20386
20387 query = createPQExpBuffer();
20388 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20389 oid);
20390
20391 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20392
20393 /* result of format_type is already quoted */
20394 result = pg_strdup(PQgetvalue(res, 0, 0));
20395
20396 PQclear(res);
20397 destroyPQExpBuffer(query);
20398
20399 /*
20400 * Cache the result for re-use in later requests, if possible. If we
20401 * don't have a TypeInfo for the type, the string will be leaked once the
20402 * caller is done with it ... but that case really should not happen, so
20403 * leaking if it does seems acceptable.
20404 */
20405 if (typeInfo)
20406 typeInfo->ftypname = result;
20407
20408 return result;
20409}
20410
20411/*
20412 * Return a column list clause for the given relation.
20413 *
20414 * Special case: if there are no undropped columns in the relation, return
20415 * "", not an invalid "()" column list.
20416 */
20417static const char *
20419{
20420 int numatts = ti->numatts;
20421 char **attnames = ti->attnames;
20422 bool *attisdropped = ti->attisdropped;
20423 char *attgenerated = ti->attgenerated;
20424 bool needComma;
20425 int i;
20426
20427 appendPQExpBufferChar(buffer, '(');
20428 needComma = false;
20429 for (i = 0; i < numatts; i++)
20430 {
20431 if (attisdropped[i])
20432 continue;
20433 if (attgenerated[i])
20434 continue;
20435 if (needComma)
20436 appendPQExpBufferStr(buffer, ", ");
20437 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20438 needComma = true;
20439 }
20440
20441 if (!needComma)
20442 return ""; /* no undropped columns */
20443
20444 appendPQExpBufferChar(buffer, ')');
20445 return buffer->data;
20446}
20447
20448/*
20449 * Check if a reloptions array is nonempty.
20450 */
20451static bool
20452nonemptyReloptions(const char *reloptions)
20453{
20454 /* Don't want to print it if it's just "{}" */
20455 return (reloptions != NULL && strlen(reloptions) > 2);
20456}
20457
20458/*
20459 * Format a reloptions array and append it to the given buffer.
20460 *
20461 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20462 */
20463static void
20464appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20465 const char *prefix, Archive *fout)
20466{
20467 bool res;
20468
20469 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20470 fout->std_strings);
20471 if (!res)
20472 pg_log_warning("could not parse %s array", "reloptions");
20473}
20474
20475/*
20476 * read_dump_filters - retrieve object identifier patterns from file
20477 *
20478 * Parse the specified filter file for include and exclude patterns, and add
20479 * them to the relevant lists. If the filename is "-" then filters will be
20480 * read from STDIN rather than a file.
20481 */
20482static void
20484{
20485 FilterStateData fstate;
20486 char *objname;
20487 FilterCommandType comtype;
20488 FilterObjectType objtype;
20489
20490 filter_init(&fstate, filename, exit_nicely);
20491
20492 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20493 {
20494 if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20495 {
20496 switch (objtype)
20497 {
20499 break;
20506 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20507 "include",
20508 filter_object_type_name(objtype));
20509 exit_nicely(1);
20510 break; /* unreachable */
20511
20514 break;
20517 break;
20520 dopt->include_everything = false;
20521 break;
20524 dopt->include_everything = false;
20525 break;
20528 objname);
20529 dopt->include_everything = false;
20530 break;
20531 }
20532 }
20533 else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20534 {
20535 switch (objtype)
20536 {
20538 break;
20544 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20545 "exclude",
20546 filter_object_type_name(objtype));
20547 exit_nicely(1);
20548 break;
20549
20552 break;
20555 objname);
20556 break;
20559 objname);
20560 break;
20563 break;
20566 break;
20569 objname);
20570 break;
20571 }
20572 }
20573 else
20574 {
20575 Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20576 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20577 }
20578
20579 if (objname)
20580 free(objname);
20581 }
20582
20583 filter_free(&fstate);
20584}
Acl * acldefault(ObjectType objtype, Oid ownerId)
Definition: acl.c:803
#define InvalidAttrNumber
Definition: attnum.h:23
int lo_read(int fd, char *buf, int len)
Definition: be-fsstubs.c:154
void recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
Definition: common.c:719
void recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
Definition: common.c:1063
FuncInfo * findFuncByOid(Oid oid)
Definition: common.c:918
TableInfo * findTableByOid(Oid oid)
Definition: common.c:863
ExtensionInfo * findExtensionByOid(Oid oid)
Definition: common.c:1008
CollInfo * findCollationByOid(Oid oid)
Definition: common.c:972
SubscriptionInfo * findSubscriptionByOid(Oid oid)
Definition: common.c:1044
OprInfo * findOprByOid(Oid oid)
Definition: common.c:936
NamespaceInfo * findNamespaceByOid(Oid oid)
Definition: common.c:990
void addObjectDependency(DumpableObject *dobj, DumpId refId)
Definition: common.c:818
DumpableObject * findObjectByDumpId(DumpId dumpId)
Definition: common.c:765
void parseOidArray(const char *str, Oid *array, int arraysize)
Definition: common.c:1111
ExtensionInfo * findOwningExtension(CatalogId catalogId)
Definition: common.c:1087
TableInfo * getSchemaData(Archive *fout, int *numTablesPtr)
Definition: common.c:98
TypeInfo * findTypeByOid(Oid oid)
Definition: common.c:899
DumpId createDumpId(void)
Definition: common.c:745
DumpableObject * findObjectByCatalogId(CatalogId catalogId)
Definition: common.c:778
void AssignDumpId(DumpableObject *dobj)
Definition: common.c:657
void getDumpableObjects(DumpableObject ***objs, int *numObjs)
Definition: common.c:797
PublicationInfo * findPublicationByOid(Oid oid)
Definition: common.c:1026
void on_exit_close_archive(Archive *AHX)
Definition: parallel.c:330
void init_parallel_dump_utils(void)
Definition: parallel.c:238
#define PG_MAX_JOBS
Definition: parallel.h:48
uint32 BlockNumber
Definition: block.h:31
static void cleanup(void)
Definition: bootstrap.c:715
static const gbtree_vinfo tinfo
Definition: btree_bit.c:109
#define PG_INT32_MAX
Definition: c.h:608
#define ngettext(s, p, n)
Definition: c.h:1179
#define INT64_FORMAT
Definition: c.h:570
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1212
int64_t int64
Definition: c.h:549
#define PG_INT16_MIN
Definition: c.h:604
#define CppAsString2(x)
Definition: c.h:434
int32_t int32
Definition: c.h:548
#define PG_INT64_MAX
Definition: c.h:611
#define PG_INT64_MIN
Definition: c.h:610
uint32_t uint32
Definition: c.h:552
#define lengthof(array)
Definition: c.h:801
#define PG_INT32_MIN
Definition: c.h:607
#define PG_INT16_MAX
Definition: c.h:605
#define OidIsValid(objectId)
Definition: c.h:788
int nspid
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:430
char * supports_compression(const pg_compress_specification compression_spec)
Definition: compress_io.c:87
char * validate_compress_specification(pg_compress_specification *spec)
Definition: compression.c:344
bool parse_compress_algorithm(char *name, pg_compress_algorithm *algorithm)
Definition: compression.c:49
void parse_compress_specification(pg_compress_algorithm algorithm, char *specification, pg_compress_specification *result)
Definition: compression.c:107
#define PG_COMPRESSION_OPTION_WORKERS
Definition: compression.h:29
pg_compress_algorithm
Definition: compression.h:22
@ PG_COMPRESSION_NONE
Definition: compression.h:23
void parse_compress_options(const char *option, char **algorithm, char **detail)
#define ALWAYS_SECURE_SEARCH_PATH_SQL
Definition: connect.h:25
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:206
char * generate_restrict_key(void)
Definition: dumputils.c:972
bool buildACLCommands(const char *name, const char *subname, const char *nspname, const char *type, const char *acls, const char *baseacls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql)
Definition: dumputils.c:104
bool valid_restrict_key(const char *restrict_key)
Definition: dumputils.c:996
void buildShSecLabelQuery(const char *catalog_name, Oid objectId, PQExpBuffer sql)
Definition: dumputils.c:678
void makeAlterConfigCommand(PGconn *conn, const char *configitem, const char *type, const char *name, const char *type2, const char *name2, PQExpBuffer buf)
Definition: dumputils.c:864
bool buildDefaultACLCommands(const char *type, const char *nspname, const char *acls, const char *acldefault, const char *owner, int remoteVersion, PQExpBuffer sql)
Definition: dumputils.c:366
char * sanitize_line(const char *str, bool want_hyphen)
Definition: dumputils.c:52
bool variable_is_guc_list_quote(const char *name)
Definition: dumputils.c:730
void quoteAclUserName(PQExpBuffer output, const char *input)
Definition: dumputils.c:585
void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *objtype, const char *objname)
Definition: dumputils.c:696
#define _(x)
Definition: elog.c:91
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:7538
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
Definition: fe-connect.c:7659
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7794
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7704
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7802
void PQfreemem(void *ptr)
Definition: fe-exec.c:4049
Oid PQftype(const PGresult *res, int field_num)
Definition: fe-exec.c:3736
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:3606
int PQgetCopyData(PGconn *conn, char **buffer, int async)
Definition: fe-exec.c:2833
int lo_close(PGconn *conn, int fd)
Definition: fe-lobj.c:96
int lo_open(PGconn *conn, Oid lobjId, int mode)
Definition: fe-lobj.c:57
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void * pg_realloc(void *ptr, size_t size)
Definition: fe_memutils.c:65
DataDirSyncMethod
Definition: file_utils.h:28
@ DATA_DIR_SYNC_METHOD_FSYNC
Definition: file_utils.h:29
void filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
Definition: filter.c:36
void filter_free(FilterStateData *fstate)
Definition: filter.c:60
const char * filter_object_type_name(FilterObjectType fot)
Definition: filter.c:82
bool filter_read_item(FilterStateData *fstate, char **objname, FilterCommandType *comtype, FilterObjectType *objtype)
Definition: filter.c:392
void pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
Definition: filter.c:154
FilterObjectType
Definition: filter.h:48
@ FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN
Definition: filter.h:51
@ FILTER_OBJECT_TYPE_SCHEMA
Definition: filter.h:57
@ FILTER_OBJECT_TYPE_INDEX
Definition: filter.h:56
@ FILTER_OBJECT_TYPE_TRIGGER
Definition: filter.h:60
@ FILTER_OBJECT_TYPE_FOREIGN_DATA
Definition: filter.h:54
@ FILTER_OBJECT_TYPE_DATABASE
Definition: filter.h:52
@ FILTER_OBJECT_TYPE_FUNCTION
Definition: filter.h:55
@ FILTER_OBJECT_TYPE_TABLE_DATA
Definition: filter.h:50
@ FILTER_OBJECT_TYPE_NONE
Definition: filter.h:49
@ FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN
Definition: filter.h:59
@ FILTER_OBJECT_TYPE_EXTENSION
Definition: filter.h:53
@ FILTER_OBJECT_TYPE_TABLE
Definition: filter.h:58
FilterCommandType
Definition: filter.h:38
@ FILTER_COMMAND_TYPE_NONE
Definition: filter.h:39
@ FILTER_COMMAND_TYPE_EXCLUDE
Definition: filter.h:41
@ FILTER_COMMAND_TYPE_INCLUDE
Definition: filter.h:40
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:60
#define no_argument
Definition: getopt_long.h:25
#define required_argument
Definition: getopt_long.h:26
Assert(PointerIsAligned(start, uint64))
#define free(a)
Definition: header.h:65
#define comment
Definition: indent_codes.h:49
#define storage
Definition: indent_codes.h:68
long val
Definition: informix.c:689
static struct @171 value
static char * locale
Definition: initdb.c:140
static DataDirSyncMethod sync_method
Definition: initdb.c:170
static int pg_cmp_u32(uint32 a, uint32 b)
Definition: int.h:719
int j
Definition: isn.c:78
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
#define PQgetvalue
Definition: libpq-be-fe.h:253
#define PQgetResult
Definition: libpq-be-fe.h:246
#define PQgetlength
Definition: libpq-be-fe.h:254
#define PQclear
Definition: libpq-be-fe.h:245
#define PQnfields
Definition: libpq-be-fe.h:252
#define PQresultStatus
Definition: libpq-be-fe.h:247
#define PQgetisnull
Definition: libpq-be-fe.h:255
#define PQfname
Definition: libpq-be-fe.h:256
#define PQntuples
Definition: libpq-be-fe.h:251
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:125
@ PGRES_COPY_OUT
Definition: libpq-fe.h:131
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:128
#define INV_READ
Definition: libpq-fs.h:22
void pg_logging_increase_verbosity(void)
Definition: logging.c:185
void pg_logging_init(const char *argv0)
Definition: logging.c:83
void pg_logging_set_level(enum pg_log_level new_level)
Definition: logging.c:176
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_error_hint(...)
Definition: logging.h:112
#define pg_log_info(...)
Definition: logging.h:124
@ PG_LOG_WARNING
Definition: logging.h:38
#define pg_log_error_detail(...)
Definition: logging.h:109
const char * progname
Definition: main.c:44
char * pstrdup(const char *in)
Definition: mcxt.c:1759
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
Definition: option_utils.c:50
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
Definition: option_utils.c:90
Oid oprid(Operator op)
Definition: parse_oper.c:239
static AmcheckOptions opts
Definition: pg_amcheck.c:112
NameData attname
Definition: pg_attribute.h:41
char attalign
Definition: pg_attribute.h:100
int16 attlen
Definition: pg_attribute.h:59
NameData rolname
Definition: pg_authid.h:34
@ SECTION_NONE
Definition: pg_backup.h:57
@ SECTION_POST_DATA
Definition: pg_backup.h:60
@ SECTION_PRE_DATA
Definition: pg_backup.h:58
@ SECTION_DATA
Definition: pg_backup.h:59
int DumpId
Definition: pg_backup.h:284
int EndLO(Archive *AHX, Oid oid)
void ProcessArchiveRestoreOptions(Archive *AHX)
RestoreOptions * NewRestoreOptions(void)
#define InvalidDumpId
Definition: pg_backup.h:286
#define appendStringLiteralAH(buf, str, AH)
Definition: pg_backup.h:343
int StartLO(Archive *AHX, Oid oid)
enum _archiveFormat ArchiveFormat
void ConnectDatabaseAhx(Archive *AHX, const ConnParams *cparams, bool isReconnect)
Definition: pg_backup_db.c:109
void CloseArchive(Archive *AHX)
Archive * CreateArchive(const char *FileSpec, const ArchiveFormat fmt, const pg_compress_specification compression_spec, bool dosync, ArchiveMode mode, SetupWorkerPtrType setupDumpWorker, DataDirSyncMethod sync_method)
@ archModeWrite
Definition: pg_backup.h:51
@ archModeAppend
Definition: pg_backup.h:50
@ PREPQUERY_DUMPFUNC
Definition: pg_backup.h:71
@ PREPQUERY_DUMPTABLEATTACH
Definition: pg_backup.h:74
@ PREPQUERY_DUMPBASETYPE
Definition: pg_backup.h:67
@ PREPQUERY_DUMPRANGETYPE
Definition: pg_backup.h:73
@ PREPQUERY_DUMPOPR
Definition: pg_backup.h:72
@ PREPQUERY_GETATTRIBUTESTATS
Definition: pg_backup.h:75
@ PREPQUERY_DUMPDOMAIN
Definition: pg_backup.h:69
@ PREPQUERY_DUMPCOMPOSITETYPE
Definition: pg_backup.h:68
@ PREPQUERY_DUMPAGG
Definition: pg_backup.h:66
@ PREPQUERY_GETCOLUMNACLS
Definition: pg_backup.h:76
@ PREPQUERY_GETDOMAINCONSTRAINTS
Definition: pg_backup.h:77
@ PREPQUERY_DUMPENUMTYPE
Definition: pg_backup.h:70
int archprintf(Archive *AH, const char *fmt,...) pg_attribute_printf(2
void SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ropt)
#define NUM_PREP_QUERIES
Definition: pg_backup.h:80
void RestoreArchive(Archive *AHX)
void archputs(const char *s, Archive *AH)
@ archUnknown
Definition: pg_backup.h:41
@ archTar
Definition: pg_backup.h:43
@ archCustom
Definition: pg_backup.h:42
@ archDirectory
Definition: pg_backup.h:45
@ archNull
Definition: pg_backup.h:44
void InitDumpOptions(DumpOptions *opts)
void WriteData(Archive *AHX, const void *data, size_t dLen)
int TocIDRequired(ArchiveHandle *AH, DumpId id)
TocEntry * ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId, ArchiveOpts *opts)
#define ARCHIVE_OPTS(...)
#define LOBBUFSIZE
#define REQ_STATS
int(* DataDumperPtr)(Archive *AH, const void *userArg)
void ExecuteSqlStatement(Archive *AHX, const char *query)
Definition: pg_backup_db.c:217
PGresult * ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
Definition: pg_backup_db.c:229
PGresult * ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
Definition: pg_backup_db.c:244
void exit_nicely(int code)
void set_dump_section(const char *arg, int *dumpSections)
void * arg
#define pg_fatal(...)
static char format
static char * label
static PgChecksumMode mode
Definition: pg_checksums.c:56
#define FUNC_MAX_ARGS
const void size_t len
char datlocprovider
Definition: pg_database.h:44
NameData datname
Definition: pg_database.h:35
int32 encoding
Definition: pg_database.h:41
bool datistemplate
Definition: pg_database.h:47
int32 datconnlimit
Definition: pg_database.h:59
static void expand_schema_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition: pg_dump.c:1663
static const CatalogId nilCatalogId
Definition: pg_dump.c:189
static void dumpEncoding(Archive *AH)
Definition: pg_dump.c:3835
void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:8291
static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, const char *type, const char *name, const char *subname, const char *nspname, const char *tag, const char *owner, const DumpableAcl *dacl)
Definition: pg_dump.c:16408
static SimpleStringList schema_include_patterns
Definition: pg_dump.c:165
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition: pg_dump.c:18175
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition: pg_dump.c:6123
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition: pg_dump.c:2215
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition: pg_dump.c:5823
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17052
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition: pg_dump.c:16222
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition: pg_dump.c:4963
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition: pg_dump.c:20137
void getPublicationNamespaces(Archive *fout)
Definition: pg_dump.c:4756
static void dumpSearchPath(Archive *AH)
Definition: pg_dump.c:3884
static int ncomments
Definition: pg_dump.c:201
static void selectDumpableTable(TableInfo *tbinfo, Archive *fout)
Definition: pg_dump.c:2084
static DumpableObject * createBoundaryObjects(void)
Definition: pg_dump.c:20113
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition: pg_dump.c:14395
static void dumpDatabase(Archive *fout)
Definition: pg_dump.c:3282
static SimpleStringList table_include_patterns
Definition: pg_dump.c:170
static void append_depends_on_extension(Archive *fout, PQExpBuffer create, const DumpableObject *dobj, const char *catalog, const char *keyword, const char *objname)
Definition: pg_dump.c:5636
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition: pg_dump.c:5681
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition: pg_dump.c:11857
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition: pg_dump.c:2842
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition: pg_dump.c:13871
static SimpleOidList schema_exclude_oids
Definition: pg_dump.c:168
static bool have_extra_float_digits
Definition: pg_dump.c:192
static void dumpIndex(Archive *fout, const IndxInfo *indxinfo)
Definition: pg_dump.c:18265
void getPartitioningInfo(Archive *fout)
Definition: pg_dump.c:7783
static int nbinaryUpgradeClassOids
Definition: pg_dump.c:209
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12450
OidOptions
Definition: pg_dump.c:143
@ zeroIsError
Definition: pg_dump.c:144
@ zeroAsStar
Definition: pg_dump.c:145
@ zeroAsNone
Definition: pg_dump.c:146
static char * dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
Definition: pg_dump.c:11102
static SimpleOidList extension_include_oids
Definition: pg_dump.c:184
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition: pg_dump.c:15794
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition: pg_dump.c:15370
static int extra_float_digits
Definition: pg_dump.c:193
static int SequenceItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:18896
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition: pg_dump.c:11375
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:11401
static SimpleStringList extension_include_patterns
Definition: pg_dump.c:183
static void selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
Definition: pg_dump.c:1998
InhInfo * getInherits(Archive *fout, int *numInherits)
Definition: pg_dump.c:7727
void getForeignDataWrappers(Archive *fout)
Definition: pg_dump.c:10411
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition: pg_dump.c:19303
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition: pg_dump.c:5792
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:16863
static SimpleOidList extension_exclude_oids
Definition: pg_dump.c:187
static SimpleStringList table_exclude_patterns
Definition: pg_dump.c:173
static PQExpBuffer createViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17003
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition: pg_dump.c:18459
void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:4247
static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12228
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19686
static void dumpComment(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
Definition: pg_dump.c:10980
static char * getFormattedOperatorName(const char *oproid)
Definition: pg_dump.c:14365
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition: pg_dump.c:13420
static int nseclabels
Definition: pg_dump.c:205
static pg_compress_algorithm compression_algorithm
Definition: pg_dump.c:157
static void dumpStdStrings(Archive *AH)
Definition: pg_dump.c:3860
static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
Definition: pg_dump.c:18536
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12057
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition: pg_dump.c:18107
static void help(const char *progname)
Definition: pg_dump.c:1318
void getTypes(Archive *fout)
Definition: pg_dump.c:6198
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition: pg_dump.c:14417
int main(int argc, char **argv)
Definition: pg_dump.c:421
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition: pg_dump.c:14105
static void selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
Definition: pg_dump.c:2340
static void selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:2322
static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:19226
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition: pg_dump.c:13449
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition: pg_dump.c:2168
static void BuildArchiveDependencies(Archive *fout)
Definition: pg_dump.c:20263
static RelStatsInfo * getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages, char *reltuples, int32 relallvisible, int32 relallfrozen, char relkind, char **indAttNames, int nindAttNames)
Definition: pg_dump.c:7108
static const char *const SeqTypeNames[]
Definition: pg_dump.c:118
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:7662
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
Definition: pg_dump.c:3039
static int nsequences
Definition: pg_dump.c:213
static const char * getAttrName(int attrnum, const TableInfo *tblInfo)
Definition: pg_dump.c:18236
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition: pg_dump.c:16122
static RoleNameItem * rolenames
Definition: pg_dump.c:196
static void collectRoleNames(Archive *fout)
Definition: pg_dump.c:10716
static PGresult * fetchAttributeStats(Archive *fout)
Definition: pg_dump.c:11014
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition: pg_dump.c:20464
void getOpclasses(Archive *fout)
Definition: pg_dump.c:6644
void getForeignServers(Archive *fout)
Definition: pg_dump.c:10495
void getFuncs(Archive *fout)
Definition: pg_dump.c:6913
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:2870
static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
Definition: pg_dump.c:1923
static bool dosync
Definition: pg_dump.c:150
static int dumpTableData_copy(Archive *fout, const void *dcontext)
Definition: pg_dump.c:2380
#define MAX_BLOBS_PER_ARCHIVE_ENTRY
Definition: pg_dump.c:235
static const char * getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
Definition: pg_dump.c:20367
static void getDependencies(Archive *fout)
Definition: pg_dump.c:19960
static void buildMatViewRefreshDependencies(Archive *fout)
Definition: pg_dump.c:3126
void getTSDictionaries(Archive *fout)
Definition: pg_dump.c:10227
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_type_oid, bool force_array_type, bool include_multirange_type)
Definition: pg_dump.c:5712
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition: pg_dump.c:228
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:4836
static SeqType parse_sequence_type(const char *name)
Definition: pg_dump.c:18880
static const char * getRoleName(const char *roleoid_str)
Definition: pg_dump.c:10680
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition: pg_dump.c:13219
static SequenceItem * sequences
Definition: pg_dump.c:212
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:2985
static int findComments(Oid classoid, Oid objoid, CommentItem **items)
Definition: pg_dump.c:11499
static SimpleStringList foreign_servers_include_patterns
Definition: pg_dump.c:180
static void selectDumpableCast(CastInfo *cast, Archive *fout)
Definition: pg_dump.c:2190
void getCasts(Archive *fout)
Definition: pg_dump.c:9040
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition: pg_dump.c:4653
static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
Definition: pg_dump.c:4419
void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:7843
static void setupDumpWorker(Archive *AH)
Definition: pg_dump.c:1596
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition: pg_dump.c:8450
void getTSConfigurations(Archive *fout)
Definition: pg_dump.c:10352
static int nrolenames
Definition: pg_dump.c:197
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition: pg_dump.c:16698
static SimpleStringList table_include_patterns_and_children
Definition: pg_dump.c:171
static char * convertRegProcReference(const char *proc)
Definition: pg_dump.c:14324
static void getAdditionalACLs(Archive *fout)
Definition: pg_dump.c:10751
StaticAssertDecl(lengthof(SeqTypeNames)==(SEQTYPE_BIGINT+1), "array length mismatch")
static bool is_superuser(Archive *fout)
Definition: pg_dump.c:5067
static void getTableDataFKConstraints(void)
Definition: pg_dump.c:3241
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
Definition: pg_dump.c:3020
static SimpleOidList table_exclude_oids
Definition: pg_dump.c:175
SeqType
Definition: pg_dump.c:112
@ SEQTYPE_BIGINT
Definition: pg_dump.c:115
@ SEQTYPE_INTEGER
Definition: pg_dump.c:114
@ SEQTYPE_SMALLINT
Definition: pg_dump.c:113
void getAccessMethods(Archive *fout)
Definition: pg_dump.c:6570
void getConversions(Archive *fout)
Definition: pg_dump.c:6508
void getRules(Archive *fout)
Definition: pg_dump.c:8585
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12699
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition: pg_dump.c:9234
static void collectComments(Archive *fout)
Definition: pg_dump.c:11576
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition: pg_dump.c:8473
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition: pg_dump.c:14766
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition: pg_dump.c:16316
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:2358
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition: pg_dump.c:15874
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition: pg_dump.c:18416
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition: pg_dump.c:5413
void getCollations(Archive *fout)
Definition: pg_dump.c:6442
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition: pg_dump.c:13397
static int strict_names
Definition: pg_dump.c:155
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition: pg_dump.c:13976
void getAggregates(Archive *fout)
Definition: pg_dump.c:6772
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition: pg_dump.c:4111
void getNamespaces(Archive *fout)
Definition: pg_dump.c:5991
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition: pg_dump.c:5006
void getPublications(Archive *fout)
Definition: pg_dump.c:4537
static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, const DumpableObject *dobj, const char *objtype, const char *objname, const char *objnamespace)
Definition: pg_dump.c:5947
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition: pg_dump.c:11661
static void getLOs(Archive *fout)
Definition: pg_dump.c:3946
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
Definition: pg_dump.c:3791
void getTSParsers(Archive *fout)
Definition: pg_dump.c:10153
static DumpId lo_metadata_dumpId
Definition: pg_dump.c:219
static void setup_connection(Archive *AH, const char *dumpencoding, const char *dumpsnapshot, char *use_role)
Definition: pg_dump.c:1426
static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
Definition: pg_dump.c:18857
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12386
static void selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
Definition: pg_dump.c:2248
static const char * fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
Definition: pg_dump.c:20418
static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
Definition: pg_dump.c:1827
static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
Definition: pg_dump.c:20311
static void determineNotNullFlags(Archive *fout, PGresult *res, int r, TableInfo *tbinfo, int j, int i_notnull_name, int i_notnull_comment, int i_notnull_invalidoid, int i_notnull_noinherit, int i_notnull_islocal, PQExpBuffer *invalidnotnulloids)
Definition: pg_dump.c:10011
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17092
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition: pg_dump.c:15730
static void expand_foreign_server_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
Definition: pg_dump.c:1775
TableInfo * getTables(Archive *fout, int *numTables)
Definition: pg_dump.c:7185
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition: pg_dump.c:19519
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12924
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12088
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition: pg_dump.c:11934
#define fmtQualifiedDumpable(obj)
Definition: pg_dump.c:240
static bool nonemptyReloptions(const char *reloptions)
Definition: pg_dump.c:20452
static SimpleStringList extension_exclude_patterns
Definition: pg_dump.c:186
static BinaryUpgradeClassOidItem * binaryUpgradeClassOids
Definition: pg_dump.c:208
static SimpleOidList table_include_oids
Definition: pg_dump.c:172
void getExtendedStatistics(Archive *fout)
Definition: pg_dump.c:8212
static NamespaceInfo * findNamespace(Oid nsoid)
Definition: pg_dump.c:6105
static char * get_synchronized_snapshot(Archive *fout)
Definition: pg_dump.c:1611
static int dumpLOs(Archive *fout, const void *arg)
Definition: pg_dump.c:4201
static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
Definition: pg_dump.c:5482
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition: pg_dump.c:10996
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19779
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition: pg_dump.c:19429
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:5807
static void dumpCommentExtended(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId, const char *initdb_comment)
Definition: pg_dump.c:10880
void getDefaultACLs(Archive *fout)
Definition: pg_dump.c:10583
static SimpleStringList tabledata_exclude_patterns
Definition: pg_dump.c:176
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition: pg_dump.c:15242
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition: pg_dump.c:16052
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition: pg_dump.c:13265
static void dumpSecLabel(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
Definition: pg_dump.c:16536
void getSubscriptions(Archive *fout)
Definition: pg_dump.c:5107
static void collectSecLabels(Archive *fout)
Definition: pg_dump.c:16777
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition: pg_dump.c:2283
static void collectSequences(Archive *fout)
Definition: pg_dump.c:18911
static Oid g_last_builtin_oid
Definition: pg_dump.c:152
#define MAX_ATTR_STATS_RELS
Definition: pg_dump.c:222
void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:8682
void getTransforms(Archive *fout)
Definition: pg_dump.c:9150
void getEventTriggers(Archive *fout)
Definition: pg_dump.c:8878
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode)
Definition: pg_dump.c:1625
static void read_dump_filters(const char *filename, DumpOptions *dopt)
Definition: pg_dump.c:20483
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition: pg_dump.c:15932
static SecLabelItem * seclabels
Definition: pg_dump.c:204
static SimpleStringList tabledata_exclude_patterns_and_children
Definition: pg_dump.c:177
static char * get_language_name(Archive *fout, Oid langid)
Definition: pg_dump.c:9129
static bool checkExtensionMembership(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:1948
static CommentItem * comments
Definition: pg_dump.c:200
static int dumpTableData_insert(Archive *fout, const void *dcontext)
Definition: pg_dump.c:2548
static SimpleOidList tabledata_exclude_oids
Definition: pg_dump.c:178
static SimpleStringList table_exclude_patterns_and_children
Definition: pg_dump.c:174
static void binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_class_oid)
Definition: pg_dump.c:5857
void getTSTemplates(Archive *fout)
Definition: pg_dump.c:10293
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition: pg_dump.c:5086
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition: pg_dump.c:15338
void getProcLangs(Archive *fout)
Definition: pg_dump.c:8956
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:18972
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition: pg_dump.c:10138
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition: pg_dump.c:2817
void getSubscriptionRelations(Archive *fout)
Definition: pg_dump.c:5327
void getOperators(Archive *fout)
Definition: pg_dump.c:6366
static SimpleOidList foreign_servers_include_oids
Definition: pg_dump.c:181
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition: pg_dump.c:14985
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:16616
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition: pg_dump.c:13130
static void expand_extension_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition: pg_dump.c:1722
void getOpfamilies(Archive *fout)
Definition: pg_dump.c:6707
static void selectDumpableType(TypeInfo *tyinfo, Archive *fout)
Definition: pg_dump.c:2123
static SimpleOidList schema_include_oids
Definition: pg_dump.c:166
static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
Definition: pg_dump.c:14485
static SimpleStringList schema_exclude_patterns
Definition: pg_dump.c:167
#define DUMP_COMPONENT_COMMENT
Definition: pg_dump.h:111
#define DUMP_COMPONENT_DATA
Definition: pg_dump.h:110
#define DUMP_COMPONENT_USERMAP
Definition: pg_dump.h:115
#define DUMP_COMPONENT_POLICY
Definition: pg_dump.h:114
#define DUMP_COMPONENT_SECLABEL
Definition: pg_dump.h:112
#define DUMP_COMPONENT_ALL
Definition: pg_dump.h:117
#define DUMP_COMPONENT_ACL
Definition: pg_dump.h:113
#define DUMP_COMPONENT_NONE
Definition: pg_dump.h:108
#define DUMP_COMPONENTS_REQUIRING_LOCK
Definition: pg_dump.h:141
void sortDumpableObjects(DumpableObject **objs, int numObjs, DumpId preBoundaryId, DumpId postBoundaryId)
Definition: pg_dump_sort.c:548
#define DUMP_COMPONENT_DEFINITION
Definition: pg_dump.h:109
@ DO_EVENT_TRIGGER
Definition: pg_dump.h:80
@ DO_REFRESH_MATVIEW
Definition: pg_dump.h:81
@ DO_POLICY
Definition: pg_dump.h:82
@ DO_CAST
Definition: pg_dump.h:64
@ DO_FOREIGN_SERVER
Definition: pg_dump.h:73
@ DO_PRE_DATA_BOUNDARY
Definition: pg_dump.h:78
@ DO_PROCLANG
Definition: pg_dump.h:63
@ DO_TYPE
Definition: pg_dump.h:43
@ DO_INDEX
Definition: pg_dump.h:56
@ DO_COLLATION
Definition: pg_dump.h:51
@ DO_LARGE_OBJECT
Definition: pg_dump.h:76
@ DO_TSCONFIG
Definition: pg_dump.h:71
@ DO_OPERATOR
Definition: pg_dump.h:47
@ DO_FK_CONSTRAINT
Definition: pg_dump.h:62
@ DO_CONSTRAINT
Definition: pg_dump.h:61
@ DO_SUBSCRIPTION
Definition: pg_dump.h:87
@ DO_DEFAULT_ACL
Definition: pg_dump.h:74
@ DO_FDW
Definition: pg_dump.h:72
@ DO_SUBSCRIPTION_REL
Definition: pg_dump.h:88
@ DO_REL_STATS
Definition: pg_dump.h:86
@ DO_SEQUENCE_SET
Definition: pg_dump.h:66
@ DO_ATTRDEF
Definition: pg_dump.h:55
@ DO_PUBLICATION_REL
Definition: pg_dump.h:84
@ DO_TABLE_ATTACH
Definition: pg_dump.h:54
@ DO_OPCLASS
Definition: pg_dump.h:49
@ DO_INDEX_ATTACH
Definition: pg_dump.h:57
@ DO_TSTEMPLATE
Definition: pg_dump.h:70
@ DO_STATSEXT
Definition: pg_dump.h:58
@ DO_FUNC
Definition: pg_dump.h:45
@ DO_POST_DATA_BOUNDARY
Definition: pg_dump.h:79
@ DO_LARGE_OBJECT_DATA
Definition: pg_dump.h:77
@ DO_OPFAMILY
Definition: pg_dump.h:50
@ DO_TRANSFORM
Definition: pg_dump.h:75
@ DO_ACCESS_METHOD
Definition: pg_dump.h:48
@ DO_PUBLICATION_TABLE_IN_SCHEMA
Definition: pg_dump.h:85
@ DO_CONVERSION
Definition: pg_dump.h:52
@ DO_TRIGGER
Definition: pg_dump.h:60
@ DO_RULE
Definition: pg_dump.h:59
@ DO_DUMMY_TYPE
Definition: pg_dump.h:67
@ DO_TSDICT
Definition: pg_dump.h:69
@ DO_TSPARSER
Definition: pg_dump.h:68
@ DO_EXTENSION
Definition: pg_dump.h:42
@ DO_TABLE_DATA
Definition: pg_dump.h:65
@ DO_PUBLICATION
Definition: pg_dump.h:83
@ DO_TABLE
Definition: pg_dump.h:53
@ DO_NAMESPACE
Definition: pg_dump.h:41
@ DO_AGG
Definition: pg_dump.h:46
@ DO_SHELL_TYPE
Definition: pg_dump.h:44
void sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
Definition: pg_dump_sort.c:192
#define DUMP_COMPONENT_STATISTICS
Definition: pg_dump.h:116
static int statistics_only
Definition: pg_dumpall.c:112
static int no_statistics
Definition: pg_dumpall.c:103
static int no_data
Definition: pg_dumpall.c:101
static int no_schema
Definition: pg_dumpall.c:102
static char * filename
Definition: pg_dumpall.c:120
static int with_statistics
Definition: pg_dumpall.c:108
PGDLLIMPORT int optind
Definition: getopt.c:51
PGDLLIMPORT char * optarg
Definition: getopt.c:53
NameData subname
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
char typalign
Definition: pg_type.h:176
#define pg_encoding_to_char
Definition: pg_wchar.h:630
static char * tablespace
Definition: pgbench.c:217
#define pg_log_warning(...)
Definition: pgfnames.c:24
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:32
#define snprintf
Definition: port.h:260
const char * get_progname(const char *argv0)
Definition: path.c:652
#define printf(...)
Definition: port.h:266
#define pgoff_t
Definition: port.h:421
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
#define atooid(x)
Definition: postgres_ext.h:43
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:235
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
Definition: pqexpbuffer.c:397
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:114
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
char * c
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
Oid RelFileNumber
Definition: relpath.h:25
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
bool quote_all_identifiers
Definition: ruleutils.c:339
void simple_string_list_append(SimpleStringList *list, const char *val)
Definition: simple_list.c:63
bool simple_oid_list_member(SimpleOidList *list, Oid val)
Definition: simple_list.c:45
void simple_oid_list_append(SimpleOidList *list, Oid val)
Definition: simple_list.c:26
struct SimplePtrList SimplePtrList
char * dbname
Definition: streamutil.c:49
PGconn * conn
Definition: streamutil.c:52
const char * fmtId(const char *rawid)
Definition: string_utils.c:248
void setFmtEncoding(int encoding)
Definition: string_utils.c:69
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:446
void appendPGArray(PQExpBuffer buffer, const char *value)
Definition: string_utils.c:902
bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, PQExpBuffer dbnamebuf, int *dotcnt)
bool parsePGArray(const char *atext, char ***itemarray, int *nitems)
Definition: string_utils.c:819
bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions, const char *prefix, int encoding, bool std_strings)
Definition: string_utils.c:966
void appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
Definition: string_utils.c:484
int minRemoteVersion
Definition: pg_backup.h:236
int remoteVersion
Definition: pg_backup.h:233
DumpOptions * dopt
Definition: pg_backup.h:228
bool * is_prepared
Definition: pg_backup.h:255
char * searchpath
Definition: pg_backup.h:247
bool isStandby
Definition: pg_backup.h:234
int maxRemoteVersion
Definition: pg_backup.h:237
bool std_strings
Definition: pg_backup.h:244
int numWorkers
Definition: pg_backup.h:239
int encoding
Definition: pg_backup.h:243
char * use_role
Definition: pg_backup.h:248
char * sync_snapshot_id
Definition: pg_backup.h:240
int verbose
Definition: pg_backup.h:231
RelFileNumber toast_index_relfilenumber
Definition: pg_dump.c:107
RelFileNumber toast_relfilenumber
Definition: pg_dump.c:105
RelFileNumber relfilenumber
Definition: pg_dump.c:103
Oid tableoid
Definition: pg_backup.h:280
Oid classoid
Definition: pg_dump.c:85
Oid objoid
Definition: pg_dump.c:86
int objsubid
Definition: pg_dump.c:87
const char * descr
Definition: pg_dump.c:84
const char * rolename
Definition: pg_dump.c:79
Oid roleoid
Definition: pg_dump.c:78
const char * provider
Definition: pg_dump.c:92
Oid classoid
Definition: pg_dump.c:94
int objsubid
Definition: pg_dump.c:96
const char * label
Definition: pg_dump.c:93
Oid objoid
Definition: pg_dump.c:95
int64 minv
Definition: pg_dump.c:133
int64 cache
Definition: pg_dump.c:137
int64 startv
Definition: pg_dump.c:135
int64 maxv
Definition: pg_dump.c:134
bool is_called
Definition: pg_dump.c:139
int64 incby
Definition: pg_dump.c:136
int64 last_value
Definition: pg_dump.c:138
SeqType seqtype
Definition: pg_dump.c:131
bool cycled
Definition: pg_dump.c:132
SimpleOidListCell * head
Definition: simple_list.h:28
struct SimplePtrListCell * next
Definition: simple_list.h:48
SimplePtrListCell * head
Definition: simple_list.h:54
char val[FLEXIBLE_ARRAY_MEMBER]
Definition: simple_list.h:37
struct SimpleStringListCell * next
Definition: simple_list.h:34
SimpleStringListCell * head
Definition: simple_list.h:42
const char * rolname
Definition: pg_dump.h:670
bool puballsequences
Definition: pg_dump.h:672
bool puballtables
Definition: pg_dump.h:671
bool pubtruncate
Definition: pg_dump.h:676
PublishGencolsType pubgencols_type
Definition: pg_dump.h:678
DumpableObject dobj
Definition: pg_dump.h:669
NamespaceInfo * pubschema
Definition: pg_dump.h:702
DumpableObject dobj
Definition: pg_dump.h:700
PublicationInfo * publication
Definition: pg_dump.h:701
DumpableObject dobj
Definition: pg_dump.h:742
char * srsublsn
Definition: pg_dump.h:746
SubscriptionInfo * subinfo
Definition: pg_dump.h:743
TableInfo * tblinfo
Definition: pg_dump.h:744
char srsubstate
Definition: pg_dump.h:745
char * suboriginremotelsn
Definition: pg_dump.h:727
bool subpasswordrequired
Definition: pg_dump.h:717
char * suborigin
Definition: pg_dump.h:726
const char * rolname
Definition: pg_dump.h:711
char * subsynccommit
Definition: pg_dump.h:724
char * subpublications
Definition: pg_dump.h:725
bool subdisableonerr
Definition: pg_dump.h:716
bool subrunasowner
Definition: pg_dump.h:718
char * subslotname
Definition: pg_dump.h:723
char subtwophasestate
Definition: pg_dump.h:715
bool subretaindeadtuples
Definition: pg_dump.h:720
char * subconninfo
Definition: pg_dump.h:722
DumpableObject dobj
Definition: pg_dump.h:710
char * amhandler
Definition: pg_dump.h:272
DumpableObject dobj
Definition: pg_dump.h:270
ArchiveFormat format
struct _tocEntry * toc
DumpableObject dobj
Definition: pg_dump.h:404
char * adef_expr
Definition: pg_dump.h:407
TableInfo * adtable
Definition: pg_dump.h:405
bool separate
Definition: pg_dump.h:408
char castmethod
Definition: pg_dump.h:549
Oid casttarget
Definition: pg_dump.h:546
char castcontext
Definition: pg_dump.h:548
DumpableObject dobj
Definition: pg_dump.h:544
Oid castsource
Definition: pg_dump.h:545
Oid castfunc
Definition: pg_dump.h:547
Oid cfgparser
Definition: pg_dump.h:597
DumpableObject dobj
Definition: pg_dump.h:595
const char * rolname
Definition: pg_dump.h:596
int collencoding
Definition: pg_dump.h:293
const char * rolname
Definition: pg_dump.h:292
DumpableObject dobj
Definition: pg_dump.h:291
char * pgport
Definition: pg_backup.h:87
char * pghost
Definition: pg_backup.h:88
trivalue promptPassword
Definition: pg_backup.h:90
char * username
Definition: pg_backup.h:89
char * dbname
Definition: pg_backup.h:86
TypeInfo * condomain
Definition: pg_dump.h:519
TableInfo * contable
Definition: pg_dump.h:518
bool condeferred
Definition: pg_dump.h:525
bool conperiod
Definition: pg_dump.h:526
bool conislocal
Definition: pg_dump.h:527
DumpableObject dobj
Definition: pg_dump.h:517
DumpId conindex
Definition: pg_dump.h:523
bool condeferrable
Definition: pg_dump.h:524
char * condef
Definition: pg_dump.h:521
DumpableObject dobj
Definition: pg_dump.h:298
const char * rolname
Definition: pg_dump.h:299
DumpableObject dobj
Definition: pg_dump.h:623
DumpableAcl dacl
Definition: pg_dump.h:624
const char * defaclrole
Definition: pg_dump.h:625
char defaclobjtype
Definition: pg_dump.h:626
char * dictinitoption
Definition: pg_dump.h:583
DumpableObject dobj
Definition: pg_dump.h:580
const char * rolname
Definition: pg_dump.h:581
Oid dicttemplate
Definition: pg_dump.h:582
int dump_inserts
Definition: pg_backup.h:180
int no_toast_compression
Definition: pg_backup.h:191
char * restrict_key
Definition: pg_backup.h:219
int column_inserts
Definition: pg_backup.h:184
bool dontOutputLOs
Definition: pg_backup.h:207
int use_setsessauth
Definition: pg_backup.h:197
int outputCreateDB
Definition: pg_backup.h:205
bool include_everything
Definition: pg_backup.h:202
int sequence_data
Definition: pg_backup.h:211
int disable_dollar_quoting
Definition: pg_backup.h:183
bool dumpSchema
Definition: pg_backup.h:215
bool outputLOs
Definition: pg_backup.h:206
int no_comments
Definition: pg_backup.h:186
int serializable_deferrable
Definition: pg_backup.h:193
int outputNoTableAm
Definition: pg_backup.h:195
int enable_row_security
Definition: pg_backup.h:198
char * outputSuperuser
Definition: pg_backup.h:209
int dumpSections
Definition: pg_backup.h:177
int no_security_labels
Definition: pg_backup.h:189
bool dumpData
Definition: pg_backup.h:216
int no_unlogged_table_data
Definition: pg_backup.h:192
bool dumpStatistics
Definition: pg_backup.h:217
int no_publications
Definition: pg_backup.h:188
ConnParams cparams
Definition: pg_backup.h:172
const char * lockWaitTimeout
Definition: pg_backup.h:179
int no_subscriptions
Definition: pg_backup.h:190
bool aclsSkip
Definition: pg_backup.h:178
int load_via_partition_root
Definition: pg_backup.h:199
int outputClean
Definition: pg_backup.h:204
int no_policies
Definition: pg_backup.h:187
int do_nothing
Definition: pg_backup.h:212
int outputNoTablespaces
Definition: pg_backup.h:196
int disable_triggers
Definition: pg_backup.h:194
int outputNoOwner
Definition: pg_backup.h:208
int binary_upgrade
Definition: pg_backup.h:174
char privtype
Definition: pg_dump.h:173
char * acldefault
Definition: pg_dump.h:171
char * acl
Definition: pg_dump.h:170
char * initprivs
Definition: pg_dump.h:174
DumpableAcl dacl
Definition: pg_dump.h:181
DumpComponents dump
Definition: pg_dump.h:153
char * name
Definition: pg_dump.h:152
DumpId * dependencies
Definition: pg_dump.h:159
DumpId dumpId
Definition: pg_dump.h:151
bool ext_member
Definition: pg_dump.h:157
DumpComponents components
Definition: pg_dump.h:156
DumpableObjectType objType
Definition: pg_dump.h:149
CatalogId catId
Definition: pg_dump.h:150
DumpComponents dump_contains
Definition: pg_dump.h:155
bool depends_on_ext
Definition: pg_dump.h:158
char * evtevent
Definition: pg_dump.h:498
char * evtfname
Definition: pg_dump.h:501
char evtenabled
Definition: pg_dump.h:502
char * evtname
Definition: pg_dump.h:497
const char * evtowner
Definition: pg_dump.h:499
char * evttags
Definition: pg_dump.h:500
DumpableObject dobj
Definition: pg_dump.h:496
bool relocatable
Definition: pg_dump.h:196
char * extversion
Definition: pg_dump.h:198
DumpableObject dobj
Definition: pg_dump.h:195
char * extcondition
Definition: pg_dump.h:200
char * extconfig
Definition: pg_dump.h:199
char * fdwhandler
Definition: pg_dump.h:605
const char * rolname
Definition: pg_dump.h:604
char * fdwvalidator
Definition: pg_dump.h:606
char * fdwoptions
Definition: pg_dump.h:607
DumpableAcl dacl
Definition: pg_dump.h:603
DumpableObject dobj
Definition: pg_dump.h:602
DumpableAcl dacl
Definition: pg_dump.h:613
char * srvoptions
Definition: pg_dump.h:618
DumpableObject dobj
Definition: pg_dump.h:612
const char * rolname
Definition: pg_dump.h:614
char * srvversion
Definition: pg_dump.h:617
bool postponed_def
Definition: pg_dump.h:248
Oid lang
Definition: pg_dump.h:244
const char * rolname
Definition: pg_dump.h:243
Oid * argtypes
Definition: pg_dump.h:246
Oid prorettype
Definition: pg_dump.h:247
DumpableObject dobj
Definition: pg_dump.h:241
int nargs
Definition: pg_dump.h:245
DumpableAcl dacl
Definition: pg_dump.h:242
IndxInfo * partitionIdx
Definition: pg_dump.h:445
DumpableObject dobj
Definition: pg_dump.h:443
IndxInfo * parentIdx
Definition: pg_dump.h:444
bool indisreplident
Definition: pg_dump.h:432
int indnkeyattrs
Definition: pg_dump.h:427
char * indstatvals
Definition: pg_dump.h:426
char * indstatcols
Definition: pg_dump.h:425
int indnattrs
Definition: pg_dump.h:428
TableInfo * indextable
Definition: pg_dump.h:421
Oid parentidx
Definition: pg_dump.h:434
Oid * indkeys
Definition: pg_dump.h:429
char * indreloptions
Definition: pg_dump.h:424
DumpId indexconstraint
Definition: pg_dump.h:438
bool indisclustered
Definition: pg_dump.h:431
SimplePtrList partattaches
Definition: pg_dump.h:435
char * tablespace
Definition: pg_dump.h:423
bool indnullsnotdistinct
Definition: pg_dump.h:433
char * indexdef
Definition: pg_dump.h:422
DumpableObject dobj
Definition: pg_dump.h:420
Oid inhparent
Definition: pg_dump.h:565
Oid inhrelid
Definition: pg_dump.h:564
const char * rolname
Definition: pg_dump.h:641
DumpableObject dobj
Definition: pg_dump.h:639
DumpableAcl dacl
Definition: pg_dump.h:640
Oid looids[FLEXIBLE_ARRAY_MEMBER]
Definition: pg_dump.h:643
int numlos
Definition: pg_dump.h:642
DumpableObject dobj
Definition: pg_dump.h:186
DumpableAcl dacl
Definition: pg_dump.h:187
const char * rolname
Definition: pg_dump.h:190
Oid opcmethod
Definition: pg_dump.h:278
DumpableObject dobj
Definition: pg_dump.h:277
const char * rolname
Definition: pg_dump.h:279
const char * rolname
Definition: pg_dump.h:286
Oid opfmethod
Definition: pg_dump.h:285
DumpableObject dobj
Definition: pg_dump.h:284
DumpableObject dobj
Definition: pg_dump.h:260
Oid oprleft
Definition: pg_dump.h:263
char oprkind
Definition: pg_dump.h:262
Oid oprcode
Definition: pg_dump.h:265
Oid oprright
Definition: pg_dump.h:264
const char * rolname
Definition: pg_dump.h:261
TableInfo * poltable
Definition: pg_dump.h:655
char * polqual
Definition: pg_dump.h:660
char polcmd
Definition: pg_dump.h:657
char * polroles
Definition: pg_dump.h:659
char * polwithcheck
Definition: pg_dump.h:661
DumpableObject dobj
Definition: pg_dump.h:654
bool polpermissive
Definition: pg_dump.h:658
char * polname
Definition: pg_dump.h:656
Oid lanvalidator
Definition: pg_dump.h:538
DumpableAcl dacl
Definition: pg_dump.h:534
DumpableObject dobj
Definition: pg_dump.h:533
Oid laninline
Definition: pg_dump.h:537
const char * lanowner
Definition: pg_dump.h:539
Oid lanplcallfoid
Definition: pg_dump.h:536
bool lanpltrusted
Definition: pg_dump.h:535
DumpableObject dobj
Definition: pg_dump.h:570
Oid prstoken
Definition: pg_dump.h:572
Oid prslextype
Definition: pg_dump.h:575
Oid prsheadline
Definition: pg_dump.h:574
Oid prsstart
Definition: pg_dump.h:571
Oid prsend
Definition: pg_dump.h:573
int32 nindAttNames
Definition: pg_dump.h:462
char relkind
Definition: pg_dump.h:455
char ** indAttNames
Definition: pg_dump.h:461
int32 relpages
Definition: pg_dump.h:451
int32 relallfrozen
Definition: pg_dump.h:454
char * reltuples
Definition: pg_dump.h:452
teSection section
Definition: pg_dump.h:463
int32 relallvisible
Definition: pg_dump.h:453
DumpableObject dobj
Definition: pg_dump.h:450
int include_everything
Definition: pg_backup.h:125
int suppressDumpWarnings
Definition: pg_backup.h:151
ConnParams cparams
Definition: pg_backup.h:145
pg_compress_specification compression_spec
Definition: pg_backup.h:149
int no_subscriptions
Definition: pg_backup.h:117
bool dumpStatistics
Definition: pg_backup.h:165
int disable_dollar_quoting
Definition: pg_backup.h:109
char * restrict_key
Definition: pg_backup.h:167
const char * filename
Definition: pg_backup.h:120
int no_security_labels
Definition: pg_backup.h:116
char * superuser
Definition: pg_backup.h:106
const char * lockWaitTimeout
Definition: pg_backup.h:124
int enable_row_security
Definition: pg_backup.h:158
int disable_triggers
Definition: pg_backup.h:102
DumpableObject dobj
Definition: pg_dump.h:476
bool separate
Definition: pg_dump.h:481
char ev_enabled
Definition: pg_dump.h:480
bool is_instead
Definition: pg_dump.h:479
TableInfo * ruletable
Definition: pg_dump.h:477
char ev_type
Definition: pg_dump.h:478
TypeInfo * baseType
Definition: pg_dump.h:236
DumpableObject dobj
Definition: pg_dump.h:234
TableInfo * stattable
Definition: pg_dump.h:470
int stattarget
Definition: pg_dump.h:471
const char * rolname
Definition: pg_dump.h:469
DumpableObject dobj
Definition: pg_dump.h:468
TableInfo * partitionTbl
Definition: pg_dump.h:399
DumpableObject dobj
Definition: pg_dump.h:397
TableInfo * parentTbl
Definition: pg_dump.h:398
TableInfo * tdtable
Definition: pg_dump.h:414
DumpableObject dobj
Definition: pg_dump.h:413
char * filtercond
Definition: pg_dump.h:415
bool * notnull_invalid
Definition: pg_dump.h:376
char * attidentity
Definition: pg_dump.h:361
char * reltablespace
Definition: pg_dump.h:314
char ** notnull_constrs
Definition: pg_dump.h:371
struct _relStatsInfo * stats
Definition: pg_dump.h:381
int ncheck
Definition: pg_dump.h:330
bool ispartition
Definition: pg_dump.h:344
struct _indxInfo * indexes
Definition: pg_dump.h:389
bool * attislocal
Definition: pg_dump.h:365
DumpableObject dobj
Definition: pg_dump.h:307
bool is_identity_sequence
Definition: pg_dump.h:337
Oid reloftype
Definition: pg_dump.h:332
int numParents
Definition: pg_dump.h:347
bool interesting
Definition: pg_dump.h:341
char * toast_reloptions
Definition: pg_dump.h:317
struct _tableInfo ** parents
Definition: pg_dump.h:348
DumpableAcl dacl
Definition: pg_dump.h:308
bool relispopulated
Definition: pg_dump.h:312
char * attgenerated
Definition: pg_dump.h:362
int * attlen
Definition: pg_dump.h:363
Oid reltype
Definition: pg_dump.h:331
char ** attfdwoptions
Definition: pg_dump.h:369
bool hasoids
Definition: pg_dump.h:324
Oid toast_oid
Definition: pg_dump.h:327
Oid foreign_server
Definition: pg_dump.h:333
char ** notnull_comment
Definition: pg_dump.h:375
bool hasrules
Definition: pg_dump.h:319
struct _triggerInfo * triggers
Definition: pg_dump.h:392
bool * attisdropped
Definition: pg_dump.h:360
bool needs_override
Definition: pg_dump.h:382
struct _constraintInfo * checkexprs
Definition: pg_dump.h:380
int * attstattarget
Definition: pg_dump.h:357
bool * notnull_islocal
Definition: pg_dump.h:378
uint32 frozenxid
Definition: pg_dump.h:325
char * typstorage
Definition: pg_dump.h:359
int owning_col
Definition: pg_dump.h:336
char * checkoption
Definition: pg_dump.h:316
int numatts
Definition: pg_dump.h:354
bool hastriggers
Definition: pg_dump.h:320
const char * rolname
Definition: pg_dump.h:309
struct _attrDefInfo ** attrdefs
Definition: pg_dump.h:379
char ** attoptions
Definition: pg_dump.h:366
char relreplident
Definition: pg_dump.h:313
int numTriggers
Definition: pg_dump.h:391
uint32 minmxid
Definition: pg_dump.h:326
Oid * attcollation
Definition: pg_dump.h:367
bool * notnull_noinh
Definition: pg_dump.h:377
char * attstorage
Definition: pg_dump.h:358
int toastpages
Definition: pg_dump.h:339
Oid owning_tab
Definition: pg_dump.h:335
struct _tableDataInfo * dataObj
Definition: pg_dump.h:390
char * amname
Definition: pg_dump.h:383
bool dummy_view
Definition: pg_dump.h:342
int32 relpages
Definition: pg_dump.h:338
bool forcerowsec
Definition: pg_dump.h:323
bool hascolumnACLs
Definition: pg_dump.h:321
char ** atttypnames
Definition: pg_dump.h:356
char ** attmissingval
Definition: pg_dump.h:370
char relpersistence
Definition: pg_dump.h:311
char ** attnames
Definition: pg_dump.h:355
char relkind
Definition: pg_dump.h:310
bool hasindex
Definition: pg_dump.h:318
bool unsafe_partitions
Definition: pg_dump.h:345
char * reloptions
Definition: pg_dump.h:315
int numIndexes
Definition: pg_dump.h:388
uint32 toast_frozenxid
Definition: pg_dump.h:328
uint32 toast_minmxid
Definition: pg_dump.h:329
char * attalign
Definition: pg_dump.h:364
char * attcompression
Definition: pg_dump.h:368
bool postponed_def
Definition: pg_dump.h:343
bool rowsec
Definition: pg_dump.h:322
Oid tmpllexize
Definition: pg_dump.h:590
Oid tmplinit
Definition: pg_dump.h:589
DumpableObject dobj
Definition: pg_dump.h:588
pgoff_t dataLength
struct _tocEntry * next
DumpId * dependencies
DumpableObject dobj
Definition: pg_dump.h:554
Oid trffromsql
Definition: pg_dump.h:557
TableInfo * tgtable
Definition: pg_dump.h:488
DumpableObject dobj
Definition: pg_dump.h:487
char tgenabled
Definition: pg_dump.h:489
char * tgdef
Definition: pg_dump.h:491
bool tgispartition
Definition: pg_dump.h:490
bool isMultirange
Definition: pg_dump.h:221
struct _constraintInfo * domChecks
Definition: pg_dump.h:229
DumpableAcl dacl
Definition: pg_dump.h:206
DumpableObject dobj
Definition: pg_dump.h:205
bool isDefined
Definition: pg_dump.h:222
char * ftypname
Definition: pg_dump.h:213
char typrelkind
Definition: pg_dump.h:218
Oid typarray
Definition: pg_dump.h:217
Oid typelem
Definition: pg_dump.h:215
struct _shellTypeInfo * shellType
Definition: pg_dump.h:224
int nDomChecks
Definition: pg_dump.h:228
struct _constraintInfo * notnull
Definition: pg_dump.h:226
char typtype
Definition: pg_dump.h:219
const char * rolname
Definition: pg_dump.h:214
Oid typrelid
Definition: pg_dump.h:216
bool isArray
Definition: pg_dump.h:220
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
#define MaxCommandIdAttributeNumber
Definition: sysattr.h:25
#define MaxTransactionIdAttributeNumber
Definition: sysattr.h:24
#define TableOidAttributeNumber
Definition: sysattr.h:26
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
#define MinCommandIdAttributeNumber
Definition: sysattr.h:23
static StringInfo copybuf
Definition: tablesync.c:127
static ItemArray items
Definition: test_tidstore.c:48
static void * fn(void *arg)
Definition: thread-alloc.c:119
#define FirstNormalObjectId
Definition: transam.h:197
@ TRI_YES
Definition: vacuumlo.c:38
@ TRI_NO
Definition: vacuumlo.c:37
bool SplitGUCList(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3003
const char * description
const char * type
const char * name
ArchiveMode
Definition: xlog.h:64